Merge remote-tracking branch 'origin/GP-1232_Dan_map1to1' into patch

This commit is contained in:
Ryan Kurtz 2021-08-24 08:39:25 -04:00
commit dc8c5f5a47
8 changed files with 161 additions and 13 deletions

View file

@ -96,6 +96,13 @@
selected modules to Ghidra data types in the trace. These are often the same types that Ghidra
imports from the static image, anyway.</P>
<H3><A name="map_identically"></A>Map Identically</H3>
<P>This action is available when both a trace and a program are opened. It maps the current
trace to the current program using identical addresses. This action ignores the module list. It
is a suitable mapping when the current program is loaded in the trace <EM>without
relocation</EM>. It is also a fallback worth trying in the absence of a module list.</P>
<H3><A name="map_modules"></A>Map Modules</H3>
<P>This action is available from the modules' or sections' pop-up menu. It searches the tool's

View file

@ -28,9 +28,9 @@
automation, e.g., the <A href="help/topics/DebuggerBots/DebuggerBots.html#map_modules">Map
Modules</A> bot, or by higher-level user actions, e.g., the <A href=
"help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html#map_modules">Map Modules</A>
action. This under-the-hood static mapping window displays the mappings table, allowing users or
developers to diagnose image mapping issues and manually add mappings, regardless of reported
modules and/or sections. For most users, there is no reason to access this window.</P>
action. This under-the-hood static mapping window displays the mappings table, allowing users
or developers to diagnose image mapping issues and manually add mappings, regardless of
reported modules and/or sections. For most users, there is no reason to access this window.</P>
<H2>Table Columns</H2>

View file

@ -50,10 +50,10 @@
<H2>Navigating Threads</H2>
<P>Selecting a thread in the table will navigate to (or "activate" or "focus")
that thread. Windows which are sensitive to the current thread will update. Notably, the <A
href="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html">Registers</A> window
will display the activated thread's register values. <A href=
<P>Selecting a thread in the table will navigate to (or "activate" or "focus") that thread.
Windows which are sensitive to the current thread will update. Notably, the <A href=
"help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html">Registers</A> window will
display the activated thread's register values. <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Listing</A> windows with
configured location tracking will re-compute that location with the thread's context and
navigate to it. The thread timeline plots all recorded threads. Threads which are alive will
@ -77,16 +77,17 @@
The state always reflects the present, or latest known, state.</LI>
<LI>Comment - a user-modifiable comment about the thread.</LI>
<LI>Plot - a graphical representation of the thread's lifespan. Unlike other column headers, clicking and dragging
in this one will navigate through time. To rearrange this column, hold SHIFT while dragging.</LI>
<LI>Plot - a graphical representation of the thread's lifespan. Unlike other column headers,
clicking and dragging in this one will navigate through time. To rearrange this column, hold
SHIFT while dragging.</LI>
</UL>
<H2>Navigating Time</H2>
<P>The user can navigate through time within the current trace by using the caret in the plot
column header. There are also actions for "stepping the trace" forward and backward. See the
<A href="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html">Time</A> window for a way to
column header. There are also actions for "stepping the trace" forward and backward. See the <A
href="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html">Time</A> window for a way to
display and navigate to specific events in the trace's timeline. Note that stepping away from
the present will prevent most windows from interacting with the live target. While some
components and scripts may interact with the target and record things "into the present," such

View file

@ -146,6 +146,7 @@ public interface DebuggerResources {
//ResourceManager.loadImage("images/capture-memory.png");
// TODO: Draw an icon
ImageIcon ICON_MAP_IDENTICALLY = ResourceManager.loadImage("images/doubleArrow.png");
ImageIcon ICON_MAP_MODULES = ResourceManager.loadImage("images/modules.png");
ImageIcon ICON_MAP_SECTIONS = ICON_MAP_MODULES; // TODO
ImageIcon ICON_BLOCK = ICON_MAP_SECTIONS; // TODO
@ -1209,6 +1210,23 @@ public interface DebuggerResources {
}
}
interface MapIdenticallyAction {
String NAME = "Map Identically";
String DESCRIPTION =
"Map the current trace to the current program using identical addresses";
Icon ICON = ICON_MAP_IDENTICALLY;
String GROUP = GROUP_MAPPING;
String HELP_ANCHOR = "map_identically";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName).description(DESCRIPTION)
.toolBarIcon(ICON)
.toolBarGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface MapModulesAction {
String NAME = "Map Modules";
String DESCRIPTION = "Map selected modules to program images";

View file

@ -539,6 +539,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
private Program currentProgram;
private ProgramLocation currentLocation;
DockingAction actionMapIdentically;
DockingAction actionMapModules;
DockingAction actionMapModuleTo;
DockingAction actionMapSections;
@ -747,6 +748,10 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
}
protected void createActions() {
actionMapIdentically = MapIdenticallyAction.builder(plugin)
.enabledWhen(ctx -> currentProgram != null && currentTrace != null)
.onAction(this::activatedMapIdentically)
.buildAndInstallLocal(this);
actionMapModules = MapModulesAction.builder(plugin)
.enabledWhen(this::isContextNonEmpty)
.popupWhen(this::isContextNonEmpty)
@ -822,6 +827,14 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
return sel.stream().map(TraceSection::getModule).distinct().count() == 1;
}
private void activatedMapIdentically(ActionContext ignored) {
if (currentProgram == null || currentTrace == null) {
return;
}
staticMappingService.addIdentityMapping(currentTrace, currentProgram,
Range.atLeast(traceManager.getCurrentSnap()), true);
}
private void activatedMapModules(ActionContext ignored) {
Set<TraceModule> sel = getSelectedModules(myActionContext);
if (sel == null || sel.isEmpty()) {

View file

@ -980,6 +980,69 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
manager.add(range, fromLifespan, toURL, toAddress.toString(true));
}
static protected AddressRange clippedRange(Trace trace, String spaceName, long min,
long max) {
AddressSpace space = trace.getBaseAddressFactory().getAddressSpace(spaceName);
if (space == null) {
return null;
}
Address spaceMax = space.getMaxAddress();
if (Long.compareUnsigned(min, spaceMax.getOffset()) > 0) {
return null;
}
if (Long.compareUnsigned(max, spaceMax.getOffset()) > 0) {
return new AddressRangeImpl(space.getAddress(min), spaceMax);
}
return new AddressRangeImpl(space.getAddress(min), space.getAddress(max));
}
@Override
public void addIdentityMapping(Trace from, Program toProgram, Range<Long> lifespan,
boolean truncateExisting) {
try (UndoableTransaction tid =
UndoableTransaction.start(from, "Add identity mappings", false)) {
doAddIdentityMapping(from, toProgram, lifespan, truncateExisting);
tid.commit();
}
}
protected void doAddIdentityMapping(Trace from, Program toProgram, Range<Long> lifespan,
boolean truncateExisting) {
Map<String, Address> mins = new HashMap<>();
Map<String, Address> maxs = new HashMap<>();
for (AddressRange range : toProgram.getMemory().getAddressRanges()) {
mins.compute(range.getAddressSpace().getName(), (n, min) -> {
Address can = range.getMinAddress();
if (min == null || can.compareTo(min) < 0) {
return can;
}
return min;
});
maxs.compute(range.getAddressSpace().getName(), (n, max) -> {
Address can = range.getMaxAddress();
if (max == null || can.compareTo(max) > 0) {
return can;
}
return max;
});
}
for (String name : mins.keySet()) {
AddressRange range = clippedRange(from, name, mins.get(name).getOffset(),
maxs.get(name).getOffset());
if (range == null) {
continue;
}
try {
addMapping(new DefaultTraceLocation(from, null, lifespan, range.getMinAddress()),
new ProgramLocation(toProgram, mins.get(name)), range.getLength(),
truncateExisting);
}
catch (TraceConflictedMappingException e) {
Msg.error(this, "Could not add identity mapping " + range + ": " + e.getMessage());
}
}
}
@Override
public void addModuleMapping(TraceModule from, long length, Program toProgram,
boolean truncateExisting) throws TraceConflictedMappingException {

View file

@ -532,7 +532,6 @@ public interface DebuggerStaticMappingService {
* Note if the trace is backed by a Ghidra database, the caller must already have started a
* transaction on the relevant domain object.
*
*
* @param from the source trace location, including lifespan
* @param to the destination program location
* @param length the length of the mapped region, where 0 indicates {@code 1 << 64}.
@ -543,6 +542,18 @@ public interface DebuggerStaticMappingService {
void addMapping(TraceLocation from, ProgramLocation to, long length, boolean truncateExisting)
throws TraceConflictedMappingException;
/**
* Add a static mapping from the given trace to the given program, using identical addresses
*
* @param from the source trace
* @param toProgram the destination program
* @param lifespan the lifespan of the mapping
* @param truncateExisting true to delete or truncate the lifespan of overlapping entries. If
* false, overlapping entries are omitted.
*/
void addIdentityMapping(Trace from, Program toProgram, Range<Long> lifespan,
boolean truncateExisting);
/**
* Add a static mapping (relocation) from the given module to the given program
*

View file

@ -327,6 +327,41 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertProviderEmpty();
}
@Test
public void testActionMapIdentically() throws Exception {
assertFalse(modulesProvider.actionMapIdentically.isEnabled());
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
intoProject(program);
// No modules necessary
traceManager.activateTrace(tb.trace);
waitForSwing();
assertTrue(modulesProvider.actionMapIdentically.isEnabled());
// Need some substance in the program
try (UndoableTransaction tid = UndoableTransaction.start(program, "Populate", true)) {
addBlock();
}
waitForDomainObject(program);
performAction(modulesProvider.actionMapIdentically);
waitForDomainObject(tb.trace);
Collection<? extends TraceStaticMapping> mappings =
tb.trace.getStaticMappingManager().getAllEntries();
assertEquals(1, mappings.size());
TraceStaticMapping sm = mappings.iterator().next();
assertEquals(Range.atLeast(0L), sm.getLifespan());
assertEquals("ram:00400000", sm.getStaticAddress());
assertEquals(0x1000, sm.getLength()); // Block is 0x1000 in length
assertEquals(tb.addr(0x00400000), sm.getMinTraceAddress());
}
@Test
public void testActionMapModules() throws Exception {
assertFalse(modulesProvider.actionMapModules.isEnabled());