Merge remote-tracking branch 'origin/GP-4636_Dan_mapFailureInConsole--SQUASHED' into Ghidra_11.1

This commit is contained in:
Ryan Kurtz 2024-05-31 06:04:27 -04:00
commit bbd11bd1e0
8 changed files with 435 additions and 58 deletions

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.debug.gui.modules; package ghidra.debug.api.modules;
import java.util.Objects; import java.util.Objects;
@ -43,10 +43,9 @@ public class DebuggerMissingModuleActionContext extends DefaultActionContext {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (!(obj instanceof DebuggerMissingModuleActionContext)) { if (!(obj instanceof DebuggerMissingModuleActionContext that)) {
return false; return false;
} }
DebuggerMissingModuleActionContext that = (DebuggerMissingModuleActionContext) obj;
if (!this.module.equals(that.module)) { if (!this.module.equals(that.module)) {
return false; return false;
} }

View file

@ -0,0 +1,98 @@
/* ###
* 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.debug.api.modules;
import java.util.Objects;
import docking.DefaultActionContext;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
public class DebuggerMissingProgramActionContext extends DefaultActionContext {
public static Address getMappingProbeAddress(Program program) {
if (program == null) {
return null;
}
AddressIterator eepi = program.getSymbolTable().getExternalEntryPointIterator();
if (eepi.hasNext()) {
return eepi.next();
}
InstructionIterator ii = program.getListing().getInstructions(true);
if (ii.hasNext()) {
return ii.next().getAddress();
}
AddressSetView es = program.getMemory().getExecuteSet();
if (!es.isEmpty()) {
return es.getMinAddress();
}
if (!program.getMemory().isEmpty()) {
return program.getMinAddress();
}
return null;
}
private final Trace trace;
private final Program program;
private final int hashCode;
private Address probe;
public DebuggerMissingProgramActionContext(Trace trace, Program program) {
this.trace = Objects.requireNonNull(trace);
this.program = Objects.requireNonNull(program);
this.hashCode = Objects.hash(getClass(), trace, program);
}
public Trace getTrace() {
return trace;
}
public Program getProgram() {
return program;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DebuggerMissingProgramActionContext that)) {
return false;
}
if (!this.trace.equals(that.trace)) {
return false;
}
if (!this.program.equals(that.program)) {
return false;
}
return true;
}
public Address getMappingProbeAddress() {
if (probe == null) {
probe = getMappingProbeAddress(program);
}
return probe;
}
}

View file

@ -45,16 +45,14 @@ import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.AutoConfigState.ConfigStateField; import ghidra.framework.plugintool.AutoConfigState.ConfigStateField;
import ghidra.framework.plugintool.AutoConfigState.PathIsFile; import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.pty.*; import ghidra.pty.*;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceLocation; import ghidra.trace.model.TraceLocation;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.util.MessageType; import ghidra.util.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.Task; import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -149,25 +147,8 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
} }
protected Address getMappingProbeAddress() { protected Address getMappingProbeAddress() {
if (program == null) { // May be null, in which case, we won't wait for a mapping
return null; return DebuggerMissingProgramActionContext.getMappingProbeAddress(program);
}
AddressIterator eepi = program.getSymbolTable().getExternalEntryPointIterator();
if (eepi.hasNext()) {
return eepi.next();
}
InstructionIterator ii = program.getListing().getInstructions(true);
if (ii.hasNext()) {
return ii.next().getAddress();
}
AddressSetView es = program.getMemory().getExecuteSet();
if (!es.isEmpty()) {
return es.getMinAddress();
}
if (!program.getMemory().isEmpty()) {
return program.getMinAddress();
}
return null; // I guess we won't wait for a mapping, then
} }
protected CompletableFuture<Void> listenForMapping(DebuggerStaticMappingService mappingService, protected CompletableFuture<Void> listenForMapping(DebuggerStaticMappingService mappingService,
@ -664,6 +645,22 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
} }
return new LaunchResult(program, Map.of(), null, null, null, lastExc); return new LaunchResult(program, Map.of(), null, null, null, lastExc);
} }
catch (NoStaticMappingException e) {
DebuggerConsoleService consoleService =
tool.getService(DebuggerConsoleService.class);
if (consoleService == null) {
Msg.error(this, e.getMessage());
}
else {
consoleService.log(DebuggerResources.ICON_MODULES,
"<html>The trace <b>%s</b> has no mapping to its program <b>%s</b></html>"
.formatted(
HTMLUtilities.escapeHTML(trace.getDomainFile().getName()),
HTMLUtilities.escapeHTML(program.getDomainFile().getName())),
new DebuggerMissingProgramActionContext(trace, program));
}
return new LaunchResult(program, sessions, acceptor, connection, trace, e);
}
catch (Exception e) { catch (Exception e) {
DebuggerConsoleService consoleService = DebuggerConsoleService consoleService =
tool.getService(DebuggerConsoleService.class); tool.getService(DebuggerConsoleService.class);

View file

@ -152,14 +152,51 @@
<H3><A name="import_missing_module"></A><IMG alt="" src="icon.debugger.import"> Import Missing <H3><A name="import_missing_module"></A><IMG alt="" src="icon.debugger.import"> Import Missing
Module</H3> Module</H3>
<P>This action is offered to resolve a "missing module" console message. It is equivalent to <A <P>This action is offered to resolve a "missing module" console message. Such a message is
href="#import_from_fs">Import From File System</A> on the missing module.</P> reported in the <A href="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html">Debug
Console</A> when the cursor in the <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Dynamic Listing</A> cannot be
synchronized to the static listing for lack of a module mapping. This action is equivalent to
<A href="#import_from_fs">Import From File System</A> on the missing module.</P>
<H3><A name="map_missing_module"></A><IMG alt="" src="icon.debugger.map.modules"> Map Missing <H3><A name="map_missing_module"></A><IMG alt="" src="icon.debugger.map.modules"> Map Missing
Module</H3> Module</H3>
<P>This action is offered to resolve a "missing module" console message. It is equivalent to <A <P>This action is offered to resolve a "missing module" console message. Such a message is
href="#map_module_to">Map Module To</A> on the missing module.</P> reported in the <A href="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html">Debug
Console</A> when the cursor in the <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Dynamic Listing</A> cannot be
synchronized to the static listing for lack of a module mapping. This action is equivalent to
<A href="#map_module_to">Map Module To</A> on the missing module.</P>
<H3><A name="map_missing_program_retry"></A><IMG alt="" src="icon.debugger.map.auto"> Retry Map
Missing Program</H3>
<P>This action is offered to resolve a "missing program" console message. Such a message is
reported in the <A href="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html">Debug
Console</A> when the launcher fails to map the current program database to the launched trace.
This action is equivalent to <A href="#map_modules">Map Modules</A>, but considering only the
missing program and launched trace.</P>
<H3><A name="map_missing_program_current"></A><IMG alt="" src="icon.debugger.map.modules"> Map
Missing Program to Current Module</H3>
<P>This action is offered to resolve a "missing program" console message. Such a message is
reported in the <A href="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html">Debug
Console</A> when the launcher fails to map the current program database to the launched trace.
This action is only available when the current trace is the launched trace. It finds the module
containing the cursor in the <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Dynamic Listing</A> and proposes
to map it to the missing program.</P>
<H3><A name="map_missing_program_identically"></A><IMG alt="" src=
"icon.debugger.map.identically"> Map Missing Program Identically</H3>
<P>This action is offered to resolve a "missing program" console message. Such a message is
reported in the <A href="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html">Debug
Console</A> when the launcher fails to map the current program database to the launched trace.
This action is equivalent to <A href="#map_identically">Map Identically</A>, but for the
missing program and launched trace.</P>
<H3><A name="show_sections_table"></A><IMG alt="" src="icon.debugger.modules.table.sections"> <H3><A name="show_sections_table"></A><IMG alt="" src="icon.debugger.modules.table.sections">
Show Sections Table</H3> Show Sections Table</H3>
@ -179,12 +216,15 @@
Addresses</H3> Addresses</H3>
<P>This action is available when at least one module or section is selected. It selects all <P>This action is available when at least one module or section is selected. It selects all
addresses in the dynamic listing contained by the selected modules or sections.</P> addresses in the <A href="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Dynamic
Listing</A> contained by the selected modules or sections.</P>
<H3><A name="select_rows"></A><IMG alt="" src="icon.debugger.select.rows"> Select Rows</H3> <H3><A name="select_rows"></A><IMG alt="" src="icon.debugger.select.rows"> Select Rows</H3>
<P>This action is available when the dynamic listing's cursor is at a valid location. It <P>This action is available when the <A href=
selects the module and section, if applicable, containing that cursor. If the dynamic listing "help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Dynamic Listing</A>'s cursor is
has a selection, it selects all modules and sections intersecting that selection.</P> at a valid location. It selects the module and section, if applicable, containing that cursor.
If the dynamic listing has a selection, it selects all modules and sections intersecting that
selection.</P>
</BODY> </BODY>
</HTML> </HTML>

View file

@ -56,7 +56,6 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction; import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.OpenProgramAction; import ghidra.app.plugin.core.debug.gui.DebuggerResources.OpenProgramAction;
import ghidra.app.plugin.core.debug.gui.action.*; import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.plugin.core.debug.gui.thread.DebuggerTraceFileActionContext; import ghidra.app.plugin.core.debug.gui.thread.DebuggerTraceFileActionContext;
import ghidra.app.plugin.core.debug.gui.trace.DebuggerTraceTabPanel; import ghidra.app.plugin.core.debug.gui.trace.DebuggerTraceTabPanel;
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils; import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
@ -74,6 +73,7 @@ import ghidra.debug.api.action.GoToInput;
import ghidra.debug.api.action.LocationTrackingSpec; import ghidra.debug.api.action.LocationTrackingSpec;
import ghidra.debug.api.control.ControlMode; import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.listing.MultiBlendedListingBackgroundColorModel; import ghidra.debug.api.listing.MultiBlendedListingBackgroundColorModel;
import ghidra.debug.api.modules.DebuggerMissingModuleActionContext;
import ghidra.debug.api.modules.DebuggerStaticMappingChangeListener; import ghidra.debug.api.modules.DebuggerStaticMappingChangeListener;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;

View file

@ -20,6 +20,7 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin; import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent; import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
@ -37,6 +38,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
ProgramLocationPluginEvent.class, ProgramLocationPluginEvent.class,
ProgramClosedPluginEvent.class, ProgramClosedPluginEvent.class,
TraceActivatedPluginEvent.class, TraceActivatedPluginEvent.class,
TraceClosedPluginEvent.class,
}, },
servicesRequired = { servicesRequired = {
DebuggerStaticMappingService.class, DebuggerStaticMappingService.class,
@ -81,6 +83,9 @@ public class DebuggerModulesPlugin extends AbstractDebuggerPlugin {
else if (event instanceof TraceActivatedPluginEvent ev) { else if (event instanceof TraceActivatedPluginEvent ev) {
provider.coordinatesActivated(ev.getActiveCoordinates()); provider.coordinatesActivated(ev.getActiveCoordinates());
} }
else if (event instanceof TraceClosedPluginEvent ev) {
provider.traceClosed(ev.getTrace());
}
} }
@Override @Override

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.debug.gui.modules; package ghidra.app.plugin.core.debug.gui.modules;
import static ghidra.framework.main.DataTreeDialogType.*; import static ghidra.framework.main.DataTreeDialogType.OPEN;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.io.File; import java.io.File;
@ -67,15 +67,29 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.util.TraceEvent; import ghidra.trace.util.TraceEvent;
import ghidra.trace.util.TraceEvents; import ghidra.trace.util.TraceEvents;
import ghidra.util.HelpLocation; import ghidra.util.*;
import ghidra.util.Msg;
public class DebuggerModulesProvider extends ComponentProviderAdapter { public class DebuggerModulesProvider extends ComponentProviderAdapter {
protected static final AutoConfigState.ClassHandler<DebuggerModulesProvider> CONFIG_STATE_HANDLER = protected static final AutoConfigState.ClassHandler<DebuggerModulesProvider> CONFIG_STATE_HANDLER =
AutoConfigState.wireHandler(DebuggerModulesProvider.class, MethodHandles.lookup()); AutoConfigState.wireHandler(DebuggerModulesProvider.class, MethodHandles.lookup());
protected static final String NO_MODULES_PROPOSAL_SEL = """
Could not formulate a proposal for any selected module. \
You may need to import and/or open the destination images first.\
""";
protected static final String FMT_NO_MODULES_PROPOSAL_RETRY = """
Could not formulate a proposal for program '%s' to trace '%s'. \
The module may not be loaded yet, or the chosen image could be wrong.\
""";
protected static final String FMT_NO_MODULES_PROPOSAL_CURRENT = """
Could not formulate a proposal from module '%s' to program '%s'.\
""";
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) { protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) { if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false; return false;
@ -253,6 +267,57 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
} }
} }
interface MapMissingProgramRetryAction {
String NAME = "Retry Map Missing Program";
String DESCRIPTION = "Retry mapping the missing program by finding its module";
Icon ICON = DebuggerResources.ICON_MAP_AUTO;
String HELP_ANCHOR = "map_missing_program_retry";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.popupMenuIcon(ICON)
.popupMenuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface MapMissingProgramToCurrentAction {
String NAME = "Map Missing Program to Current Module";
String DESCRIPTION = "Map the missing program to the current module";
Icon ICON = DebuggerResources.ICON_MAP_MODULES;
String HELP_ANCHOR = "map_missing_program_current";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.popupMenuIcon(ICON)
.popupMenuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface MapMissingProgramIdenticallyAction {
String NAME = "Map Missing Program Identically";
String DESCRIPTION = "Map the missing program to its trace identically";
Icon ICON = DebuggerResources.ICON_MAP_IDENTICALLY;
String HELP_ANCHOR = "map_missing_program_identically";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.popupMenuIcon(ICON)
.popupMenuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface ShowSectionsTableAction { interface ShowSectionsTableAction {
String NAME = "Show Sections Table"; String NAME = "Show Sections Table";
Icon ICON = new GIcon("icon.debugger.modules.table.sections"); Icon ICON = new GIcon("icon.debugger.modules.table.sections");
@ -404,9 +469,17 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
} }
} }
protected class ForCleanupMappingChangeListener
implements DebuggerStaticMappingChangeListener {
@Override
public void mappingsChanged(Set<Trace> affectedTraces, Set<Program> affectedPrograms) {
Swing.runIfSwingOrRunLater(() -> cleanMissingProgramMessages(null, null));
}
}
final DebuggerModulesPlugin plugin; final DebuggerModulesPlugin plugin;
@AutoServiceConsumed //@AutoServiceConsumed via method
private DebuggerStaticMappingService staticMappingService; private DebuggerStaticMappingService staticMappingService;
@AutoServiceConsumed @AutoServiceConsumed
private DebuggerTraceManagerService traceManager; private DebuggerTraceManagerService traceManager;
@ -467,6 +540,13 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
DockingAction actionImportMissingModule; DockingAction actionImportMissingModule;
DockingAction actionMapMissingModule; DockingAction actionMapMissingModule;
DockingAction actionMapMissingProgramRetry;
DockingAction actionMapMissingProgramToCurrent;
DockingAction actionMapMissingProgramIdentically;
protected final ForCleanupMappingChangeListener mappingChangeListener =
new ForCleanupMappingChangeListener();
SelectAddressesAction actionSelectAddresses; SelectAddressesAction actionSelectAddresses;
ImportFromFileSystemAction actionImportFromFileSystem; ImportFromFileSystemAction actionImportFromFileSystem;
ToggleDockingAction actionShowSectionsTable; ToggleDockingAction actionShowSectionsTable;
@ -507,26 +587,37 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
importerService.importFile(root, file); importerService.importFile(root, file);
} }
void addResolutionActionMaybe(DebuggerConsoleService consoleService, DockingActionIf action) {
if (action != null) {
consoleService.addResolutionAction(action);
}
}
void removeResolutionActionMaybe(DebuggerConsoleService consoleService,
DockingActionIf action) {
if (action != null) {
consoleService.removeResolutionAction(action);
}
}
@AutoServiceConsumed @AutoServiceConsumed
private void setConsoleService(DebuggerConsoleService consoleService) { private void setConsoleService(DebuggerConsoleService consoleService) {
if (consoleService != null) { if (consoleService != null) {
if (actionImportMissingModule != null) { addResolutionActionMaybe(consoleService, actionImportMissingModule);
consoleService.addResolutionAction(actionImportMissingModule); addResolutionActionMaybe(consoleService, actionMapMissingModule);
} addResolutionActionMaybe(consoleService, actionMapMissingProgramRetry);
if (actionMapMissingModule != null) { addResolutionActionMaybe(consoleService, actionMapMissingProgramToCurrent);
consoleService.addResolutionAction(actionMapMissingModule); addResolutionActionMaybe(consoleService, actionMapMissingProgramIdentically);
}
} }
} }
protected void dispose() { protected void dispose() {
if (consoleService != null) { if (consoleService != null) {
if (actionImportMissingModule != null) { removeResolutionActionMaybe(consoleService, actionImportMissingModule);
consoleService.removeResolutionAction(actionImportMissingModule); removeResolutionActionMaybe(consoleService, actionMapMissingModule);
} removeResolutionActionMaybe(consoleService, actionMapMissingProgramRetry);
if (actionMapMissingModule != null) { removeResolutionActionMaybe(consoleService, actionMapMissingProgramToCurrent);
consoleService.removeResolutionAction(actionMapMissingModule); removeResolutionActionMaybe(consoleService, actionMapMissingProgramIdentically);
}
} }
blockChooserDialog.dispose(); blockChooserDialog.dispose();
@ -637,6 +728,20 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
.onAction(this::activatedMapMissingModule) .onAction(this::activatedMapMissingModule)
.build(); .build();
actionMapMissingProgramRetry = MapMissingProgramRetryAction.builder(plugin)
.withContext(DebuggerMissingProgramActionContext.class)
.onAction(this::activatedMapMissingProgramRetry)
.build();
actionMapMissingProgramToCurrent = MapMissingProgramToCurrentAction.builder(plugin)
.withContext(DebuggerMissingProgramActionContext.class)
.enabledWhen(this::isEnabledMapMissingProgramToCurrent)
.onAction(this::activatedMapMissingProgramToCurrent)
.build();
actionMapMissingProgramIdentically = MapMissingProgramIdenticallyAction.builder(plugin)
.withContext(DebuggerMissingProgramActionContext.class)
.onAction(this::activatedMapMissingProgramIdentically)
.build();
actionSelectAddresses = new SelectAddressesAction(); actionSelectAddresses = new SelectAddressesAction();
actionImportFromFileSystem = new ImportFromFileSystemAction(); actionImportFromFileSystem = new ImportFromFileSystemAction();
actionShowSectionsTable = ShowSectionsTableAction.builder(plugin) actionShowSectionsTable = ShowSectionsTableAction.builder(plugin)
@ -799,6 +904,78 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
mapModuleTo(context.getModule()); mapModuleTo(context.getModule());
} }
private void activatedMapMissingProgramRetry(DebuggerMissingProgramActionContext context) {
if (staticMappingService == null) {
return;
}
Program program = context.getProgram();
Trace trace = context.getTrace();
Map<TraceModule, ModuleMapProposal> map = staticMappingService.proposeModuleMaps(
trace.getModuleManager().getAllModules(), List.of(program));
Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values());
promptModuleProposal(proposal, FMT_NO_MODULES_PROPOSAL_RETRY.formatted(
trace.getDomainFile().getName(), program.getDomainFile().getName()));
}
private boolean isEnabledMapMissingProgramToCurrent(
DebuggerMissingProgramActionContext context) {
if (staticMappingService == null || traceManager == null || listingService == null) {
return false;
}
ProgramLocation loc = listingService.getCurrentLocation();
if (loc == null) {
return false;
}
if (!(loc.getProgram() instanceof TraceProgramView view)) {
return false;
}
Trace trace = context.getTrace();
if (view.getTrace() != trace) {
return false;
}
long snap = traceManager.getCurrentFor(trace).getSnap();
Address address = loc.getAddress();
return !trace.getModuleManager().getModulesAt(snap, address).isEmpty();
}
private void activatedMapMissingProgramToCurrent(DebuggerMissingProgramActionContext context) {
if (staticMappingService == null || traceManager == null || listingService == null) {
return;
}
Trace trace = context.getTrace();
long snap = traceManager.getCurrentFor(trace).getSnap();
Address address = listingService.getCurrentLocation().getAddress();
TraceModule module = trace.getModuleManager().getModulesAt(snap, address).iterator().next();
Program program = context.getProgram();
ModuleMapProposal proposal =
staticMappingService.proposeModuleMap(module, program);
Map<TraceModule, ModuleMapEntry> map = proposal.computeMap();
promptModuleProposal(map.values(), FMT_NO_MODULES_PROPOSAL_CURRENT.formatted(
module.getName(), program.getDomainFile().getName()));
}
private void activatedMapMissingProgramIdentically(
DebuggerMissingProgramActionContext context) {
if (staticMappingService == null) {
return;
}
Trace trace = context.getTrace();
long snap = traceManager == null ? 0 : traceManager.getCurrentFor(trace).getSnap();
try {
staticMappingService.addIdentityMapping(trace, context.getProgram(),
Lifespan.nowOn(snap), true);
}
catch (TraceConflictedMappingException e) {
Msg.showError(this, null, "Map Identically", e.getMessage());
}
}
private void toggledShowSectionsTable(ActionContext ignored) { private void toggledShowSectionsTable(ActionContext ignored) {
setShowSectionsTable(actionShowSectionsTable.isSelected()); setShowSectionsTable(actionShowSectionsTable.isSelected());
} }
@ -903,11 +1080,9 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
} }
} }
protected void promptModuleProposal(Collection<ModuleMapEntry> proposal) { protected void promptModuleProposal(Collection<ModuleMapEntry> proposal, String emptyMsg) {
if (proposal.isEmpty()) { if (proposal.isEmpty()) {
Msg.showInfo(this, getComponent(), "Map Modules", Msg.showInfo(this, getComponent(), "Map Modules", emptyMsg);
"Could not formulate a proposal for any selected module." +
" You may need to import and/or open the destination images first.");
return; return;
} }
Collection<ModuleMapEntry> adjusted = Collection<ModuleMapEntry> adjusted =
@ -926,7 +1101,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
Map<TraceModule, ModuleMapProposal> map = staticMappingService.proposeModuleMaps(modules, Map<TraceModule, ModuleMapProposal> map = staticMappingService.proposeModuleMaps(modules,
List.of(programManager.getAllOpenPrograms())); List.of(programManager.getAllOpenPrograms()));
Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values()); Collection<ModuleMapEntry> proposal = MapProposal.flatten(map.values());
promptModuleProposal(proposal); promptModuleProposal(proposal, NO_MODULES_PROPOSAL_SEL);
} }
protected void mapModuleTo(TraceModule module) { protected void mapModuleTo(TraceModule module) {
@ -939,7 +1114,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
} }
ModuleMapProposal proposal = staticMappingService.proposeModuleMap(module, program); ModuleMapProposal proposal = staticMappingService.proposeModuleMap(module, program);
Map<TraceModule, ModuleMapEntry> map = proposal.computeMap(); Map<TraceModule, ModuleMapEntry> map = proposal.computeMap();
promptModuleProposal(map.values()); promptModuleProposal(map.values(), NO_MODULES_PROPOSAL_SEL);
} }
protected void promptSectionProposal(Collection<SectionMapEntry> proposal) { protected void promptSectionProposal(Collection<SectionMapEntry> proposal) {
@ -1090,6 +1265,11 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
if (currentProgram == program) { if (currentProgram == program) {
currentProgram = null; currentProgram = null;
} }
cleanMissingProgramMessages(null, program);
}
public void traceClosed(Trace trace) {
cleanMissingProgramMessages(trace, null);
} }
protected void addNewTraceListener() { protected void addNewTraceListener() {
@ -1232,4 +1412,62 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
doSetFilterSectionsByModules(filterSectionsByModules); doSetFilterSectionsByModules(filterSectionsByModules);
doSetShowSectionsTable(showSectionsTable); doSetShowSectionsTable(showSectionsTable);
} }
protected boolean shouldKeepMessage(DebuggerMissingProgramActionContext ctx, Trace closedTrace,
Program closedProgram) {
Trace trace = ctx.getTrace();
if (trace == closedTrace) {
return false;
}
if (!traceManager.getOpenTraces().contains(trace)) {
return false;
}
Program program = ctx.getProgram();
if (program == closedProgram) {
return false;
}
if (programManager != null &&
!Arrays.asList(programManager.getAllOpenPrograms()).contains(program)) {
return false;
}
// Only do mapping probe on mapping changed events
if (closedTrace != null || closedProgram != null) {
return true;
}
TraceProgramView view = traceManager.getCurrentFor(trace).getView();
Address probe = ctx.getMappingProbeAddress();
ProgramLocation dyn = staticMappingService.getDynamicLocationFromStatic(view,
new ProgramLocation(program, probe));
if (dyn != null) {
return false;
}
return true;
}
protected void cleanMissingProgramMessages(Trace closedTrace, Program closedProgram) {
if (traceManager == null) {
return;
}
for (ActionContext ctx : consoleService.getActionContexts()) {
if (!(ctx instanceof DebuggerMissingProgramActionContext mpCtx)) {
continue;
}
if (!shouldKeepMessage(mpCtx, closedTrace, closedProgram)) {
consoleService.removeFromLog(mpCtx);
}
}
}
@AutoServiceConsumed
private void setStaticMappingService(DebuggerStaticMappingService staticMappingService) {
if (this.staticMappingService != null) {
this.staticMappingService.removeChangeListener(mappingChangeListener);
}
this.staticMappingService = staticMappingService;
if (this.staticMappingService != null) {
this.staticMappingService.addChangeListener(mappingChangeListener);
}
}
} }

View file

@ -47,13 +47,13 @@ import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin; import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.BoundAction; import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.BoundAction;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.LogRow; import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.LogRow;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.ProgramManager; import ghidra.app.services.ProgramManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.SwingExecutorService; import ghidra.async.SwingExecutorService;
import ghidra.debug.api.model.TraceRecorder; import ghidra.debug.api.model.TraceRecorder;
import ghidra.debug.api.modules.DebuggerMissingModuleActionContext;
import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.plugin.importer.ImporterPlugin; import ghidra.plugin.importer.ImporterPlugin;