mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5885 - Updated the Next Instruction action to jump to the the function entry when in the function header
This commit is contained in:
parent
dc09c94c81
commit
bdf3c1d2f6
7 changed files with 126 additions and 23 deletions
|
@ -649,6 +649,20 @@
|
||||||
<P>To move the cursor to the next instruction click on the Navigate by Instruction icon,
|
<P>To move the cursor to the next instruction click on the Navigate by Instruction icon,
|
||||||
<IMG src="images/I.gif" alt="I" border="0">. This icon is disabled when no more
|
<IMG src="images/I.gif" alt="I" border="0">. This icon is disabled when no more
|
||||||
instructions exist in the current search direction.</P>
|
instructions exist in the current search direction.</P>
|
||||||
|
<P>
|
||||||
|
If already on an instruction, <B>on the address field</B>, then this action will find the
|
||||||
|
next data or undefined data before finding the next instruction. This allows users to jump
|
||||||
|
between ranges of instructions.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P><IMG src="help/shared/note.yellow.png" alt="Note" border="0">When performing this
|
||||||
|
action while not on the address field, then the action will first go to the address
|
||||||
|
field for the current address if an instruction exists. Subsequent invocations will
|
||||||
|
then work as described above. This is convenient for jumping from a function
|
||||||
|
signature to the function entry point.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<P>When inverted, this task, if on an instruction, will attempt to navigate to the next
|
<P>When inverted, this task, if on an instruction, will attempt to navigate to the next
|
||||||
data or undefined data. If not on an instruction, then this task will find the next
|
data or undefined data. If not on an instruction, then this task will find the next
|
||||||
|
|
|
@ -78,7 +78,7 @@ public abstract class AbstractNextPreviousAction extends NavigatableContextActio
|
||||||
protected abstract KeyStroke getKeyStroke();
|
protected abstract KeyStroke getKeyStroke();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final NavigatableActionContext context) {
|
public void actionPerformed(NavigatableActionContext context) {
|
||||||
Task t = new Task("Searching for " + doGetNavigationTypeName(), true, false, true) {
|
Task t = new Task("Searching for " + doGetNavigationTypeName(), true, false, true) {
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
|
@ -88,7 +88,7 @@ public abstract class AbstractNextPreviousAction extends NavigatableContextActio
|
||||||
new TaskLauncher(t);
|
new TaskLauncher(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gotoNextPrevious(TaskMonitor monitor, final NavigatableActionContext context) {
|
private void gotoNextPrevious(TaskMonitor monitor, NavigatableActionContext context) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean direction = isForward;
|
boolean direction = isForward;
|
||||||
|
@ -97,8 +97,8 @@ public abstract class AbstractNextPreviousAction extends NavigatableContextActio
|
||||||
}
|
}
|
||||||
|
|
||||||
Address address = direction
|
Address address = direction
|
||||||
? getNextAddress(monitor, context.getProgram(), context.getAddress())
|
? getNextAddress(monitor, context)
|
||||||
: getPreviousAddress(monitor, context.getProgram(), context.getAddress());
|
: getPreviousAddress(monitor, context);
|
||||||
|
|
||||||
Swing.runLater(() -> gotoAddress(context, address));
|
Swing.runLater(() -> gotoAddress(context, address));
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,32 @@ public abstract class AbstractNextPreviousAction extends NavigatableContextActio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Address getNextAddress(TaskMonitor monitor, NavigatableActionContext context)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
// default for clients that do not need the context
|
||||||
|
Program program = context.getProgram();
|
||||||
|
Address address = context.getAddress();
|
||||||
|
return getNextAddress(monitor, program, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Address getPreviousAddress(TaskMonitor monitor, NavigatableActionContext context)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
// default for clients that do not need the context
|
||||||
|
Program program = context.getProgram();
|
||||||
|
Address address = context.getAddress();
|
||||||
|
return getPreviousAddress(monitor, program, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected Address getNextAddress(TaskMonitor monitor, Program program, Address address)
|
||||||
|
throws CancelledException;
|
||||||
|
|
||||||
|
abstract protected Address getPreviousAddress(TaskMonitor monitor, Program program,
|
||||||
|
Address address) throws CancelledException;
|
||||||
|
|
||||||
|
abstract protected String getNavigationTypeName();
|
||||||
|
|
||||||
private void gotoAddress(NavigatableActionContext actionContext, Address address) {
|
private void gotoAddress(NavigatableActionContext actionContext, Address address) {
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
tool.setStatusInfo("Unable to locate another \"" + doGetNavigationTypeName() +
|
tool.setStatusInfo("Unable to locate another \"" + doGetNavigationTypeName() +
|
||||||
|
@ -120,7 +146,6 @@ public abstract class AbstractNextPreviousAction extends NavigatableContextActio
|
||||||
Navigatable navigatable = actionContext.getNavigatable();
|
Navigatable navigatable = actionContext.getNavigatable();
|
||||||
gotoAddress(service, navigatable, address);
|
gotoAddress(service, navigatable, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void gotoAddress(GoToService service, Navigatable navigatable, Address address) {
|
protected void gotoAddress(GoToService service, Navigatable navigatable, Address address) {
|
||||||
|
@ -145,16 +170,8 @@ public abstract class AbstractNextPreviousAction extends NavigatableContextActio
|
||||||
return getNavigationTypeName();
|
return getNavigationTypeName();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected String getNavigationTypeName();
|
|
||||||
|
|
||||||
protected String getInvertedNavigationTypeName() {
|
protected String getInvertedNavigationTypeName() {
|
||||||
return "Non-" + getNavigationTypeName();
|
return "Non-" + getNavigationTypeName();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected Address getNextAddress(TaskMonitor monitor, Program program, Address address)
|
|
||||||
throws CancelledException;
|
|
||||||
|
|
||||||
abstract protected Address getPreviousAddress(TaskMonitor monitor, Program program,
|
|
||||||
Address address) throws CancelledException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,17 @@ package ghidra.app.plugin.core.navigation;
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import javax.help.UnsupportedOperationException;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.util.AddressFieldLocation;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -53,13 +57,20 @@ public class NextPreviousInstructionAction extends AbstractNextPreviousAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Address getNextAddress(TaskMonitor monitor, Program program, Address address)
|
protected Address getNextAddress(TaskMonitor monitor, NavigatableActionContext context)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
|
Program program = context.getProgram();
|
||||||
|
Address address = context.getAddress();
|
||||||
if (isInverted) {
|
if (isInverted) {
|
||||||
return getNextNonInstructionAddress(monitor, program, address);
|
return getNextNonInstructionAddress(monitor, program, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for known special cases
|
||||||
|
if (useCurrentInstruction(context)) {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
if (isInstructionAt(program, address)) {
|
if (isInstructionAt(program, address)) {
|
||||||
// on an instruction, find a non-instruction before finding the next instruction
|
// on an instruction, find a non-instruction before finding the next instruction
|
||||||
address = getAddressOfNextPreviousNonInstruction(monitor, program, address, true);
|
address = getAddressOfNextPreviousNonInstruction(monitor, program, address, true);
|
||||||
|
@ -69,10 +80,27 @@ public class NextPreviousInstructionAction extends AbstractNextPreviousAction {
|
||||||
return getAddressOfNextInstructionAfter(program, address);
|
return getAddressOfNextInstructionAfter(program, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean useCurrentInstruction(NavigatableActionContext context) {
|
||||||
|
// Jumping to the next instruction below the current instruction is not useful. When on an
|
||||||
|
// instruction, find the next non-instruction and then look for the next instruction after
|
||||||
|
// that. We do not want to do this when there is an instruction at an address, but it is
|
||||||
|
// not close to the cursor, such as when on a function signature. In that case, jump to the
|
||||||
|
// instruction at that same address. In the case when the cursor is on the function
|
||||||
|
// signature, this will allow the user to quickly jump to the entry address field.
|
||||||
|
//
|
||||||
|
// Each time this action is executed, it places the cursor on the address field. Use that
|
||||||
|
// as a signal that we should go to the next instruction. This allows the example outlined
|
||||||
|
// above to work, with only minor intrusion to the user's workflow.
|
||||||
|
ProgramLocation location = context.getLocation();
|
||||||
|
return !(location instanceof AddressFieldLocation);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Address getPreviousAddress(TaskMonitor monitor, Program program, Address address)
|
protected Address getPreviousAddress(TaskMonitor monitor, NavigatableActionContext context)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
|
Program program = context.getProgram();
|
||||||
|
Address address = context.getAddress();
|
||||||
if (isInverted) {
|
if (isInverted) {
|
||||||
return getPreviousNonInstructionAddress(monitor, program, address);
|
return getPreviousNonInstructionAddress(monitor, program, address);
|
||||||
}
|
}
|
||||||
|
@ -86,6 +114,20 @@ public class NextPreviousInstructionAction extends AbstractNextPreviousAction {
|
||||||
return getAddressOfPreviousInstructionBefore(program, address);
|
return getAddressOfPreviousInstructionBefore(program, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Address getNextAddress(TaskMonitor monitor, Program program, Address address)
|
||||||
|
throws CancelledException {
|
||||||
|
// use getNextAddress(NavigatableActionContext, TaskMonitor) instead
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Address getPreviousAddress(TaskMonitor monitor, Program program, Address address)
|
||||||
|
throws CancelledException {
|
||||||
|
// use getPreviousAddress(NavigatableActionContext, TaskMonitor) instead
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
private Address getNextNonInstructionAddress(TaskMonitor monitor, Program program,
|
private Address getNextNonInstructionAddress(TaskMonitor monitor, Program program,
|
||||||
Address address) throws CancelledException {
|
Address address) throws CancelledException {
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||||
import ghidra.app.plugin.core.bookmark.BookmarkEditCmd;
|
import ghidra.app.plugin.core.bookmark.BookmarkEditCmd;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.services.GoToService;
|
import ghidra.app.services.GoToService;
|
||||||
|
import ghidra.app.util.viewer.field.FunctionSignatureFieldFactory;
|
||||||
import ghidra.framework.cmd.CompoundCmd;
|
import ghidra.framework.cmd.CompoundCmd;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
@ -38,6 +39,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.test.*;
|
import ghidra.test.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
|
@ -173,6 +175,35 @@ public class NextPrevCodeUnitPluginTest extends AbstractGhidraHeadedIntegrationT
|
||||||
assertAddress("0x100294d");
|
assertAddress("0x100294d");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchInstruction_WhenOnFunctionSignature() throws Exception {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test that the Next Instruction action will go to the Address Field when inside of a
|
||||||
|
// function signature.
|
||||||
|
//
|
||||||
|
|
||||||
|
Symbol symbol = getUniqueSymbol(program, "FUN_01002239");
|
||||||
|
Address functionEntry = symbol.getAddress();
|
||||||
|
|
||||||
|
assertTrue(cb.goToField(functionEntry, FunctionSignatureFieldFactory.FIELD_NAME, 0, 0));
|
||||||
|
|
||||||
|
nextInstruction();
|
||||||
|
|
||||||
|
// address is the same, but the field has changed from the signature to the bytes field
|
||||||
|
assertAddress(functionEntry);
|
||||||
|
|
||||||
|
nextInstruction();
|
||||||
|
assertAddress("0x1002cf5");
|
||||||
|
|
||||||
|
toggleDirection();
|
||||||
|
|
||||||
|
// going backwards the address before the function will be chosen
|
||||||
|
nextInstruction();
|
||||||
|
assertAddress("0x100294d");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchNotInstruction() throws Exception {
|
public void testSearchNotInstruction() throws Exception {
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ public class NextPrevFunctionTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
return addrFactory.getAddress(address);
|
return addrFactory.getAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // we know that bookmarks is of type String
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
env = new TestEnv();
|
env = new TestEnv();
|
||||||
|
|
|
@ -388,7 +388,7 @@ public class FGActionManager {
|
||||||
fullViewAction.setHelpLocation(
|
fullViewAction.setHelpLocation(
|
||||||
new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Full_View"));
|
new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Full_View"));
|
||||||
|
|
||||||
DockingAction xrefsAction = new DockingAction("Jump to a XRef", owner) {
|
DockingAction xrefsAction = new DockingAction("Jump to XRef", owner) {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
controller.showXRefsDialog();
|
controller.showXRefsDialog();
|
||||||
|
@ -410,7 +410,7 @@ public class FGActionManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
menuData = new MenuData(new String[] { "Jump to a XRef" }, popupMutateGroup1);
|
menuData = new MenuData(new String[] { "Jump to XRef" }, popupMutateGroup1);
|
||||||
menuData.setIcon(XREFS_ICON);
|
menuData.setIcon(XREFS_ICON);
|
||||||
menuData.setMenuSubGroup(Integer.toString(vertexGroupingSubgroupOffset++));
|
menuData.setMenuSubGroup(Integer.toString(vertexGroupingSubgroupOffset++));
|
||||||
xrefsAction.setPopupMenuData(menuData);
|
xrefsAction.setPopupMenuData(menuData);
|
||||||
|
|
|
@ -331,7 +331,7 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
|
||||||
controller.showXRefsDialog();
|
controller.showXRefsDialog();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xrefsAction.setDescription("Jump to a XRef");
|
xrefsAction.setDescription("Jump to XRef");
|
||||||
Icon imageIcon = new GIcon("icon.plugin.functiongraph.action.vertex.xrefs");
|
Icon imageIcon = new GIcon("icon.plugin.functiongraph.action.vertex.xrefs");
|
||||||
xrefsAction.setToolBarData(new ToolBarData(imageIcon, firstGroup));
|
xrefsAction.setToolBarData(new ToolBarData(imageIcon, firstGroup));
|
||||||
xrefsAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Vertex_Action_XRefs"));
|
xrefsAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Vertex_Action_XRefs"));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue