mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-10-22 18:22:23 -09:00
Merge remote-tracking branch
'origin/GP-2141_ghidragon_initial_address_on_program_open' (Closes #4267)
This commit is contained in:
@@ -798,7 +798,6 @@ src/main/resources/images/field.header.up.png||GHIDRA||||END|
|
||||
src/main/resources/images/fingerPointer.png||GHIDRA||||END|
|
||||
src/main/resources/images/flag-green.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/flag-yellow.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/font.png||FAMFAMFAM Icons - CC 2.5||||END|
|
||||
src/main/resources/images/format-text-bold.png||Tango Icons - Public Domain|||tango icon set|END|
|
||||
src/main/resources/images/functionDef.png||GHIDRA||||END|
|
||||
src/main/resources/images/function_graph.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
<LI> Use the <A href="#Navigation_History">Navigation History</A> to return to a
|
||||
previously visited location</LI>
|
||||
<LI> Specify a specific <A href="#Starting_Program_Location">Program Start
|
||||
Location</A> when
|
||||
opening a program</LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
@@ -734,10 +737,6 @@
|
||||
<H2><A NAME="Navigation_Options"></A>Navigation Options</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<P>
|
||||
<B>'Go To' in Current Program Only</B> -
|
||||
@@ -788,6 +787,32 @@
|
||||
range being navigated.
|
||||
</P>
|
||||
|
||||
<H3><A name="Starting_Program_Location"></A>Starting Program Location Options</H3>
|
||||
<P>The staring location for newly opened programs can be configured using the following
|
||||
options:</P>
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Start At</B> - Choose a starting program location option:
|
||||
<UL>
|
||||
<LI>Lowest Address - The program will open at the lowest address.</LI>
|
||||
<LI>Lowest Code Block Address - The program will open at the firest executable
|
||||
memory block. If no executable block found, it will go to lowest address.</LI>
|
||||
<LI>Preferred Symbol Name - The program will open at the first symbol name it
|
||||
finds that matches a name in the "Start Symbols" option. If no symbol is found,
|
||||
it will try the lowest code block, and finally the lowest address.</LI>
|
||||
</UL>
|
||||
|
||||
<P><B>Start Symbols</B> - A comma separated list of symbol names to be be used as the
|
||||
starting location for the program if the "Preferred Symbol Name" option is selected
|
||||
above. The first matching symbol found will be used as the starting location for
|
||||
newly opened programs.</P>
|
||||
|
||||
<P><B>Use Underscores</B> - If selected, each of the preferred symbols listed above
|
||||
will also be searched for with either one or two underscores prepended to the name. For
|
||||
example, if selected, it will search for "_main" and "__main" in addition to "main"
|
||||
when trying to find a starting symbol.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
@@ -69,8 +69,7 @@ public class NextPrevAddressPlugin extends Plugin {
|
||||
|
||||
private static final String PREVIOUS_ACTION_NAME = "Previous Location in History";
|
||||
private static final String NEXT_ACTION_NAME = "Next Location in History";
|
||||
private static final String PREVIOUS_FUNCTION_ACTION_NAME =
|
||||
"Previous Function in History";
|
||||
private static final String PREVIOUS_FUNCTION_ACTION_NAME = "Previous Function in History";
|
||||
private static final String NEXT_FUNCTION_ACTION_NAME = "Next Function in History";
|
||||
private static final String[] CLEAR_MENUPATH = { "Navigation", "Clear History" };
|
||||
|
||||
@@ -114,6 +113,10 @@ public class NextPrevAddressPlugin extends Plugin {
|
||||
return nextFunctionAction;
|
||||
}
|
||||
|
||||
DockingAction getClearHistoryAction() {
|
||||
return clearAction;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Class for managing the options associated with the {@link ProgramStartingLocationPlugin}
|
||||
*/
|
||||
public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
|
||||
static final String NAVIGATION_TOPIC = "Navigation";
|
||||
public static final String SUB_OPTION = "Starting Program Location";
|
||||
public static final String START_LOCATION_TYPE_OPTION = SUB_OPTION + ".Start At: ";
|
||||
public static final String START_SYMBOLS_OPTION = SUB_OPTION + ".Start Symbols: ";
|
||||
public static final String UNDERSCORE_OPTION = SUB_OPTION + ".Use Underscores:";
|
||||
|
||||
private static final String START_LOCATION_DESCRIPTION =
|
||||
"Determines the start location for newly opened programs.\n" +
|
||||
"Either lowest address, lowest code address, or symbol name.\nEach higher " +
|
||||
"option will revert to the next lower option if that option can't be satisfied.";
|
||||
private static final String STARTING_SYSMBOLS_DESCRIPTION =
|
||||
"A comma separated list of symbol names in preference order. " +
|
||||
"(Used when option above is set to \"Preferred Symbol Name\")";
|
||||
private static final String SYMBOL_PREFIX_DESCRIPTION =
|
||||
"When searching for symbols, also search for the names prepended with \"_\" and \"__\".";
|
||||
|
||||
private static final String DEFAULT_STARTING_SYMBOLS =
|
||||
"main, WinMain, libc_start_main, WinMainStartup, start, entry";
|
||||
|
||||
public static enum StartLocationType {
|
||||
LOWEST_ADDRESS("Lowest Address"),
|
||||
LOWEST_CODE_BLOCK("Lowest Code Block Address"),
|
||||
SYMBOL_NAME("Preferred Symbol Name");
|
||||
|
||||
private String label;
|
||||
|
||||
private StartLocationType(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private StartLocationType startLocationType;
|
||||
private List<String> startSymbols;
|
||||
private boolean useUnderscorePrefixes;
|
||||
|
||||
private ToolOptions options;
|
||||
|
||||
public ProgramStartingLocationOptions(PluginTool tool) {
|
||||
options = tool.getOptions(GhidraOptions.NAVIGATION_OPTIONS);
|
||||
HelpLocation help = new HelpLocation(NAVIGATION_TOPIC, "Starting_Program_Location");
|
||||
|
||||
// set a help location on the group
|
||||
Options subOptions = options.getOptions(SUB_OPTION);
|
||||
subOptions.setOptionsHelpLocation(help);
|
||||
|
||||
options.registerOption(START_LOCATION_TYPE_OPTION, StartLocationType.SYMBOL_NAME, help,
|
||||
START_LOCATION_DESCRIPTION);
|
||||
|
||||
options.registerOption(START_SYMBOLS_OPTION, DEFAULT_STARTING_SYMBOLS, help,
|
||||
STARTING_SYSMBOLS_DESCRIPTION);
|
||||
|
||||
options.registerOption(UNDERSCORE_OPTION, true, help, SYMBOL_PREFIX_DESCRIPTION);
|
||||
|
||||
startLocationType =
|
||||
options.getEnum(START_LOCATION_TYPE_OPTION, StartLocationType.SYMBOL_NAME);
|
||||
|
||||
String symbolNames = options.getString(START_SYMBOLS_OPTION, DEFAULT_STARTING_SYMBOLS);
|
||||
startSymbols = parse(symbolNames);
|
||||
|
||||
useUnderscorePrefixes = options.getBoolean(UNDERSCORE_OPTION, true);
|
||||
options.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
private List<String> parse(String symbolNames) {
|
||||
List<String> names = new ArrayList<>();
|
||||
String[] split = symbolNames.split(",");
|
||||
for (String string : split) {
|
||||
String trimmed = string.trim();
|
||||
if (!trimmed.isBlank()) {
|
||||
names.add(trimmed);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the program should start on one of the starting symbols.
|
||||
* @return true if the program should start on one of the starting symbols
|
||||
*/
|
||||
public boolean shouldStartOnSymbol() {
|
||||
return startLocationType == StartLocationType.SYMBOL_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the program should start at the lowest address.
|
||||
* @return true if the program should start at the lowest address
|
||||
*/
|
||||
public boolean shouldStartAtLowestAddress() {
|
||||
return startLocationType == StartLocationType.LOWEST_ADDRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the program should start at the first code block.
|
||||
* @return true if the program should start at the first code block.
|
||||
*/
|
||||
public boolean shouldStartAtLowestCodeBlock() {
|
||||
return startLocationType == StartLocationType.LOWEST_CODE_BLOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of possible starting symbol names. The symbols are returned in order
|
||||
* of preference.
|
||||
* @return a list of starting symbols.
|
||||
*/
|
||||
public List<String> getStartingSymbolNames() {
|
||||
return startSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the list of starting symbol names should also be search for with "_" and
|
||||
* "__" prepended.
|
||||
* @return true if the list of starting symbol names should also be search for with "_" and
|
||||
* "__" prepended.
|
||||
*/
|
||||
public boolean useUnderscorePrefixes() {
|
||||
return useUnderscorePrefixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the options listener
|
||||
*/
|
||||
public void dispose() {
|
||||
options.removeOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions toolOptions, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
if (START_LOCATION_TYPE_OPTION.equals(optionName)) {
|
||||
startLocationType = (StartLocationType) newValue;
|
||||
}
|
||||
else if (START_SYMBOLS_OPTION.equals(optionName)) {
|
||||
startSymbols = parse((String) newValue);
|
||||
}
|
||||
else if (UNDERSCORE_OPTION.equals(optionName)) {
|
||||
useUnderscorePrefixes = (Boolean) newValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.COMMON,
|
||||
shortDescription = "Determines the starting location when a program is opened.",
|
||||
description = "This plugin watches for new programs being opened and determines the best starting location for the listing view.",
|
||||
servicesRequired = { GoToService.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||
|
||||
private Program lastOpenedProgram;
|
||||
private ProgramStartingLocationOptions startOptions;
|
||||
|
||||
public ProgramStartingLocationPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
startOptions = new ProgramStartingLocationOptions(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void programOpened(Program program) {
|
||||
// if the open program event is a result of restoring the tool's data state, don't
|
||||
// interfere with the tool's restoration of the last location for that program
|
||||
if (tool.isRestoringDataState()) {
|
||||
return;
|
||||
}
|
||||
if (startOptions.shouldStartAtLowestAddress()) {
|
||||
// this is what happens by default, so no need to do anything
|
||||
return;
|
||||
}
|
||||
lastOpenedProgram = program;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void programActivated(Program program) {
|
||||
super.programActivated(program);
|
||||
if (program == lastOpenedProgram) {
|
||||
Swing.runLater(this::setStartingLocationForNewProgram);
|
||||
}
|
||||
lastOpenedProgram = null;
|
||||
}
|
||||
|
||||
private void setStartingLocationForNewProgram() {
|
||||
if (currentProgram == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GoToService gotoService = tool.getService(GoToService.class);
|
||||
|
||||
ProgramLocation location = getStartingProgramLocation(currentProgram);
|
||||
if (location != null) {
|
||||
gotoService.goTo(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ProgramLocation getStartingProgramLocation(Program program) {
|
||||
if (startOptions.shouldStartOnSymbol()) {
|
||||
List<String> symbolNames = startOptions.getStartingSymbolNames();
|
||||
boolean useUnderscores = startOptions.useUnderscorePrefixes();
|
||||
for (String symbolName : symbolNames) {
|
||||
Symbol symbol = findSymbol(program, symbolName, useUnderscores);
|
||||
if (symbol != null) {
|
||||
return symbol.getProgramLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the option is start on first code block, or we couldn't find a symbol, try and
|
||||
// find the first executable code block
|
||||
return findLowestCodeBlockLocation(program);
|
||||
}
|
||||
|
||||
private ProgramLocation findLowestCodeBlockLocation(Program program) {
|
||||
AddressSetView executeSet = program.getMemory().getExecuteSet();
|
||||
if (executeSet.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramLocation(program, executeSet.getMinAddress());
|
||||
}
|
||||
|
||||
private Symbol findSymbol(Program program, String symbolName, boolean useUnderscores) {
|
||||
Symbol symbol = findSymbol(program, symbolName);
|
||||
if (symbol != null) {
|
||||
return symbol;
|
||||
}
|
||||
if (!useUnderscores) {
|
||||
return null;
|
||||
}
|
||||
symbol = findSymbol(program, "_" + symbolName);
|
||||
if (symbol != null) {
|
||||
return symbol;
|
||||
}
|
||||
return findSymbol(program, "__" + symbolName);
|
||||
}
|
||||
|
||||
private Symbol findSymbol(Program program, String symbolName) {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
List<Symbol> symbols = symbolTable.getLabelOrFunctionSymbols(symbolName, null);
|
||||
if (symbols.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (symbols.size() > 1) {
|
||||
Collections.sort(symbols, (s1, s2) -> s1.getAddress().compareTo(s2.getAddress()));
|
||||
}
|
||||
return symbols.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
super.dispose();
|
||||
startOptions.dispose();
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
private MultiActionDockingAction nextAction;
|
||||
private DockingAction previousFunctionAction;
|
||||
private DockingAction nextFunctionAction;
|
||||
private DockingAction clearHistoryAction;
|
||||
private CodeBrowserPlugin cbPlugin;
|
||||
private CodeViewerProvider provider;
|
||||
|
||||
@@ -71,9 +72,12 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
nextAction = plugin.getNextAction();
|
||||
previousFunctionAction = plugin.getPreviousFunctionAction();
|
||||
nextFunctionAction = plugin.getNextFunctionAction();
|
||||
clearHistoryAction = plugin.getClearHistoryAction();
|
||||
|
||||
cbPlugin = env.getPlugin(CodeBrowserPlugin.class);
|
||||
provider = cbPlugin.getProvider();
|
||||
goTo(program.getMinAddress());
|
||||
clearHistory();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -90,15 +94,13 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
Symbol bulkNavigationSymbol = navigatedSymbols.remove(0);
|
||||
|
||||
// the previous list of actions should be the size of the list - 1
|
||||
List<DockingActionIf> actionList =
|
||||
previousAction.getActionList(getContext());
|
||||
List<DockingActionIf> actionList = previousAction.getActionList(getContext());
|
||||
|
||||
// verify the size...
|
||||
// (the navigated symbols plus the original location before we started)
|
||||
assertEquals(actionList.size(), navigatedSymbols.size() + 1);
|
||||
assertEquals(actionList.size(), navigatedSymbols.size());
|
||||
|
||||
// ...verify the order
|
||||
int n = actionList.size() - 1; // don't compare the first location
|
||||
int n = actionList.size(); // don't compare the first location
|
||||
for (int i = 0; i < n; i++) {
|
||||
DockingActionIf dockableAction = actionList.get(i);
|
||||
LocationMemento location =
|
||||
@@ -122,7 +124,7 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
|
||||
// ...make sure the 'previous' list is updated with only actions after the one in our list
|
||||
actionList = previousAction.getActionList(getContext());
|
||||
n = actionList.size() - 1; // don't compare the first location
|
||||
n = actionList.size();
|
||||
int navigatedIndexOffset = (navigatedIndex + 1); // don't count the navigated index
|
||||
int remainingSymbols = navigatedSymbols.size() - navigatedIndexOffset;
|
||||
assertEquals(n, remainingSymbols);
|
||||
@@ -169,7 +171,7 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
|
||||
// ...make sure that the 'previous' list is properly updated
|
||||
actionList = previousAction.getActionList(getContext());
|
||||
n = actionList.size() - 1; // don't compare the first location
|
||||
n = actionList.size();
|
||||
navigatedIndexOffset = (navigatedIndex + 1); // don't count the navigated index
|
||||
remainingSymbols = navigatedSymbols.size() - navigatedIndexOffset;
|
||||
assertEquals(n, remainingSymbols);
|
||||
@@ -358,6 +360,14 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void clearHistory() {
|
||||
assertTrue(clearHistoryAction.isEnabledForContext(getContext()));
|
||||
performAction(clearHistoryAction, true);
|
||||
cbPlugin.updateNow();
|
||||
waitForSwing();
|
||||
|
||||
}
|
||||
|
||||
private void previous() {
|
||||
assertTrue(previousAction.isEnabledForContext(getContext()));
|
||||
performAction(previousAction, true);
|
||||
@@ -437,9 +447,8 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
Object actionToGuiMapper = TestUtils.getInstanceField("actionToGuiMapper", windowManager);
|
||||
Object menuAndToolBarManager =
|
||||
TestUtils.getInstanceField("menuAndToolBarManager", actionToGuiMapper);
|
||||
Map<WindowNode, WindowActionManager> map =
|
||||
(Map<WindowNode, WindowActionManager>) TestUtils.getInstanceField(
|
||||
"windowToActionManagerMap", menuAndToolBarManager);
|
||||
Map<WindowNode, WindowActionManager> map = (Map<WindowNode, WindowActionManager>) TestUtils
|
||||
.getInstanceField("windowToActionManagerMap", menuAndToolBarManager);
|
||||
Iterator<WindowActionManager> iterator = map.values().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
@@ -448,8 +457,8 @@ public class NextPrevAddressPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
(ToolBarManager) TestUtils.getInstanceField("toolBarMgr", wam);
|
||||
|
||||
Map<String, List<ToolBarItemManager>> groupToItemsMap =
|
||||
(Map<String, List<ToolBarItemManager>>) TestUtils.getInstanceField(
|
||||
"groupToItemsMap", toolBarManager);
|
||||
(Map<String, List<ToolBarItemManager>>) TestUtils
|
||||
.getInstanceField("groupToItemsMap", toolBarManager);
|
||||
|
||||
ToolBarData toolBarData = action.getToolBarData();
|
||||
String group = toolBarData.getToolBarGroup();
|
||||
|
||||
@@ -30,6 +30,7 @@ import ghidra.app.cmd.data.CreateDataCmd;
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.plugin.core.bookmark.BookmarkEditCmd;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
@@ -85,6 +86,7 @@ public class NextPrevCodeUnitPluginTest extends AbstractGhidraHeadedIntegrationT
|
||||
nextBookmark = (MultiStateDockingAction<String>) getAction(p, "Next Bookmark");
|
||||
|
||||
cb = env.getPlugin(CodeBrowserPlugin.class);
|
||||
goTo(program.getMinAddress());
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -112,8 +114,7 @@ public class NextPrevCodeUnitPluginTest extends AbstractGhidraHeadedIntegrationT
|
||||
addType("0100102a", "15 00", new DWordDataType());
|
||||
}
|
||||
|
||||
private void addType(String addrString, String bytes, DataType dt)
|
||||
throws Exception {
|
||||
private void addType(String addrString, String bytes, DataType dt) throws Exception {
|
||||
builder.setBytes(addrString, bytes);
|
||||
builder.applyDataType(addrString, dt);
|
||||
}
|
||||
@@ -938,4 +939,10 @@ public class NextPrevCodeUnitPluginTest extends AbstractGhidraHeadedIntegrationT
|
||||
});
|
||||
}
|
||||
|
||||
private void goTo(Address a) throws Exception {
|
||||
GoToService goToService = tool.getService(GoToService.class);
|
||||
goToService.goTo(a);
|
||||
cb.updateNow();
|
||||
waitForSwing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
|
||||
public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private TestEnv env;
|
||||
private PluginTool tool;
|
||||
private AddressFactory addrFactory;
|
||||
private CodeBrowserPlugin cb;
|
||||
private Options options;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv();
|
||||
tool = env.launchDefaultTool();
|
||||
cb = env.getPlugin(CodeBrowserPlugin.class);
|
||||
options = tool.getOptions(GhidraOptions.NAVIGATION_OPTIONS);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
env.closeTool(tool);
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolByDefault() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
builder.createLabel("0x105", "main");
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpensToLowestCodeBlock() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
MemoryBlock block = builder.createMemory(".text", "0x200", 0x200);
|
||||
builder.setExecute(block, true);
|
||||
builder.createLabel("0x105", "main");
|
||||
|
||||
setOptionToLowestCodeBlock();
|
||||
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x200"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolNotFirstInSymbolList() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
setSymbolListOption("main, foobar, start");
|
||||
builder.createLabel("0x107", "start");
|
||||
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x107"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpensToFirstSymbolWhenMutlipesAreFoud() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
setSymbolListOption("main, start");
|
||||
builder.createLabel("0x110", "start");
|
||||
builder.createLabel("0x105", "start");
|
||||
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolWithOneUndercore() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
setSymbolListOption("main, start");
|
||||
builder.createLabel("0x105", "_main");
|
||||
builder.createLabel("0x107", "start");
|
||||
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolWithTwoUndercores() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
setSymbolListOption("main, start");
|
||||
builder.createLabel("0x105", "__main");
|
||||
builder.createLabel("0x107", "_start");
|
||||
builder.createLabel("0x109", "start");
|
||||
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoUnderscoresSearching() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
setSymbolListOption("main, start");
|
||||
setUnderscoreOption(false);
|
||||
builder.createLabel("0x105", "__main");
|
||||
builder.createLabel("0x107", "_start");
|
||||
builder.createLabel("0x109", "start");
|
||||
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x109"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionToStartAtLowestAddress() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
builder.createLabel("0x105", "main");
|
||||
setOptionToLowestAddress();
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
private void setUnderscoreOption(boolean b) {
|
||||
options.setBoolean(ProgramStartingLocationOptions.UNDERSCORE_OPTION, b);
|
||||
}
|
||||
|
||||
private void setOptionToLowestAddress() {
|
||||
options.setEnum(ProgramStartingLocationOptions.START_LOCATION_TYPE_OPTION,
|
||||
ProgramStartingLocationOptions.StartLocationType.LOWEST_ADDRESS);
|
||||
}
|
||||
|
||||
private void setOptionToLowestCodeBlock() {
|
||||
options.setEnum(ProgramStartingLocationOptions.START_LOCATION_TYPE_OPTION,
|
||||
ProgramStartingLocationOptions.StartLocationType.LOWEST_CODE_BLOCK);
|
||||
}
|
||||
|
||||
private void setSymbolListOption(String symbolListString) {
|
||||
options.setString(ProgramStartingLocationOptions.START_SYMBOLS_OPTION, symbolListString);
|
||||
}
|
||||
|
||||
private ProgramBuilder getProgramBuilder(String baseAddress) throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder();
|
||||
builder.createMemory(".data", baseAddress, 0x100);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void loadProgram(Program program) throws Exception {
|
||||
env.open(program);
|
||||
addrFactory = program.getAddressFactory();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private Address addr(String address) {
|
||||
return addrFactory.getAddress(address);
|
||||
}
|
||||
}
|
||||
@@ -351,8 +351,8 @@ class PluginManager {
|
||||
}
|
||||
|
||||
Map<String, Exception> badMap = new LinkedHashMap<>();
|
||||
List<Plugin> list = getPluginsByServiceOrder(0);
|
||||
for (Plugin p : list) {
|
||||
List<Plugin> plugins = getPluginsByServiceOrder(0);
|
||||
for (Plugin p : plugins) {
|
||||
SaveState saveState = map.get(p.getName());
|
||||
if (saveState != null) {
|
||||
try {
|
||||
@@ -376,9 +376,7 @@ class PluginManager {
|
||||
Msg.showError(this, null, "Data State Error",
|
||||
"Errors in plugin data states - check console for details");
|
||||
}
|
||||
for (Plugin plugin : list) {
|
||||
plugin.dataStateRestoreCompleted();
|
||||
}
|
||||
plugins.forEach(Plugin::dataStateRestoreCompleted);
|
||||
}
|
||||
|
||||
Element saveDataStateToXml(boolean savingProject) {
|
||||
|
||||
@@ -121,6 +121,7 @@ public abstract class PluginTool extends AbstractDockingTool {
|
||||
|
||||
private boolean isConfigurable = true;
|
||||
protected boolean isDisposed = false;
|
||||
private boolean restoringDataState;
|
||||
|
||||
/**
|
||||
* Construct a new PluginTool.
|
||||
@@ -574,8 +575,14 @@ public abstract class PluginTool extends AbstractDockingTool {
|
||||
}
|
||||
|
||||
public void restoreDataStateFromXml(Element root) {
|
||||
pluginMgr.restoreDataStateFromXml(root);
|
||||
setConfigChanged(false);
|
||||
restoringDataState = true;
|
||||
try {
|
||||
pluginMgr.restoreDataStateFromXml(root);
|
||||
setConfigChanged(false);
|
||||
}
|
||||
finally {
|
||||
restoringDataState = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Element saveDataStateToXml(boolean savingProject) {
|
||||
@@ -1502,6 +1509,10 @@ public abstract class PluginTool extends AbstractDockingTool {
|
||||
super.contextChanged(provider);
|
||||
}
|
||||
|
||||
public boolean isRestoringDataState() {
|
||||
return restoringDataState;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
@@ -151,8 +151,13 @@ public interface SymbolTable {
|
||||
* @param namespace the namespace of the symbol to retrieve. May be null which indicates the
|
||||
* global namespace.
|
||||
* @return the symbol which matches the specified criteria or null if not found
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
* @see #getGlobalSymbol(String, Address) for a convenience method if the namespace is the
|
||||
* global namespace.
|
||||
*/
|
||||
@@ -204,16 +209,28 @@ public interface SymbolTable {
|
||||
* default source type) since it mirrors the name and parent namespace of the function it
|
||||
* thunks.
|
||||
*
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* @param name the name of the symbols to search for
|
||||
* @param namespace the namespace to search. If null, then the global namespace is assumed.
|
||||
* @return a list of all the label or function symbols with the given name in the given parent
|
||||
* namespace
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* <p>NOTE: This method will not return a default thunk (i.e., thunk function symbol with default source type)
|
||||
* since it mirrors the name and parent namespace of the function it thunks.</p>
|
||||
*
|
||||
* @param name the name of the symbols to search for.
|
||||
* @param namespace the namespace to search. If null, then the global namespace is assumed.
|
||||
* @return a list of all the label or function symbols with the given name in the given namespace.
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public List<Symbol> getLabelOrFunctionSymbols(String name, Namespace namespace);
|
||||
|
||||
/**
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* Get a generic namespace symbol with the given name in the given parent namespace
|
||||
*
|
||||
* @param name the name of the namespace symbol to retrieve
|
||||
@@ -221,6 +238,14 @@ public interface SymbolTable {
|
||||
* @return the symbol, or null
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* Returns a generic namespace symbol with the given name in the given namespace.
|
||||
* @param name the name of the namespace symbol to retrieve.
|
||||
* @param namespace the namespace containing the symbol to retrieve.
|
||||
* @return a generic namespace symbol with the given name in the given namespace.
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public Symbol getNamespaceSymbol(String name, Namespace namespace);
|
||||
|
||||
@@ -233,6 +258,7 @@ public interface SymbolTable {
|
||||
public Symbol getLibrarySymbol(String name);
|
||||
|
||||
/**
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* Get the class symbol with the given name in the given namespace
|
||||
*
|
||||
* @param name the name of the class
|
||||
@@ -240,10 +266,19 @@ public interface SymbolTable {
|
||||
* @return the class symbol with the given name in the given namespace
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* Returns the class symbol with the given name in the given namespace.
|
||||
* @param name the name of the class.
|
||||
* @param namespace the namespace to search for the class.
|
||||
* @return the class symbol with the given name in the given namespace.
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public Symbol getClassSymbol(String name, Namespace namespace);
|
||||
|
||||
/**
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* Get the parameter symbol with the given name in the given namespace
|
||||
*
|
||||
* @param name the name of the parameter
|
||||
@@ -251,10 +286,19 @@ public interface SymbolTable {
|
||||
* @return the parameter symbol with the given name in the given namespace
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* Returns the parameter symbol with the given name in the given namespace.
|
||||
* @param name the name of the parameter.
|
||||
* @param namespace the namespace (function) to search for the class.
|
||||
* @return the parameter symbol with the given name in the given namespace.
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public Symbol getParameterSymbol(String name, Namespace namespace);
|
||||
|
||||
/**
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* Get the local variable symbol with the given name in the given namespace
|
||||
*
|
||||
* @param name the name of the local variable
|
||||
@@ -262,6 +306,14 @@ public interface SymbolTable {
|
||||
* @return the local variable symbol with the given name in the given namespace
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* Returns the local variable symbol with the given name in the given namespace.
|
||||
* @param name the name of the local variable.
|
||||
* @param namespace the namespace (function) to search for the class.
|
||||
* @return the local variable symbol with the given name in the given namespace.
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public Symbol getLocalVariableSymbol(String name, Namespace namespace);
|
||||
|
||||
@@ -276,11 +328,22 @@ public interface SymbolTable {
|
||||
* <b>NOTE:</b> The resulting iterator will not return default thunks (i.e., thunk function
|
||||
* symbol with default source type).
|
||||
*
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* @param name the name of the symbols to retrieve
|
||||
* @param namespace the namespace to search for symbols
|
||||
* @return a list of symbols which satisfy specified criteria
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* <p>NOTE: The resulting iterator will not return default thunks (i.e.,
|
||||
* thunk function symbol with default source type).</p>
|
||||
*
|
||||
* @param name the name of the symbols to retrieve.
|
||||
* @param namespace the namespace to search for symbols.
|
||||
* @return all symbols which satisfy specified criteria
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public List<Symbol> getSymbols(String name, Namespace namespace);
|
||||
|
||||
@@ -296,6 +359,7 @@ public interface SymbolTable {
|
||||
public Symbol getVariableSymbol(String name, Function function);
|
||||
|
||||
/**
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* Get the namespace with the given name in the given parent namespace.
|
||||
* <p>
|
||||
* The returned namespace can be a generic namespace ({@link SymbolType#NAMESPACE},
|
||||
@@ -310,6 +374,16 @@ public interface SymbolTable {
|
||||
* @return the namespace with the given name in the given parent namespace
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* Returns the namespace with the given name in the given parent namespace. The namespace
|
||||
* returned can be either a generic namespace or a class or library. It does not include
|
||||
* functions.
|
||||
* @param name the name of the namespace to be retrieved.
|
||||
* @param namespace the parent namespace of the namespace to be retrieved.
|
||||
* @return the namespace with the given name in the given parent namespace.
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public Namespace getNamespace(String name, Namespace namespace);
|
||||
|
||||
@@ -405,10 +479,20 @@ public interface SymbolTable {
|
||||
* <b>NOTE:</b> The resulting iterator will not return default thunks (i.e., thunk function
|
||||
* symbol with default source type).
|
||||
*
|
||||
<<<<<<< Upstream, based on origin/master
|
||||
* @param namespace the namespace to search for symbols
|
||||
* @return an iterator over the symbols
|
||||
* @throws IllegalArgumentException if the given parent namespace is from a different program
|
||||
* than that of this symbol table
|
||||
=======
|
||||
* <p>NOTE: The resulting iterator will not return default thunks (i.e.,
|
||||
* thunk function symbol with default source type).</p>
|
||||
*
|
||||
* @param namespace the namespace to search for symbols.
|
||||
* @return symbol iterator
|
||||
* @throws IllegalArgumentException namespace which does not correspond to this
|
||||
* symbol table's program.
|
||||
>>>>>>> ba3dc0f GP-2141 adding feature for better starting location when opening a program.
|
||||
*/
|
||||
public SymbolIterator getSymbols(Namespace namespace);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user