Merge remote-tracking branch

'origin/GP-2141_ghidragon_initial_address_on_program_open'
(Closes #4267)
This commit is contained in:
Ryan Kurtz
2023-01-31 11:48:55 -05:00
11 changed files with 671 additions and 28 deletions

View File

@@ -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|

View File

@@ -25,6 +25,9 @@
<LI>&nbsp;&nbsp; Use the <A href="#Navigation_History">Navigation History</A> to return to a
previously visited location</LI>
<LI>&nbsp;&nbsp; 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>

View File

@@ -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
//==================================================================================================

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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
//==================================================================================================

View File

@@ -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);