mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 14:54:29 +08:00
Merge remote-tracking branch 'origin/GP-3064_ghidragon_goto_after_first_analysis--SQUASHED'
This commit is contained in:
@@ -805,7 +805,7 @@
|
|||||||
lowest code block, and finally lowest overall address</LI>
|
lowest code block, and finally lowest overall address</LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<P><B>Start Symbols</B> - A comma separated list of symbol names to be be used as the
|
<P><B><A name="Start_Symbols"></A>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
|
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
|
above. The first matching symbol found will be used as the starting location for
|
||||||
newly opened programs.</P>
|
newly opened programs.</P>
|
||||||
@@ -816,6 +816,24 @@
|
|||||||
when trying to find a starting symbol.</P>
|
when trying to find a starting symbol.</P>
|
||||||
|
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
<H3><A name="After_Initial_Analysis"></A>Initial Analysis Navigation Options</H3>
|
||||||
|
<P>These options control the behavior of the tool after the initial analysis has
|
||||||
|
completed.</P>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P><B>Ask To Reposition Program</B> - If selected, the user will be prompted if they
|
||||||
|
would like the program to be positioned to any newly discovered starting symbols as
|
||||||
|
specified in the <A href="#Start_Symbols">Start Symbols</A> option.</P>
|
||||||
|
|
||||||
|
<P><B>Auto Reposition If Not Moved</B> - If selected, the program will automatically
|
||||||
|
be reposition to any newly discovered starting symbols as specified in the
|
||||||
|
<A href="#Start_Symbols">Start Symbols</A> option, provided the user has
|
||||||
|
not manually moved the cursor off the starting location address. If they have manually
|
||||||
|
moved the cursor, then the behavior will revert to the setting of the "Ask To
|
||||||
|
Reposition Program" option above.</P>
|
||||||
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.events;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin event class for notification of when programs have completed being analyzed for the first
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
public class FirstTimeAnalyzedPluginEvent extends PluginEvent {
|
||||||
|
public static final String EVENT_NAME = "FirstTimeAnalyzed";
|
||||||
|
|
||||||
|
private WeakReference<Program> programRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param sourceName source name of the plugin that created this event
|
||||||
|
* @param program the program that has been analyzed for the first time
|
||||||
|
*/
|
||||||
|
public FirstTimeAnalyzedPluginEvent(String sourceName, Program program) {
|
||||||
|
super(sourceName, EVENT_NAME);
|
||||||
|
this.programRef = new WeakReference<Program>(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Program} that has just been analyzed for the first time. This method
|
||||||
|
* can return null, but only if the program has been closed and is no longer in use which
|
||||||
|
* can't happen if the method is called during the original event notification.
|
||||||
|
* @return the {@link Program} that has just been analyzed for the first time.
|
||||||
|
*/
|
||||||
|
public Program getProgram() {
|
||||||
|
return programRef.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-10
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.events;
|
package ghidra.app.events;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
import ghidra.framework.plugintool.PluginEvent;
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin event class for notification of programs being created, opened, or
|
* Plugin event class for notification of programs being created, opened, or
|
||||||
* closed.
|
* closed.
|
||||||
@@ -29,11 +28,6 @@ import java.lang.ref.WeakReference;
|
|||||||
public class ProgramActivatedPluginEvent extends PluginEvent {
|
public class ProgramActivatedPluginEvent extends PluginEvent {
|
||||||
|
|
||||||
static final String NAME = "Program Activated";
|
static final String NAME = "Program Activated";
|
||||||
// static final String TOOL_EVENT_NAME = "Program Activated";
|
|
||||||
//
|
|
||||||
// static {
|
|
||||||
// registerPluginEventMapping(OpenProgramPluginEvent.class, TOOL_EVENT_NAME);
|
|
||||||
// }
|
|
||||||
|
|
||||||
private WeakReference<Program> newProgramRef;
|
private WeakReference<Program> newProgramRef;
|
||||||
|
|
||||||
@@ -48,8 +42,10 @@ public class ProgramActivatedPluginEvent extends PluginEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the new activated program. May be null.
|
* Returns the {@link Program} that has is being activated. This method
|
||||||
* @return null if the event if for a program closing.
|
* can return null, but it is unlikely. It will only return null if the program has been closed
|
||||||
|
* and is no longer in use.
|
||||||
|
* @return the {@link Program} that has just been analyzed for the first time.
|
||||||
*/
|
*/
|
||||||
public Program getActiveProgram() {
|
public Program getActiveProgram() {
|
||||||
return newProgramRef.get();
|
return newProgramRef.get();
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ public class ProgramClosedPluginEvent extends PluginEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the program on this event.
|
* Returns the {@link Program} that has just been opened. This method
|
||||||
* @return null if the event if for a program closing.
|
* can return null, but only if the method is called some time after the original event
|
||||||
|
* notification.
|
||||||
|
* @return the {@link Program} that has just been analyzed for the first time.
|
||||||
*/
|
*/
|
||||||
public Program getProgram() {
|
public Program getProgram() {
|
||||||
return programRef.get();
|
return programRef.get();
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -37,8 +36,7 @@ public final class ProgramHighlightPluginEvent extends PluginEvent {
|
|||||||
* @param hl Program selection containing the selected address set.
|
* @param hl Program selection containing the selected address set.
|
||||||
* @param program program being highlighted
|
* @param program program being highlighted
|
||||||
*/
|
*/
|
||||||
public ProgramHighlightPluginEvent(String src,ProgramSelection hl,
|
public ProgramHighlightPluginEvent(String src, ProgramSelection hl, Program program) {
|
||||||
Program program) {
|
|
||||||
super(src, NAME);
|
super(src, NAME);
|
||||||
this.highlight = hl;
|
this.highlight = hl;
|
||||||
this.programRef = new WeakReference<Program>(program);
|
this.programRef = new WeakReference<Program>(program);
|
||||||
@@ -54,6 +52,7 @@ public final class ProgramHighlightPluginEvent extends PluginEvent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Program object that the highlight refers to.
|
* Returns the Program object that the highlight refers to.
|
||||||
|
* @return the Program object that the highlight refers to.
|
||||||
*/
|
*/
|
||||||
public Program getProgram() {
|
public Program getProgram() {
|
||||||
return programRef.get();
|
return programRef.get();
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ public class ProgramOpenedPluginEvent extends PluginEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the program on this event.
|
* Returns the {@link Program} that has just been opened. This method
|
||||||
* @return null if the event if for a program closing.
|
* can return null, but only if the program has been closed and is no longer in use which
|
||||||
|
* can't happen if the method is called during the original event notification.
|
||||||
|
* @return the {@link Program} that has just been analyzed for the first time.
|
||||||
*/
|
*/
|
||||||
public Program getProgram() {
|
public Program getProgram() {
|
||||||
return programRef.get();
|
return programRef.get();
|
||||||
|
|||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.events;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin event class for notification that plugin first pass processing of a newly activated
|
||||||
|
* program is complete. More specifically, all plugins have received and had a chance
|
||||||
|
* to react to a {@link ProgramActivatedPluginEvent}.
|
||||||
|
*/
|
||||||
|
public class ProgramPostActivatedPluginEvent extends PluginEvent {
|
||||||
|
|
||||||
|
static final String NAME = "Post Program Activated";
|
||||||
|
private WeakReference<Program> newProgramRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param source name of the plugin that created this event
|
||||||
|
* @param activeProgram the program that has been activated
|
||||||
|
*/
|
||||||
|
public ProgramPostActivatedPluginEvent(String source, Program activeProgram) {
|
||||||
|
super(source, NAME);
|
||||||
|
this.newProgramRef = new WeakReference<Program>(activeProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the new activated program. May be null.
|
||||||
|
* @return null if the event if for a program closing.
|
||||||
|
*/
|
||||||
|
public Program getActiveProgram() {
|
||||||
|
return newProgramRef.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -55,6 +55,7 @@ public abstract class ProgramPlugin extends Plugin {
|
|||||||
public ProgramPlugin(PluginTool plugintool) {
|
public ProgramPlugin(PluginTool plugintool) {
|
||||||
super(plugintool);
|
super(plugintool);
|
||||||
internalRegisterEventConsumed(ProgramActivatedPluginEvent.class);
|
internalRegisterEventConsumed(ProgramActivatedPluginEvent.class);
|
||||||
|
internalRegisterEventConsumed(ProgramPostActivatedPluginEvent.class);
|
||||||
internalRegisterEventConsumed(ProgramLocationPluginEvent.class);
|
internalRegisterEventConsumed(ProgramLocationPluginEvent.class);
|
||||||
internalRegisterEventConsumed(ProgramSelectionPluginEvent.class);
|
internalRegisterEventConsumed(ProgramSelectionPluginEvent.class);
|
||||||
internalRegisterEventConsumed(ProgramHighlightPluginEvent.class);
|
internalRegisterEventConsumed(ProgramHighlightPluginEvent.class);
|
||||||
@@ -160,12 +161,16 @@ public abstract class ProgramPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
highlightChanged(currentHighlight);
|
highlightChanged(currentHighlight);
|
||||||
}
|
}
|
||||||
|
else if (event instanceof ProgramPostActivatedPluginEvent ev) {
|
||||||
|
postProgramActivated(ev.getActiveProgram());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclass should override this method if it is interested when programs become active.
|
* Subclass should override this method if it is interested when programs become active.
|
||||||
* Note: this method is called in response to a ProgramActivatedPluginEvent.
|
* Note: this method is called in response to a ProgramActivatedPluginEvent.
|
||||||
*
|
* <P>
|
||||||
* At the time this method is called,
|
* At the time this method is called,
|
||||||
* the "currentProgram" variable will be set the new active program.
|
* the "currentProgram" variable will be set the new active program.
|
||||||
*
|
*
|
||||||
@@ -175,6 +180,20 @@ public abstract class ProgramPlugin extends Plugin {
|
|||||||
// override
|
// override
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass should override this method if it is interested when programs become active and
|
||||||
|
* all plugins have had a chance to process the {@link ProgramActivatedPluginEvent}.
|
||||||
|
* Note: this method is called in response to a {@link ProgramPostActivatedPluginEvent}
|
||||||
|
* <P>
|
||||||
|
* At the time this method is called,
|
||||||
|
* the "currentProgram" variable will be set the new active program.
|
||||||
|
*
|
||||||
|
* @param program the new program going active.
|
||||||
|
*/
|
||||||
|
protected void postProgramActivated(Program program) {
|
||||||
|
// override
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses should override this method if it is interested when a program is closed.
|
* Subclasses should override this method if it is interested when a program is closed.
|
||||||
*
|
*
|
||||||
|
|||||||
+7
-5
@@ -18,6 +18,7 @@ package ghidra.app.plugin.core.analysis;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
@@ -132,7 +133,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
|||||||
|
|
||||||
private MessageLog log = new MessageLog();
|
private MessageLog log = new MessageLog();
|
||||||
|
|
||||||
private List<AutoAnalysisManagerListener> listeners = new ArrayList<>();
|
private List<AutoAnalysisManagerListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
private EventQueueID eventQueueID;
|
private EventQueueID eventQueueID;
|
||||||
|
|
||||||
@@ -850,6 +851,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
|||||||
for (AnalysisTaskList list : taskArray) {
|
for (AnalysisTaskList list : taskArray) {
|
||||||
list.notifyAnalysisEnded(program);
|
list.notifyAnalysisEnded(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AutoAnalysisManagerListener listener : listeners) {
|
for (AutoAnalysisManagerListener listener : listeners) {
|
||||||
listener.analysisEnded(this);
|
listener.analysisEnded(this);
|
||||||
}
|
}
|
||||||
@@ -1268,13 +1270,13 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
|||||||
if (testLen > spacer.length()) {
|
if (testLen > spacer.length()) {
|
||||||
testLen = spacer.length() - 5;
|
testLen = spacer.length() - 5;
|
||||||
}
|
}
|
||||||
taskTimesStringBuf.append(
|
taskTimesStringBuf
|
||||||
" " + element + spacer.substring(testLen) + secString + "\n");
|
.append(" " + element + spacer.substring(testLen) + secString + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
taskTimesStringBuf.append("-----------------------------------------------------\n");
|
taskTimesStringBuf.append("-----------------------------------------------------\n");
|
||||||
taskTimesStringBuf.append(
|
taskTimesStringBuf
|
||||||
" Total Time " + (int) (totalTaskTime / 1000.00) + " secs\n");
|
.append(" Total Time " + (int) (totalTaskTime / 1000.00) + " secs\n");
|
||||||
taskTimesStringBuf.append("-----------------------------------------------------\n");
|
taskTimesStringBuf.append("-----------------------------------------------------\n");
|
||||||
|
|
||||||
return taskTimesStringBuf.toString();
|
return taskTimesStringBuf.toString();
|
||||||
|
|||||||
+32
-30
@@ -17,8 +17,6 @@ package ghidra.app.plugin.core.analysis;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
@@ -59,7 +57,7 @@ import ghidra.util.task.TaskLauncher;
|
|||||||
category = PluginCategoryNames.ANALYSIS,
|
category = PluginCategoryNames.ANALYSIS,
|
||||||
shortDescription = "Manages auto-analysis",
|
shortDescription = "Manages auto-analysis",
|
||||||
description = "Provides coordination and a service for All Auto Analysis tasks.",
|
description = "Provides coordination and a service for All Auto Analysis tasks.",
|
||||||
eventsConsumed = { ProgramOpenedPluginEvent.class, ProgramClosedPluginEvent.class, ProgramActivatedPluginEvent.class }
|
eventsConsumed = { ProgramOpenedPluginEvent.class, ProgramClosedPluginEvent.class, ProgramActivatedPluginEvent.class, ProgramPostActivatedPluginEvent.class }
|
||||||
)
|
)
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerListener {
|
public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerListener {
|
||||||
@@ -180,16 +178,20 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
|||||||
private void analyzeCallback(Program program, ProgramSelection selection) {
|
private void analyzeCallback(Program program, ProgramSelection selection) {
|
||||||
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
|
|
||||||
analysisMgr.initializeOptions(); // get initial options
|
analysisMgr.initializeOptions(); // this allows analyzers to register options with defaults
|
||||||
|
|
||||||
if (!showOptionsDialog(program)) {
|
if (!showOptionsDialog(program)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
analysisMgr.initializeOptions(); // options may have changed
|
analysisMgr.initializeOptions(); // reloads the options in case the user changed them
|
||||||
|
|
||||||
// At this point, any analysis that is done is consider to be true for analyzed.
|
// check if this is the first time this program is being analyzed. If so,
|
||||||
GhidraProgramUtilities.setAnalyzedFlag(program, true);
|
// schedule a callback when it is completed to send a FirstTimeAnalyzedPluginEvent
|
||||||
|
boolean isAnalyzed = GhidraProgramUtilities.isAnalyzedFlagSet(program);
|
||||||
|
if (!isAnalyzed) {
|
||||||
|
analysisMgr.addListener(new FirstTimeAnalyzedCallback());
|
||||||
|
}
|
||||||
|
|
||||||
// start analysis to set the flag, but it probably won't do more. A bit goofy but better
|
// start analysis to set the flag, but it probably won't do more. A bit goofy but better
|
||||||
// than the way it was
|
// than the way it was
|
||||||
@@ -224,16 +226,13 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processEvent(PluginEvent event) {
|
public void processEvent(PluginEvent event) {
|
||||||
if (event instanceof ProgramClosedPluginEvent) {
|
if (event instanceof ProgramClosedPluginEvent ev) {
|
||||||
ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
|
|
||||||
programClosed(ev.getProgram());
|
programClosed(ev.getProgram());
|
||||||
}
|
}
|
||||||
else if (event instanceof ProgramOpenedPluginEvent) {
|
else if (event instanceof ProgramOpenedPluginEvent ev) {
|
||||||
ProgramOpenedPluginEvent ev = (ProgramOpenedPluginEvent) event;
|
|
||||||
programOpened(ev.getProgram());
|
programOpened(ev.getProgram());
|
||||||
}
|
}
|
||||||
else if (event instanceof ProgramActivatedPluginEvent) {
|
else if (event instanceof ProgramActivatedPluginEvent ev) {
|
||||||
ProgramActivatedPluginEvent ev = (ProgramActivatedPluginEvent) event;
|
|
||||||
Program program = ev.getActiveProgram();
|
Program program = ev.getActiveProgram();
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
removeOneShotActions();
|
removeOneShotActions();
|
||||||
@@ -243,6 +242,12 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
|||||||
addOneShotActions(program);
|
addOneShotActions(program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (event instanceof ProgramPostActivatedPluginEvent ev) {
|
||||||
|
Program program = ev.getActiveProgram();
|
||||||
|
if (program != null) {
|
||||||
|
postProgramActivated(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void programOpened(final Program program) {
|
protected void programOpened(final Program program) {
|
||||||
@@ -256,30 +261,18 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
|||||||
new HelpLocation("AutoAnalysisPlugin", "Auto_Analysis_Option"));
|
new HelpLocation("AutoAnalysisPlugin", "Auto_Analysis_Option"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void programActivated(final Program program) {
|
private void programActivated(Program program) {
|
||||||
|
|
||||||
program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST)
|
program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST)
|
||||||
.registerOption(
|
.registerOption(StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
|
||||||
StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
|
|
||||||
"Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
|
"Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
|
||||||
|
|
||||||
// invokeLater() to ensure that all other plugins have been notified of the program
|
|
||||||
// activated. This makes sure plugins like the Listing have opened and painted the
|
|
||||||
// program.
|
|
||||||
//
|
|
||||||
// If the user decided to instantly close the code browser before we get to run anything,
|
|
||||||
// an exception could be thrown! Therefore, we must check to see if the program is closed
|
|
||||||
// at this point before we run anything.
|
|
||||||
//
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
if (program.isClosed()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
final AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
|
||||||
|
private void postProgramActivated(Program program) {
|
||||||
|
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
if (analysisMgr.askToAnalyze(tool)) {
|
if (analysisMgr.askToAnalyze(tool)) {
|
||||||
analyzeCallback(program, null);
|
analyzeCallback(program, null);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -373,4 +366,13 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
|||||||
return canAnalyze;
|
return canAnalyze;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FirstTimeAnalyzedCallback implements AutoAnalysisManagerListener {
|
||||||
|
@Override
|
||||||
|
public void analysisEnded(AutoAnalysisManager manager) {
|
||||||
|
manager.removeListener(this);
|
||||||
|
tool.firePluginEvent(new FirstTimeAnalyzedPluginEvent(AutoAnalysisPlugin.this.getName(),
|
||||||
|
manager.getProgram()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-5
@@ -29,10 +29,18 @@ import ghidra.util.HelpLocation;
|
|||||||
public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||||
|
|
||||||
static final String NAVIGATION_TOPIC = "Navigation";
|
static final String NAVIGATION_TOPIC = "Navigation";
|
||||||
public static final String SUB_OPTION = "Starting Program Location";
|
public static final String START_LOCATION_SUB_OPTION = "Starting Program Location";
|
||||||
public static final String START_LOCATION_TYPE_OPTION = SUB_OPTION + ".Start At: ";
|
public static final String START_LOCATION_TYPE_OPTION =
|
||||||
public static final String START_SYMBOLS_OPTION = SUB_OPTION + ".Start Symbols: ";
|
START_LOCATION_SUB_OPTION + ".Start At: ";
|
||||||
public static final String UNDERSCORE_OPTION = SUB_OPTION + ".Use Underscores:";
|
public static final String START_SYMBOLS_OPTION =
|
||||||
|
START_LOCATION_SUB_OPTION + ".Start Symbols: ";
|
||||||
|
public static final String UNDERSCORE_OPTION = START_LOCATION_SUB_OPTION + ".Use Underscores:";
|
||||||
|
|
||||||
|
public static final String AFTER_ANALYSIS_SUB_OPTION = "After Initial Analysis";
|
||||||
|
public static final String ASK_TO_MOVE_OPTION =
|
||||||
|
AFTER_ANALYSIS_SUB_OPTION + ".Ask To Reposition Program";
|
||||||
|
public static final String AUTO_MOVE_OPTION =
|
||||||
|
AFTER_ANALYSIS_SUB_OPTION + ".Auto Reposition If Not Moved";
|
||||||
|
|
||||||
private static final String START_LOCATION_DESCRIPTION =
|
private static final String START_LOCATION_DESCRIPTION =
|
||||||
"Determines the start location for newly opened programs.\n" +
|
"Determines the start location for newly opened programs.\n" +
|
||||||
@@ -44,6 +52,12 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
|||||||
"(Used when option above is set to \"Preferred Symbol Name\")";
|
"(Used when option above is set to \"Preferred Symbol Name\")";
|
||||||
private static final String SYMBOL_PREFIX_DESCRIPTION =
|
private static final String SYMBOL_PREFIX_DESCRIPTION =
|
||||||
"When searching for symbols, also search for the names prepended with \"_\" and \"__\".";
|
"When searching for symbols, also search for the names prepended with \"_\" and \"__\".";
|
||||||
|
public static final String ASK_TO_MOVE_DESCRIPTION =
|
||||||
|
"When initial analysis completed, asks the user if they want to reposition the" +
|
||||||
|
" program to a newly discovered starting symbol.";
|
||||||
|
public static final String AUTO_MOVE_DESCRIPTION =
|
||||||
|
"When initial analysis is completed, automatically repositions the program to " +
|
||||||
|
"a newly discovered starting symbol, provided the user hasn't manually moved.";
|
||||||
|
|
||||||
private static final String DEFAULT_STARTING_SYMBOLS =
|
private static final String DEFAULT_STARTING_SYMBOLS =
|
||||||
"main, WinMain, libc_start_main, WinMainStartup, start, entry";
|
"main, WinMain, libc_start_main, WinMainStartup, start, entry";
|
||||||
@@ -71,13 +85,15 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
|||||||
private boolean useUnderscorePrefixes;
|
private boolean useUnderscorePrefixes;
|
||||||
|
|
||||||
private ToolOptions options;
|
private ToolOptions options;
|
||||||
|
private boolean askToMove;
|
||||||
|
private boolean autoMove;
|
||||||
|
|
||||||
public ProgramStartingLocationOptions(PluginTool tool) {
|
public ProgramStartingLocationOptions(PluginTool tool) {
|
||||||
options = tool.getOptions(GhidraOptions.NAVIGATION_OPTIONS);
|
options = tool.getOptions(GhidraOptions.NAVIGATION_OPTIONS);
|
||||||
HelpLocation help = new HelpLocation(NAVIGATION_TOPIC, "Starting_Program_Location");
|
HelpLocation help = new HelpLocation(NAVIGATION_TOPIC, "Starting_Program_Location");
|
||||||
|
|
||||||
// set a help location on the group
|
// set a help location on the group
|
||||||
Options subOptions = options.getOptions(SUB_OPTION);
|
Options subOptions = options.getOptions(START_LOCATION_SUB_OPTION);
|
||||||
subOptions.setOptionsHelpLocation(help);
|
subOptions.setOptionsHelpLocation(help);
|
||||||
|
|
||||||
options.registerOption(START_LOCATION_TYPE_OPTION, StartLocationType.LAST_LOCATION, help,
|
options.registerOption(START_LOCATION_TYPE_OPTION, StartLocationType.LAST_LOCATION, help,
|
||||||
@@ -88,6 +104,13 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
|||||||
|
|
||||||
options.registerOption(UNDERSCORE_OPTION, true, help, SYMBOL_PREFIX_DESCRIPTION);
|
options.registerOption(UNDERSCORE_OPTION, true, help, SYMBOL_PREFIX_DESCRIPTION);
|
||||||
|
|
||||||
|
help = new HelpLocation(NAVIGATION_TOPIC, "After_Initial_Analysis");
|
||||||
|
subOptions = options.getOptions(AFTER_ANALYSIS_SUB_OPTION);
|
||||||
|
subOptions.setOptionsHelpLocation(help);
|
||||||
|
|
||||||
|
options.registerOption(ASK_TO_MOVE_OPTION, true, help, ASK_TO_MOVE_DESCRIPTION);
|
||||||
|
options.registerOption(AUTO_MOVE_OPTION, true, help, AUTO_MOVE_DESCRIPTION);
|
||||||
|
|
||||||
startLocationType =
|
startLocationType =
|
||||||
options.getEnum(START_LOCATION_TYPE_OPTION, StartLocationType.SYMBOL_NAME);
|
options.getEnum(START_LOCATION_TYPE_OPTION, StartLocationType.SYMBOL_NAME);
|
||||||
|
|
||||||
@@ -95,6 +118,10 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
|||||||
startSymbols = parse(symbolNames);
|
startSymbols = parse(symbolNames);
|
||||||
|
|
||||||
useUnderscorePrefixes = options.getBoolean(UNDERSCORE_OPTION, true);
|
useUnderscorePrefixes = options.getBoolean(UNDERSCORE_OPTION, true);
|
||||||
|
|
||||||
|
askToMove = options.getBoolean(ASK_TO_MOVE_OPTION, true);
|
||||||
|
autoMove = options.getBoolean(AUTO_MOVE_OPTION, true);
|
||||||
|
|
||||||
options.addOptionsChangeListener(this);
|
options.addOptionsChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +172,32 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
|||||||
options.removeOptionsChangeListener(this);
|
options.removeOptionsChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user should be asked after first analysis if they would like the
|
||||||
|
* program to be repositioned to a newly discovered starting symbol (e.g. "main")
|
||||||
|
*
|
||||||
|
* @return true if the user should be asked after first analysis if they would like the
|
||||||
|
* program to be repositioned to a newly discovered starting symbol (e.g. "main")
|
||||||
|
*/
|
||||||
|
public boolean shouldAskToRepostionAfterAnalysis() {
|
||||||
|
return askToMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the program should be repositioned to a newly discovered starting symbol
|
||||||
|
* (e.g. "main") when the first analysis is completed, provided the user hasn't manually
|
||||||
|
* changed the program's location. Note that this option has precedence over the
|
||||||
|
* {@link #shouldAskToRepostionAfterAnalysis()} option and the user will only be asked
|
||||||
|
* if they have manually moved the program.
|
||||||
|
*
|
||||||
|
* @return true if the program should be repositioned to a newly discovered starting symbol
|
||||||
|
* (e.g. "main") when the first analysis is completed, provided the user hasn't manually
|
||||||
|
* changed the program's location.
|
||||||
|
*/
|
||||||
|
public boolean shouldAutoRepositionIfNotMoved() {
|
||||||
|
return autoMove;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void optionsChanged(ToolOptions toolOptions, String optionName, Object oldValue,
|
public void optionsChanged(ToolOptions toolOptions, String optionName, Object oldValue,
|
||||||
Object newValue) {
|
Object newValue) {
|
||||||
@@ -157,5 +210,12 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
|||||||
else if (UNDERSCORE_OPTION.equals(optionName)) {
|
else if (UNDERSCORE_OPTION.equals(optionName)) {
|
||||||
useUnderscorePrefixes = (Boolean) newValue;
|
useUnderscorePrefixes = (Boolean) newValue;
|
||||||
}
|
}
|
||||||
|
else if (ASK_TO_MOVE_OPTION.equals(optionName)) {
|
||||||
|
askToMove = (Boolean) newValue;
|
||||||
|
}
|
||||||
|
else if (AUTO_MOVE_OPTION.equals(optionName)) {
|
||||||
|
autoMove = (Boolean) newValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+107
-25
@@ -21,14 +21,14 @@ import java.util.*;
|
|||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jdom.JDOMException;
|
import org.jdom.JDOMException;
|
||||||
|
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
|
import ghidra.app.events.FirstTimeAnalyzedPluginEvent;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.ProgramPlugin;
|
import ghidra.app.plugin.ProgramPlugin;
|
||||||
import ghidra.app.plugin.core.navigation.ProgramStartingLocationOptions.StartLocationType;
|
|
||||||
import ghidra.app.services.GoToService;
|
import ghidra.app.services.GoToService;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.PluginInfo;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
@@ -36,7 +36,6 @@ import ghidra.program.model.listing.ProgramUserData;
|
|||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.program.model.symbol.SymbolTable;
|
import ghidra.program.model.symbol.SymbolTable;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.Swing;
|
|
||||||
import ghidra.util.xml.XmlUtilities;
|
import ghidra.util.xml.XmlUtilities;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@@ -46,37 +45,62 @@ import ghidra.util.xml.XmlUtilities;
|
|||||||
category = PluginCategoryNames.COMMON,
|
category = PluginCategoryNames.COMMON,
|
||||||
shortDescription = "Determines the starting location when a program is opened.",
|
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.",
|
description = "This plugin watches for new programs being opened and determines the best starting location for the listing view.",
|
||||||
servicesRequired = { GoToService.class }
|
servicesRequired = { GoToService.class },
|
||||||
|
eventsConsumed = { FirstTimeAnalyzedPluginEvent.class }
|
||||||
)
|
)
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||||
|
|
||||||
|
public static enum NonActiveProgramState {
|
||||||
|
NEWLY_OPENED,
|
||||||
|
RESTORED,
|
||||||
|
FIRST_ANALYSIS_COMPLETED
|
||||||
|
}
|
||||||
|
|
||||||
private static final String LAST_LOCATION_PROPERTY = "LAST_PROGRAM_LOCATION";
|
private static final String LAST_LOCATION_PROPERTY = "LAST_PROGRAM_LOCATION";
|
||||||
private Program lastOpenedProgram;
|
|
||||||
private ProgramStartingLocationOptions startOptions;
|
private ProgramStartingLocationOptions startOptions;
|
||||||
private Map<Program, ProgramLocation> lastLocationMap = new HashMap<>();
|
private WeakHashMap<Program, ProgramLocation> currentLocationsMap = new WeakHashMap<>();
|
||||||
|
private WeakHashMap<Program, ProgramLocation> startLocationsMap = new WeakHashMap<>();
|
||||||
|
private WeakHashMap<Program, NonActiveProgramState> programStateMap = new WeakHashMap<>();
|
||||||
|
|
||||||
public ProgramStartingLocationPlugin(PluginTool tool) {
|
public ProgramStartingLocationPlugin(PluginTool tool) {
|
||||||
super(tool);
|
super(tool);
|
||||||
startOptions = new ProgramStartingLocationOptions(tool);
|
startOptions = new ProgramStartingLocationOptions(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processEvent(PluginEvent event) {
|
||||||
|
super.processEvent(event);
|
||||||
|
|
||||||
|
if (event instanceof FirstTimeAnalyzedPluginEvent ev) {
|
||||||
|
Program program = ev.getProgram();
|
||||||
|
if (program != null) {
|
||||||
|
firstAnalysisCompleted(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void firstAnalysisCompleted(Program program) {
|
||||||
|
if (program.equals(currentProgram)) {
|
||||||
|
processFirstAnalysisCompleted();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
programStateMap.put(program, NonActiveProgramState.FIRST_ANALYSIS_COMPLETED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void programOpened(Program program) {
|
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()) {
|
if (tool.isRestoringDataState()) {
|
||||||
return;
|
programStateMap.put(program, NonActiveProgramState.RESTORED);
|
||||||
}
|
}
|
||||||
if (startOptions.getStartLocationType() == StartLocationType.LOWEST_ADDRESS) {
|
else {
|
||||||
// this is what happens by default, so no need to do anything
|
programStateMap.put(program, NonActiveProgramState.NEWLY_OPENED);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
lastOpenedProgram = program;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void programClosed(Program program) {
|
protected void programClosed(Program program) {
|
||||||
ProgramLocation lastLocation = lastLocationMap.remove(program);
|
ProgramLocation lastLocation = currentLocationsMap.remove(program);
|
||||||
if (lastLocation == null) {
|
if (lastLocation == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -87,22 +111,66 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
|||||||
String xmlString = XmlUtilities.toString(saveState.saveToXml());
|
String xmlString = XmlUtilities.toString(saveState.saveToXml());
|
||||||
programUserData.setStringProperty(LAST_LOCATION_PROPERTY, xmlString);
|
programUserData.setStringProperty(LAST_LOCATION_PROPERTY, xmlString);
|
||||||
|
|
||||||
|
programStateMap.remove(program);
|
||||||
|
currentLocationsMap.remove(program);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void programActivated(Program program) {
|
protected void postProgramActivated(Program program) {
|
||||||
super.programActivated(program);
|
NonActiveProgramState state = programStateMap.remove(program);
|
||||||
if (program == lastOpenedProgram) {
|
if (state == NonActiveProgramState.NEWLY_OPENED) {
|
||||||
Swing.runLater(this::setStartingLocationForNewProgram);
|
setStartingLocationForNewProgram();
|
||||||
}
|
}
|
||||||
lastOpenedProgram = null;
|
else if (state == NonActiveProgramState.FIRST_ANALYSIS_COMPLETED) {
|
||||||
|
processFirstAnalysisCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processFirstAnalysisCompleted() {
|
||||||
|
boolean shouldAskToRepostion = startOptions.shouldAskToRepostionAfterAnalysis();
|
||||||
|
boolean autoRepositionIfNotMoved = startOptions.shouldAutoRepositionIfNotMoved();
|
||||||
|
|
||||||
|
if (!shouldAskToRepostion && !autoRepositionIfNotMoved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if analysis didn't find any starting symbol, nothing to do
|
||||||
|
Symbol symbol = findStartingSymbol(currentProgram);
|
||||||
|
if (symbol == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if already at the symbol's address, don't do anything
|
||||||
|
if (currentLocation != null && currentLocation.getAddress().equals(symbol.getAddress())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autoRepositionIfNotMoved && isProgramAtStartingLocation()) {
|
||||||
|
gotoLocation(symbol.getProgramLocation());
|
||||||
|
}
|
||||||
|
else if (shouldAskToRepostion && askToPositionProgram(symbol)) {
|
||||||
|
gotoLocation(symbol.getProgramLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean askToPositionProgram(Symbol symbol) {
|
||||||
|
int result = OptionDialog.showYesNoDialog(null, "Reposition Program?",
|
||||||
|
"Analysis found the symbol \"" + symbol.getName() +
|
||||||
|
"\". Would you like to go to that symbol?");
|
||||||
|
return result == OptionDialog.YES_OPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void locationChanged(ProgramLocation loc) {
|
protected void locationChanged(ProgramLocation loc) {
|
||||||
if (loc != null) {
|
if (loc != null) {
|
||||||
Program program = loc.getProgram();
|
Program program = loc.getProgram();
|
||||||
lastLocationMap.put(program, loc);
|
currentLocationsMap.put(program, loc);
|
||||||
|
|
||||||
|
// the startLocationsMap only gets updated with the first location
|
||||||
|
if (!startLocationsMap.containsKey(program)) {
|
||||||
|
startLocationsMap.put(program, loc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,13 +179,27 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GoToService gotoService = tool.getService(GoToService.class);
|
|
||||||
|
|
||||||
ProgramLocation location = getStartingProgramLocation(currentProgram);
|
ProgramLocation location = getStartingProgramLocation(currentProgram);
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
|
gotoLocation(location);
|
||||||
|
startLocationsMap.put(currentProgram, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gotoLocation(ProgramLocation location) {
|
||||||
|
GoToService gotoService = tool.getService(GoToService.class);
|
||||||
gotoService.goTo(location);
|
gotoService.goTo(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isProgramAtStartingLocation() {
|
||||||
|
ProgramLocation startLocation = startLocationsMap.get(currentProgram);
|
||||||
|
if (startLocation == null || currentLocation == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// just compare address, analysis may have tweaked the current location even
|
||||||
|
// the user didn't move
|
||||||
|
return startLocation.getAddress().equals(currentLocation.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramLocation getStartingProgramLocation(Program program) {
|
private ProgramLocation getStartingProgramLocation(Program program) {
|
||||||
@@ -129,7 +211,7 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
|||||||
}
|
}
|
||||||
// fall through and try symbol name
|
// fall through and try symbol name
|
||||||
case SYMBOL_NAME:
|
case SYMBOL_NAME:
|
||||||
Symbol symbol = fingStartingSymbol(program);
|
Symbol symbol = findStartingSymbol(program);
|
||||||
if (symbol != null) {
|
if (symbol != null) {
|
||||||
return symbol.getProgramLocation();
|
return symbol.getProgramLocation();
|
||||||
}
|
}
|
||||||
@@ -158,7 +240,7 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Symbol fingStartingSymbol(Program program) {
|
private Symbol findStartingSymbol(Program program) {
|
||||||
List<String> symbolNames = startOptions.getStartingSymbolNames();
|
List<String> symbolNames = startOptions.getStartingSymbolNames();
|
||||||
boolean useUnderscores = startOptions.useUnderscorePrefixes();
|
boolean useUnderscores = startOptions.useUnderscorePrefixes();
|
||||||
for (String symbolName : symbolNames) {
|
for (String symbolName : symbolNames) {
|
||||||
|
|||||||
+18
-6
@@ -51,6 +51,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
|
|
||||||
private Runnable programChangedRunnable;
|
private Runnable programChangedRunnable;
|
||||||
private boolean hasUnsavedPrograms;
|
private boolean hasUnsavedPrograms;
|
||||||
|
private String pluginName;
|
||||||
|
|
||||||
// These data structures are accessed from multiple threads. Rather than synchronizing all
|
// These data structures are accessed from multiple threads. Rather than synchronizing all
|
||||||
// accesses, we have chosen to be weakly consistent. We assume that any out-of-date checks
|
// accesses, we have chosen to be weakly consistent. We assume that any out-of-date checks
|
||||||
@@ -64,6 +65,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
MultiProgramManager(ProgramManagerPlugin programManagerPlugin) {
|
MultiProgramManager(ProgramManagerPlugin programManagerPlugin) {
|
||||||
this.plugin = programManagerPlugin;
|
this.plugin = programManagerPlugin;
|
||||||
this.tool = programManagerPlugin.getTool();
|
this.tool = programManagerPlugin.getTool();
|
||||||
|
this.pluginName = plugin.getName();
|
||||||
|
|
||||||
txMonitor = new TransactionMonitor();
|
txMonitor = new TransactionMonitor();
|
||||||
txMonitor.setName("Transaction Open (Program being modified)");
|
txMonitor.setName("Transaction Open (Program being modified)");
|
||||||
@@ -260,25 +262,35 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
if (toolState != null) {
|
if (toolState != null) {
|
||||||
toolState.restoreTool();
|
toolState.restoreTool();
|
||||||
}
|
}
|
||||||
|
// only fire the post activated event when a program is activated (we send activated with
|
||||||
|
// null program to represent a phantom de-activated event)
|
||||||
|
if (newProgram != null) {
|
||||||
|
firePostActivatedEvent(newProgram);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireOpenEvents(Program program) {
|
private void fireOpenEvents(Program program) {
|
||||||
plugin.firePluginEvent(new ProgramOpenedPluginEvent("", program));
|
plugin.firePluginEvent(new ProgramOpenedPluginEvent(pluginName, program));
|
||||||
plugin.firePluginEvent(new OpenProgramPluginEvent("", program));
|
plugin.firePluginEvent(new OpenProgramPluginEvent(pluginName, program));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireCloseEvents(Program program) {
|
private void fireCloseEvents(Program program) {
|
||||||
plugin.firePluginEvent(new ProgramClosedPluginEvent("", program));
|
plugin.firePluginEvent(new ProgramClosedPluginEvent(pluginName, program));
|
||||||
plugin.firePluginEvent(new CloseProgramPluginEvent("", program, true));
|
plugin.firePluginEvent(new CloseProgramPluginEvent(pluginName, program, true));
|
||||||
// tool.contextChanged();
|
// tool.contextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireActivatedEvent(Program newProgram) {
|
private void fireActivatedEvent(Program newProgram) {
|
||||||
plugin.firePluginEvent(new ProgramActivatedPluginEvent("", newProgram));
|
plugin.firePluginEvent(new ProgramActivatedPluginEvent(pluginName, newProgram));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void firePostActivatedEvent(Program newProgram) {
|
||||||
|
plugin.firePluginEvent(new ProgramPostActivatedPluginEvent(pluginName, newProgram));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireVisibilityChangeEvent(Program program, boolean isVisible) {
|
private void fireVisibilityChangeEvent(Program program, boolean isVisible) {
|
||||||
plugin.firePluginEvent(new ProgramVisibilityChangePluginEvent("", program, isVisible));
|
plugin.firePluginEvent(
|
||||||
|
new ProgramVisibilityChangePluginEvent(pluginName, program, isVisible));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -89,4 +89,23 @@ public class GhidraProgramUtilities {
|
|||||||
program.endTransaction(transactionID, true);
|
program.endTransaction(transactionID, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the program has been analyzed at least once.
|
||||||
|
* @param program the program to test to see if it has been analyzed
|
||||||
|
* @return true if the program has been analyzed at least once.
|
||||||
|
*/
|
||||||
|
public static boolean isAnalyzedFlagSet(Program program) {
|
||||||
|
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||||
|
|
||||||
|
// we first have to check if the flag has even been created because checking the flag
|
||||||
|
// directly causes it to be created if it doesn't exist and we unfortunately use the
|
||||||
|
// existence of the flag to know whether or not to ask the user if they want to start
|
||||||
|
// analysis
|
||||||
|
if (!options.isRegistered(Program.ANALYZED)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.getBoolean(Program.ANALYZED, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+127
-11
@@ -19,7 +19,9 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
import ghidra.GhidraOptions;
|
import ghidra.GhidraOptions;
|
||||||
|
import ghidra.app.events.FirstTimeAnalyzedPluginEvent;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
@@ -57,7 +59,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpensToStartingSymbolByDefault() throws Exception {
|
public void testOpensToStartingSymbolByDefault() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
builder.createLabel("0x105", "main");
|
builder.createLabel("0x105", "main");
|
||||||
loadProgram(builder.getProgram());
|
loadProgram(builder.getProgram());
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpensToLowestCodeBlock() throws Exception {
|
public void testOpensToLowestCodeBlock() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
MemoryBlock block = builder.createMemory(".text", "0x200", 0x200);
|
MemoryBlock block = builder.createMemory(".text", "0x200", 0x200);
|
||||||
builder.setExecute(block, true);
|
builder.setExecute(block, true);
|
||||||
builder.createLabel("0x105", "main");
|
builder.createLabel("0x105", "main");
|
||||||
@@ -80,7 +82,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpensToStartingSymbolNotFirstInSymbolList() throws Exception {
|
public void testOpensToStartingSymbolNotFirstInSymbolList() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
setSymbolListOption("main, foobar, start");
|
setSymbolListOption("main, foobar, start");
|
||||||
builder.createLabel("0x107", "start");
|
builder.createLabel("0x107", "start");
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpensToFirstSymbolWhenMutlipesAreFoud() throws Exception {
|
public void testOpensToFirstSymbolWhenMutlipesAreFoud() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
setSymbolListOption("main, start");
|
setSymbolListOption("main, start");
|
||||||
builder.createLabel("0x110", "start");
|
builder.createLabel("0x110", "start");
|
||||||
builder.createLabel("0x105", "start");
|
builder.createLabel("0x105", "start");
|
||||||
@@ -103,7 +105,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpensToStartingSymbolWithOneUndercore() throws Exception {
|
public void testOpensToStartingSymbolWithOneUndercore() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
setSymbolListOption("main, start");
|
setSymbolListOption("main, start");
|
||||||
builder.createLabel("0x105", "_main");
|
builder.createLabel("0x105", "_main");
|
||||||
builder.createLabel("0x107", "start");
|
builder.createLabel("0x107", "start");
|
||||||
@@ -115,7 +117,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpensToStartingSymbolWithTwoUndercores() throws Exception {
|
public void testOpensToStartingSymbolWithTwoUndercores() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
setSymbolListOption("main, start");
|
setSymbolListOption("main, start");
|
||||||
builder.createLabel("0x105", "__main");
|
builder.createLabel("0x105", "__main");
|
||||||
builder.createLabel("0x107", "_start");
|
builder.createLabel("0x107", "_start");
|
||||||
@@ -128,7 +130,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoUnderscoresSearching() throws Exception {
|
public void testNoUnderscoresSearching() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
setSymbolListOption("main, start");
|
setSymbolListOption("main, start");
|
||||||
setUnderscoreOption(false);
|
setUnderscoreOption(false);
|
||||||
builder.createLabel("0x105", "__main");
|
builder.createLabel("0x105", "__main");
|
||||||
@@ -142,7 +144,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOptionToStartAtLowestAddress() throws Exception {
|
public void testOptionToStartAtLowestAddress() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
builder.createLabel("0x105", "main");
|
builder.createLabel("0x105", "main");
|
||||||
setOptionToLowestAddress();
|
setOptionToLowestAddress();
|
||||||
loadProgram(builder.getProgram());
|
loadProgram(builder.getProgram());
|
||||||
@@ -152,7 +154,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOptionToStartAtLastLocation() throws Exception {
|
public void testOptionToStartAtLastLocation() throws Exception {
|
||||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
ProgramDB program = builder.getProgram();
|
ProgramDB program = builder.getProgram();
|
||||||
loadProgram(program);
|
loadProgram(program);
|
||||||
cb.goTo(new ProgramLocation(program, addr("0x107")));
|
cb.goTo(new ProgramLocation(program, addr("0x107")));
|
||||||
@@ -162,10 +164,124 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
assertEquals(addr("0x107"), cb.getCurrentAddress());
|
assertEquals(addr("0x107"), cb.getCurrentAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProgramAutoRepostionsAfterAnalysis() throws Exception {
|
||||||
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
|
loadProgram(builder.getProgram());
|
||||||
|
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
simulateAnaysisCreatingMain(builder, "0x105");
|
||||||
|
|
||||||
|
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProgramAsksToRepostionsAfterAnalysisYes() throws Exception {
|
||||||
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
|
loadProgram(builder.getProgram());
|
||||||
|
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
cb.goToField(addr("0x102"), "Address", 0, 0);
|
||||||
|
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
simulateAnaysisCreatingMain(builder, "0x105");
|
||||||
|
|
||||||
|
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
pressButtonByText(dialog, "Yes");
|
||||||
|
waitForSwing();
|
||||||
|
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProgramAsksToRepostionsAfterAnalysisNo() throws Exception {
|
||||||
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
|
loadProgram(builder.getProgram());
|
||||||
|
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
cb.goToField(addr("0x102"), "Address", 0, 0);
|
||||||
|
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
simulateAnaysisCreatingMain(builder, "0x105");
|
||||||
|
|
||||||
|
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
pressButtonByText(dialog, "No");
|
||||||
|
waitForSwing();
|
||||||
|
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAutoMoveOff() throws Exception {
|
||||||
|
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||||
|
loadProgram(builder.getProgram());
|
||||||
|
setOptionToNotAutoMove();
|
||||||
|
setOptionToNotAsk();
|
||||||
|
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
simulateAnaysisCreatingMain(builder, "0x105");
|
||||||
|
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnalysisHappensWhenProgramIsNotActiveNoUserMove() throws Exception {
|
||||||
|
ProgramBuilder builder1 = getProgramBuilder("program 1", "0x100");
|
||||||
|
loadProgram(builder1.getProgram());
|
||||||
|
ProgramBuilder builder2 = getProgramBuilder("program 2", "0x100");
|
||||||
|
loadProgram(builder2.getProgram());
|
||||||
|
|
||||||
|
simulateAnaysisCreatingMain(builder1, "0x104");
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
env.close(builder2.getProgram());
|
||||||
|
|
||||||
|
assertEquals(addr("0x104"), cb.getCurrentAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnalysisHappensWhenProgramIsNotActiveWithUserMove() throws Exception {
|
||||||
|
ProgramBuilder builder1 = getProgramBuilder("program 1", "0x100");
|
||||||
|
loadProgram(builder1.getProgram());
|
||||||
|
cb.goToField(addr("0x102"), "Address", 0, 0);
|
||||||
|
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
ProgramBuilder builder2 = getProgramBuilder("program 2", "0x100");
|
||||||
|
loadProgram(builder2.getProgram());
|
||||||
|
|
||||||
|
simulateAnaysisCreatingMain(builder1, "0x104");
|
||||||
|
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||||
|
|
||||||
|
runSwingLater(() -> env.close(builder2.getProgram()));
|
||||||
|
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
pressButtonByText(dialog, "Yes");
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
assertEquals(addr("0x104"), cb.getCurrentAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void simulateAnaysisCreatingMain(ProgramBuilder builder, String address) {
|
||||||
|
builder.createLabel(address, "main");
|
||||||
|
runSwingLater(() -> tool
|
||||||
|
.firePluginEvent(new FirstTimeAnalyzedPluginEvent("test", builder.getProgram())));
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
private void setUnderscoreOption(boolean b) {
|
private void setUnderscoreOption(boolean b) {
|
||||||
options.setBoolean(ProgramStartingLocationOptions.UNDERSCORE_OPTION, b);
|
options.setBoolean(ProgramStartingLocationOptions.UNDERSCORE_OPTION, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setOptionToNotAsk() {
|
||||||
|
options.setBoolean(ProgramStartingLocationOptions.ASK_TO_MOVE_OPTION, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOptionToNotAutoMove() {
|
||||||
|
options.setBoolean(ProgramStartingLocationOptions.AUTO_MOVE_OPTION, false);
|
||||||
|
}
|
||||||
|
|
||||||
private void setOptionToLowestAddress() {
|
private void setOptionToLowestAddress() {
|
||||||
options.setEnum(ProgramStartingLocationOptions.START_LOCATION_TYPE_OPTION,
|
options.setEnum(ProgramStartingLocationOptions.START_LOCATION_TYPE_OPTION,
|
||||||
ProgramStartingLocationOptions.StartLocationType.LOWEST_ADDRESS);
|
ProgramStartingLocationOptions.StartLocationType.LOWEST_ADDRESS);
|
||||||
@@ -180,8 +296,8 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
options.setString(ProgramStartingLocationOptions.START_SYMBOLS_OPTION, symbolListString);
|
options.setString(ProgramStartingLocationOptions.START_SYMBOLS_OPTION, symbolListString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramBuilder getProgramBuilder(String baseAddress) throws Exception {
|
private ProgramBuilder getProgramBuilder(String name, String baseAddress) throws Exception {
|
||||||
ProgramBuilder builder = new ProgramBuilder();
|
ProgramBuilder builder = new ProgramBuilder(name, ProgramBuilder._TOY);
|
||||||
builder.createMemory(".data", baseAddress, 0x100);
|
builder.createMemory(".data", baseAddress, 0x100);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user