mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-4636: Use Debug Console for initial mapping failure.
This commit is contained in:
parent
35434613b8
commit
1db04f8c71
8 changed files with 435 additions and 58 deletions
|
@ -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;
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,16 +43,14 @@ import ghidra.debug.api.tracermi.*;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.AutoConfigState.ConfigStateField;
|
import ghidra.framework.plugintool.AutoConfigState.ConfigStateField;
|
||||||
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;
|
||||||
|
@ -169,25 +167,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,
|
||||||
|
@ -641,6 +622,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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue