mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-3887: Update Debugger course for Trace RMI.
This commit is contained in:
parent
190f1eaa1e
commit
a93a695e6a
79 changed files with 2235 additions and 1663 deletions
|
@ -26,7 +26,6 @@ import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
|||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -104,9 +103,9 @@ public interface AutoMapSpec extends ExtensionPoint {
|
|||
if (mappingService == null || programManager == null) {
|
||||
return;
|
||||
}
|
||||
BackgroundCommand cmd = new BackgroundCommand(getTaskTitle(), true, true, false) {
|
||||
BackgroundCommand<Trace> cmd = new BackgroundCommand<>(getTaskTitle(), true, true, false) {
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
public boolean applyTo(Trace trace, TaskMonitor monitor) {
|
||||
try {
|
||||
performMapping(mappingService, trace, programManager, monitor);
|
||||
return true;
|
||||
|
|
|
@ -799,7 +799,7 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid
|
|||
|
||||
protected void executeCapture(AddressRange range, Target target, TaskMonitor monitor)
|
||||
throws Exception {
|
||||
target.readMemory(new AddressSet(range), monitor);
|
||||
target.readMemoryAsync(new AddressSet(range), monitor).get();
|
||||
}
|
||||
|
||||
protected void executePlan(TaskMonitor monitor) throws Exception {
|
||||
|
|
|
@ -64,6 +64,11 @@ public interface DisplaysObjectValues {
|
|||
.collect(Collectors.joining(":"));
|
||||
}
|
||||
|
||||
default String getStringsDisplay(String[] strings) {
|
||||
return Stream.of(strings)
|
||||
.collect(Collectors.joining(":"));
|
||||
}
|
||||
|
||||
default String getPrimitiveValueDisplay(Object value) {
|
||||
assert !(value instanceof TraceObject);
|
||||
assert !(value instanceof TraceObjectValue);
|
||||
|
@ -89,6 +94,9 @@ public interface DisplaysObjectValues {
|
|||
if (value instanceof long[] longs) {
|
||||
return getLongsDisplay(longs);
|
||||
}
|
||||
if (value instanceof String[] strings) {
|
||||
return getStringsDisplay(strings);
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -42,13 +42,13 @@ public class DebuggerModuleMapProposalDialog
|
|||
implements EnumeratedTableColumn<ModuleMapTableColumns, ModuleMapEntry> {
|
||||
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
|
||||
MODULE_NAME("Module", String.class, e -> e.getModule().getName()),
|
||||
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getModule().getBase()),
|
||||
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()),
|
||||
CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()),
|
||||
PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null
|
||||
? e.getToProgram().getName()
|
||||
: e.getToProgram().getDomainFile().getName())),
|
||||
STATIC_BASE("Static Base", Address.class, e -> e.getToProgram().getImageBase()),
|
||||
SIZE("Size", Long.class, e -> e.getModuleRange().getLength()),
|
||||
STATIC_BASE("Static Base", Address.class, e -> e.getToRange().getMinAddress()),
|
||||
SIZE("Size", Long.class, e -> e.getFromRange().getLength()),
|
||||
MEMORIZE("Memorize", Boolean.class, ModuleMapEntry::isMemorize, ModuleMapEntry::setMemorize);
|
||||
|
||||
private final String header;
|
||||
|
|
|
@ -32,6 +32,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
|
|||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
ProgramOpenedPluginEvent.class,
|
||||
ProgramActivatedPluginEvent.class,
|
||||
ProgramLocationPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class,
|
||||
|
@ -65,7 +66,10 @@ public class DebuggerModulesPlugin extends AbstractDebuggerPlugin {
|
|||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
if (event instanceof ProgramActivatedPluginEvent ev) {
|
||||
if (event instanceof ProgramOpenedPluginEvent ev) {
|
||||
provider.programOpened(ev.getProgram());
|
||||
}
|
||||
else if (event instanceof ProgramActivatedPluginEvent ev) {
|
||||
provider.setProgram(ev.getActiveProgram());
|
||||
}
|
||||
else if (event instanceof ProgramLocationPluginEvent ev) {
|
||||
|
|
|
@ -1078,6 +1078,12 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
|
|||
actionMapSectionTo.getPopupMenuData().setMenuItemName(name);
|
||||
}
|
||||
|
||||
public void programOpened(Program program) {
|
||||
// TODO: Debounce this?
|
||||
cueAutoMap = true;
|
||||
doCuedAutoMap();
|
||||
}
|
||||
|
||||
public void programClosed(Program program) {
|
||||
if (currentProgram == program) {
|
||||
currentProgram = null;
|
||||
|
|
|
@ -614,7 +614,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
|
|||
pcodeTable.setTableHeader(null);
|
||||
pcodeTable.setBackground(COLOR_BACKGROUND);
|
||||
pcodeTable.setSelectionBackground(COLOR_BACKGROUND_CURSOR);
|
||||
pcodeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
pcodeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
pcodeTable.getSelectionModel().addListSelectionListener(evt -> {
|
||||
if (evt.getValueIsAdjusting()) {
|
||||
return;
|
||||
|
|
|
@ -106,6 +106,9 @@ public class UniqueRow {
|
|||
}
|
||||
|
||||
public RefType getRefType() {
|
||||
if (provider.pcodeTable.getSelectedRowCount() != 1) {
|
||||
return RefType.NONE;
|
||||
}
|
||||
int index = provider.pcodeTable.getSelectedRow();
|
||||
if (index == -1) {
|
||||
return RefType.NONE;
|
||||
|
|
|
@ -42,8 +42,8 @@ import ghidra.trace.database.DBTrace;
|
|||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.modules.TraceConflictedMappingException;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.util.*;
|
||||
|
@ -70,7 +70,8 @@ public class ProgramEmulationUtils {
|
|||
<attribute name='Modules' schema='ModuleContainer' />
|
||||
<attribute name='Threads' schema='ThreadContainer' />
|
||||
</schema>
|
||||
<schema name='BreakpointContainer' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<schema name='BreakpointContainer' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='NEVER'>
|
||||
<interface name='BreakpointSpecContainer' />
|
||||
<interface name='BreakpointLocationContainer' />
|
||||
<element schema='Breakpoint' />
|
||||
|
@ -494,6 +495,20 @@ public class ProgramEmulationUtils {
|
|||
throw new EmulatorOutOfMemoryException();
|
||||
}
|
||||
|
||||
protected static void createObjects(Trace trace) {
|
||||
TraceObjectManager om = trace.getObjectManager();
|
||||
om.createRootObject(EMU_SESSION_SCHEMA);
|
||||
|
||||
om.createObject(TraceObjectKeyPath.parse("Breakpoints"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
om.createObject(TraceObjectKeyPath.parse("Memory"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
om.createObject(TraceObjectKeyPath.parse("Modules"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
om.createObject(TraceObjectKeyPath.parse("Threads"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new trace with a single thread, ready for emulation of the given program
|
||||
*
|
||||
|
@ -510,7 +525,8 @@ public class ProgramEmulationUtils {
|
|||
try {
|
||||
trace = new DBTrace(getTraceName(program), program.getCompilerSpec(), consumer);
|
||||
try (Transaction tx = trace.openTransaction("Emulate")) {
|
||||
trace.getObjectManager().createRootObject(EMU_SESSION_SCHEMA);
|
||||
createObjects(trace);
|
||||
|
||||
TraceSnapshot initial =
|
||||
trace.getTimeManager().createSnapshot(EMULATION_STARTED_AT + pc);
|
||||
long snap = initial.getKey();
|
||||
|
|
|
@ -34,7 +34,6 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.ComparatorMath;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
|
@ -163,11 +162,17 @@ public enum DebuggerStaticMappingUtils {
|
|||
private Address min = null;
|
||||
private Address max = null;
|
||||
|
||||
public void consider(Address min, Address max) {
|
||||
this.min = this.min == null ? min : ComparatorMath.cmin(this.min, min);
|
||||
this.max = this.max == null ? max : ComparatorMath.cmax(this.max, max);
|
||||
}
|
||||
|
||||
public void consider(AddressRange range) {
|
||||
min = min == null ? range.getMinAddress()
|
||||
: ComparatorMath.cmin(min, range.getMinAddress());
|
||||
max = max == null ? range.getMaxAddress()
|
||||
: ComparatorMath.cmax(max, range.getMaxAddress());
|
||||
consider(range.getMinAddress(), range.getMaxAddress());
|
||||
}
|
||||
|
||||
public void consider(Address address) {
|
||||
consider(address, address);
|
||||
}
|
||||
|
||||
public Address getMin() {
|
||||
|
@ -181,6 +186,10 @@ public enum DebuggerStaticMappingUtils {
|
|||
public long getLength() {
|
||||
return max.subtract(min) + 1;
|
||||
}
|
||||
|
||||
public AddressRange getRange() {
|
||||
return new AddressRangeImpl(getMin(), getMax());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isReal(MemoryBlock block) {
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.service.modules;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.debug.api.modules.ModuleMapProposal;
|
||||
import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
|
||||
|
@ -27,6 +28,7 @@ import ghidra.trace.model.Lifespan;
|
|||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
public class DefaultModuleMapProposal
|
||||
extends AbstractMapProposal<TraceModule, Program, ModuleMapEntry>
|
||||
|
@ -81,20 +83,24 @@ public class DefaultModuleMapProposal
|
|||
* @param program the program image whose size to compute
|
||||
* @return the size
|
||||
*/
|
||||
public static long computeImageSize(Program program) {
|
||||
Address imageBase = program.getImageBase();
|
||||
long imageSize = 0;
|
||||
public static AddressRange computeImageRange(Program program) {
|
||||
Extrema extrema = new Extrema();
|
||||
// TODO: How to handle Harvard architectures?
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
if (!includeBlock(program, block)) {
|
||||
continue;
|
||||
}
|
||||
imageSize = Math.max(imageSize, block.getEnd().subtract(imageBase) + 1);
|
||||
// includeBlock checks address space is same as image base
|
||||
extrema.consider(block.getAddressRange());
|
||||
}
|
||||
return imageSize;
|
||||
if (program.getImageBase().getOffset() != 0) {
|
||||
extrema.consider(program.getImageBase());
|
||||
}
|
||||
return extrema.getRange();
|
||||
}
|
||||
|
||||
protected AddressRange moduleRange;
|
||||
protected AddressRange imageRange;
|
||||
protected boolean memorize = false;
|
||||
|
||||
/**
|
||||
|
@ -113,6 +119,7 @@ public class DefaultModuleMapProposal
|
|||
AddressRange moduleRange) {
|
||||
super(module.getTrace(), module, program, program);
|
||||
this.moduleRange = moduleRange;
|
||||
this.imageRange = quantize(computeImageRange(program));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,9 +132,18 @@ public class DefaultModuleMapProposal
|
|||
return getModule().getLifespan();
|
||||
}
|
||||
|
||||
private long getLength() {
|
||||
return MathUtilities.unsignedMin(moduleRange.getLength(), imageRange.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getFromRange() {
|
||||
return moduleRange;
|
||||
try {
|
||||
return new AddressRangeImpl(moduleRange.getMinAddress(), getLength());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -138,21 +154,13 @@ public class DefaultModuleMapProposal
|
|||
@Override
|
||||
public void setProgram(Program program) {
|
||||
setToObject(program, program);
|
||||
try {
|
||||
this.moduleRange = quantize(
|
||||
new AddressRangeImpl(getModule().getBase(), computeImageSize(program)));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
// This is terribly unlikely
|
||||
throw new IllegalArgumentException(
|
||||
"Specified program is too large for module's memory space");
|
||||
}
|
||||
this.imageRange = quantize(computeImageRange(program));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getToRange() {
|
||||
try {
|
||||
return new AddressRangeImpl(getToProgram().getImageBase(), moduleRange.getLength());
|
||||
return new AddressRangeImpl(imageRange.getMinAddress(), getLength());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
|
@ -174,10 +182,8 @@ public class DefaultModuleMapProposal
|
|||
|
||||
// indexed by region's offset from module base
|
||||
protected final NavigableMap<Long, ModuleRegionMatcher> matchers = new TreeMap<>();
|
||||
protected Address imageBase;
|
||||
protected Address moduleBase;
|
||||
protected long imageSize;
|
||||
protected AddressRange moduleRange; // TODO: This is now in the trace schema. Use it.
|
||||
protected AddressRange imageRange;
|
||||
protected AddressRange moduleRange;
|
||||
|
||||
protected DefaultModuleMapProposal(TraceModule module, Program program) {
|
||||
super(module.getTrace(), program);
|
||||
|
@ -196,8 +202,8 @@ public class DefaultModuleMapProposal
|
|||
}
|
||||
|
||||
private void processProgram() {
|
||||
imageBase = program.getImageBase();
|
||||
imageSize = DefaultModuleMapEntry.computeImageSize(program);
|
||||
imageRange = quantize(DefaultModuleMapEntry.computeImageRange(program));
|
||||
Address imageBase = imageRange.getMinAddress(); // not precisely, but good enough
|
||||
// TODO: How to handle Harvard architectures?
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
if (!DefaultModuleMapEntry.includeBlock(program, block)) {
|
||||
|
@ -211,13 +217,8 @@ public class DefaultModuleMapProposal
|
|||
* Must be called after processProgram, so that image size is known
|
||||
*/
|
||||
private void processModule() {
|
||||
moduleBase = module.getBase();
|
||||
try {
|
||||
moduleRange = quantize(new AddressRangeImpl(moduleBase, imageSize));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
return; // Just score it as having no matches?
|
||||
}
|
||||
moduleRange = quantize(module.getRange());
|
||||
Address moduleBase = moduleRange.getMinAddress();
|
||||
Lifespan lifespan = module.getLifespan();
|
||||
for (TraceMemoryRegion region : module.getTrace()
|
||||
.getMemoryManager()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue