mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-10-23 21:50:49 +00:00
Merge remote-tracking branch 'origin/GP-2551_Dan_RegistersViewerNewConvention-REBASED-1'
This commit is contained in:
commit
45165ea167
|
@ -65,7 +65,7 @@ public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
|
|||
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
CONTAINER_ATTRIBUTE_NAME, registers, //
|
||||
LENGTH_ATTRIBUTE_NAME, bitLength, //
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, bitLength, //
|
||||
DISPLAY_ATTRIBUTE_NAME, "[" + register.getName() + "]" //
|
||||
), "Initialized");
|
||||
}
|
||||
|
|
|
@ -52,13 +52,14 @@ public class FridaModelTargetRegisterImpl
|
|||
|
||||
changeAttributes(List.of(), Map.of( //
|
||||
CONTAINER_ATTRIBUTE_NAME, registers, //
|
||||
LENGTH_ATTRIBUTE_NAME, getBitLength(), //
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, getBitLength(), //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDescription(0), //
|
||||
VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, //
|
||||
MODIFIED_ATTRIBUTE_NAME, false //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(int level) {
|
||||
return getName() + " : " + getValue();
|
||||
}
|
||||
|
@ -85,6 +86,7 @@ public class FridaModelTargetRegisterImpl
|
|||
return (FridaValue) getModelObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
String oldValue = value;
|
||||
value = getValue();
|
||||
|
@ -113,6 +115,7 @@ public class FridaModelTargetRegisterImpl
|
|||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplay() {
|
||||
return getValue() == null ? getName() : getName() + " : " + getValue();
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class GdbModelTargetRegister
|
|||
|
||||
changeAttributes(List.of(), Map.of( //
|
||||
CONTAINER_ATTRIBUTE_NAME, registers, //
|
||||
LENGTH_ATTRIBUTE_NAME, bitLength, //
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, bitLength, //
|
||||
DISPLAY_ATTRIBUTE_NAME, getName() //
|
||||
), "Initialized");
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class GdbModelTargetStackFrameRegister
|
|||
|
||||
changeAttributes(List.of(), Map.of( //
|
||||
CONTAINER_ATTRIBUTE_NAME, registers, //
|
||||
LENGTH_ATTRIBUTE_NAME, bitLength, //
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, bitLength, //
|
||||
DISPLAY_ATTRIBUTE_NAME, getName(), //
|
||||
MODIFIED_ATTRIBUTE_NAME, false //
|
||||
), "Initialized");
|
||||
|
|
|
@ -53,7 +53,7 @@ public class LldbModelTargetStackFrameRegisterImpl
|
|||
|
||||
changeAttributes(List.of(), Map.of( //
|
||||
CONTAINER_ATTRIBUTE_NAME, bank.getContainer(), //
|
||||
LENGTH_ATTRIBUTE_NAME, getBitLength(), //
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, getBitLength(), //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDescription(0), //
|
||||
VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, //
|
||||
MODIFIED_ATTRIBUTE_NAME, false //
|
||||
|
|
|
@ -50,7 +50,7 @@ public class JdiModelTargetRegister extends JdiModelTargetObjectImpl implements
|
|||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
CONTAINER_ATTRIBUTE_NAME, parent, //
|
||||
LENGTH_ATTRIBUTE_NAME, Long.SIZE //
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, Long.SIZE //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ public class DebuggerCoordinates {
|
|||
|
||||
private Long viewSnap;
|
||||
private DefaultTraceTimeViewport viewport;
|
||||
private TraceObject registerContainer;
|
||||
|
||||
DebuggerCoordinates(Trace trace, TracePlatform platform, TraceRecorder recorder,
|
||||
TraceThread thread, TraceProgramView view, TraceSchedule time, Integer frame,
|
||||
|
@ -483,7 +484,7 @@ public class DebuggerCoordinates {
|
|||
}
|
||||
newTrace = trace;
|
||||
}
|
||||
TracePlatform newPlatform = resolvePlatform(newTrace);
|
||||
TracePlatform newPlatform = platform != null ? platform : resolvePlatform(newTrace);
|
||||
TraceThread newThread = resolveThread(newObject);
|
||||
Integer newFrame = resolveFrame(newObject);
|
||||
|
||||
|
@ -553,6 +554,13 @@ public class DebuggerCoordinates {
|
|||
return object;
|
||||
}
|
||||
|
||||
public TraceObject getRegisterContainer() {
|
||||
if (registerContainer != null) {
|
||||
return registerContainer;
|
||||
}
|
||||
return registerContainer = object.queryRegisterContainer(getFrame());
|
||||
}
|
||||
|
||||
public synchronized long getViewSnap() {
|
||||
if (viewSnap != null) {
|
||||
return viewSnap;
|
||||
|
|
|
@ -86,7 +86,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
|||
.map(TargetObject::invalidateCaches)
|
||||
.toArray(CompletableFuture[]::new);
|
||||
return CompletableFuture.allOf(requests).thenCompose(_r -> {
|
||||
return recorder.readMemoryBlocks(sel, monitor, false);
|
||||
return recorder.readMemoryBlocks(sel, monitor);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -68,6 +68,6 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
|||
return AsyncUtils.NIL;
|
||||
}
|
||||
|
||||
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false);
|
||||
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,6 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
|
|||
return AsyncUtils.NIL;
|
||||
}
|
||||
|
||||
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false);
|
||||
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -812,8 +812,8 @@ public class DebuggerCopyIntoProgramDialog extends DialogComponentProvider {
|
|||
throws Exception {
|
||||
synchronized (this) {
|
||||
monitor.checkCanceled();
|
||||
CompletableFuture<NavigableMap<Address, byte[]>> recCapture =
|
||||
recorder.readMemoryBlocks(new AddressSet(range), monitor, false);
|
||||
CompletableFuture<Void> recCapture =
|
||||
recorder.readMemoryBlocks(new AddressSet(range), monitor);
|
||||
this.captureTask = recCapture.thenCompose(__ -> {
|
||||
return recorder.getTarget().getModel().flushEvents();
|
||||
}).thenCompose(__ -> {
|
||||
|
|
|
@ -980,7 +980,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
|
|||
doLoadPcodeFrameFromEmulator(emu);
|
||||
return;
|
||||
}
|
||||
emulationService.backgroundEmulate(trace, time).thenAcceptAsync(__ -> {
|
||||
emulationService.backgroundEmulate(current.getPlatform(), time).thenAcceptAsync(__ -> {
|
||||
clear();
|
||||
if (current != this.current) {
|
||||
return;
|
||||
|
|
|
@ -70,10 +70,11 @@ import ghidra.program.model.listing.Data;
|
|||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.*;
|
||||
import ghidra.util.*;
|
||||
|
@ -223,8 +224,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
|
||||
if (!Objects.equals(a.getTrace(), b.getTrace())) {
|
||||
return false;
|
||||
if (!Objects.equals(a.getPlatform(), b.getPlatform())) {
|
||||
return false; // subsumes trace
|
||||
}
|
||||
if (!Objects.equals(a.getRecorder(), b.getRecorder())) {
|
||||
return false; // For live read/writes
|
||||
|
@ -254,11 +255,23 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
listenFor(TraceThreadChangeType.LIFESPAN_CHANGED, this::threadDestroyed);
|
||||
}
|
||||
|
||||
private boolean isVisibleObjectsMode(AddressSpace space) {
|
||||
TraceObject container = current.getRegisterContainer();
|
||||
return container != null &&
|
||||
container.getCanonicalPath().toString().equals(space.getName());
|
||||
}
|
||||
|
||||
private boolean isVisible(TraceAddressSpace space) {
|
||||
TraceThread curThread = current.getThread();
|
||||
if (curThread == null) {
|
||||
return false;
|
||||
}
|
||||
if (space.getAddressSpace().isOverlaySpace()) {
|
||||
return isVisibleObjectsMode(space.getAddressSpace());
|
||||
}
|
||||
if (!space.getAddressSpace().isRegisterSpace()) {
|
||||
return true; // Memory-mapped, visible no matter the active thread
|
||||
}
|
||||
if (space.getThread() != curThread) {
|
||||
return false;
|
||||
}
|
||||
|
@ -272,6 +285,12 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
if (!isVisible(space)) {
|
||||
return false;
|
||||
}
|
||||
if (space.getAddressSpace().isMemorySpace()) {
|
||||
return current.getPlatform()
|
||||
.getLanguage()
|
||||
.getRegisterAddresses()
|
||||
.intersects(range.getX1(), range.getX2());
|
||||
}
|
||||
TraceProgramView view = current.getView();
|
||||
if (view == null || !view.getViewport().containsAnyUpper(range.getLifespan())) {
|
||||
return false;
|
||||
|
@ -439,7 +458,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
private AsyncLazyValue<Void> readTheseCoords =
|
||||
new AsyncLazyValue<>(this::readRegistersIfLiveAndAccessible); /* "read" past tense */
|
||||
private Trace currentTrace; // Copy for transition
|
||||
private TraceRecorder currentRecorder; // Copy of transition
|
||||
private TraceRecorder currentRecorder; // Copy for transition
|
||||
|
||||
@AutoServiceConsumed
|
||||
private DebuggerModelService modelService;
|
||||
|
@ -498,7 +517,6 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
|
||||
DebuggerRegisterActionContext myActionContext;
|
||||
AddressSetView viewKnown;
|
||||
AddressSetView catalog;
|
||||
|
||||
protected DebuggerRegistersProvider(final DebuggerRegistersPlugin plugin,
|
||||
Map<LanguageCompilerSpecPair, LinkedHashSet<Register>> selectionByCSpec,
|
||||
|
@ -691,14 +709,14 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
private void selectRegistersActivated() {
|
||||
TraceThread curThread = current.getThread();
|
||||
if (curThread == null) {
|
||||
TracePlatform curPlatform = current.getPlatform();
|
||||
if (current.getThread() == null) {
|
||||
return;
|
||||
}
|
||||
availableRegsDialog.setLanguage(curThread.getTrace().getBaseLanguage());
|
||||
Set<Register> viewKnown = computeDefaultRegisterSelection(curThread);
|
||||
availableRegsDialog.setLanguage(curPlatform.getLanguage());
|
||||
Set<Register> viewKnown = computeDefaultRegisterSelection(curPlatform);
|
||||
availableRegsDialog.setKnown(viewKnown);
|
||||
Set<Register> selection = getSelectionFor(curThread);
|
||||
Set<Register> selection = getSelectionFor(curPlatform);
|
||||
// NOTE: Modifies selection in place
|
||||
availableRegsDialog.setSelection(selection);
|
||||
tool.showDialog(availableRegsDialog);
|
||||
|
@ -777,20 +795,6 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
removeOldTraceListener();
|
||||
this.currentTrace = trace;
|
||||
addNewTraceListener();
|
||||
|
||||
catalogRegisterAddresses();
|
||||
}
|
||||
|
||||
private void catalogRegisterAddresses() {
|
||||
this.catalog = null;
|
||||
if (currentTrace == null) {
|
||||
return;
|
||||
}
|
||||
AddressSet catalog = new AddressSet();
|
||||
for (Register reg : currentTrace.getBaseLanguage().getRegisters()) {
|
||||
catalog.add(TraceRegisterUtils.rangeForRegister(reg));
|
||||
}
|
||||
this.catalog = catalog;
|
||||
}
|
||||
|
||||
private void removeOldRecorderListener() {
|
||||
|
@ -830,6 +834,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
doSetRecorder(current.getRecorder());
|
||||
updateSubTitle();
|
||||
|
||||
prepareRegisterSpace();
|
||||
recomputeViewKnown();
|
||||
loadRegistersAndValues();
|
||||
contextChanged();
|
||||
|
@ -861,7 +866,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
if (regs == null) {
|
||||
return BigInteger.ZERO;
|
||||
}
|
||||
return regs.getViewValue(current.getViewSnap(), register).getUnsignedValue();
|
||||
return regs.getViewValue(current.getPlatform(), current.getViewSnap(), register)
|
||||
.getUnsignedValue();
|
||||
}
|
||||
|
||||
void writeRegisterValue(Register register, BigInteger value) {
|
||||
|
@ -902,8 +908,9 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
|
||||
private RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv) {
|
||||
TraceMemorySpace regs = getRegisterMemorySpace(false);
|
||||
TracePlatform platform = current.getPlatform();
|
||||
long snap = current.getViewSnap();
|
||||
return TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, snap, regs, true);
|
||||
return TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, platform, snap, regs, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -916,9 +923,11 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
UndoableTransaction.start(current.getTrace(), "Edit Register Type")) {
|
||||
TraceCodeSpace space = getRegisterMemorySpace(true).getCodeSpace(true);
|
||||
long snap = current.getViewSnap();
|
||||
space.definedUnits().clear(Range.closed(snap, snap), register, TaskMonitor.DUMMY);
|
||||
TracePlatform platform = current.getPlatform();
|
||||
space.definedUnits()
|
||||
.clear(platform, Range.closed(snap, snap), register, TaskMonitor.DUMMY);
|
||||
if (dataType != null) {
|
||||
space.definedData().create(Range.atLeast(snap), register, dataType);
|
||||
space.definedData().create(platform, Range.atLeast(snap), register, dataType);
|
||||
}
|
||||
}
|
||||
catch (CodeUnitInsertionException | CancelledException e) {
|
||||
|
@ -931,8 +940,9 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
if (space == null) {
|
||||
return null;
|
||||
}
|
||||
TracePlatform platform = current.getPlatform();
|
||||
long snap = current.getViewSnap();
|
||||
return space.definedData().getForRegister(snap, register);
|
||||
return space.definedData().getForRegister(platform, snap, register);
|
||||
}
|
||||
|
||||
DataType getRegisterDataType(Register register) {
|
||||
|
@ -980,27 +990,64 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
return TraceRegisterUtils.getValueRepresentationHackPointer(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the register space exists and has been populated from register object values.
|
||||
*
|
||||
* <p>
|
||||
* TODO: I wish this were not necessary. Maybe I should create the space when register object
|
||||
* values are populated.
|
||||
*/
|
||||
void prepareRegisterSpace() {
|
||||
if (current.getThread() != null &&
|
||||
current.getTrace().getObjectManager().getRootSchema() != null) {
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(current.getTrace(), "Create/initialize register space")) {
|
||||
getRegisterMemorySpace(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recomputeViewKnown() {
|
||||
if (catalog == null) {
|
||||
TracePlatform platform = current.getPlatform();
|
||||
if (platform == null) {
|
||||
viewKnown = null;
|
||||
return;
|
||||
}
|
||||
TraceMemorySpace regs = getRegisterMemorySpace(false);
|
||||
TraceProgramView view = current.getView();
|
||||
if (regs == null || view == null) {
|
||||
if (view == null) {
|
||||
viewKnown = null;
|
||||
return;
|
||||
}
|
||||
viewKnown = new AddressSet(view.getViewport()
|
||||
.unionedAddresses(snap -> regs.getAddressesWithState(snap, catalog,
|
||||
state -> state == TraceMemoryState.KNOWN)));
|
||||
TraceMemoryManager mem = current.getTrace().getMemoryManager();
|
||||
AddressSetView viewKnownMem = view.getViewport()
|
||||
.unionedAddresses(snap -> mem.getAddressesWithState(snap,
|
||||
platform.mapGuestToHost(platform.getLanguage().getRegisterAddresses()),
|
||||
state -> state == TraceMemoryState.KNOWN));
|
||||
TraceMemorySpace regs = getRegisterMemorySpace(false);
|
||||
if (regs == null) {
|
||||
viewKnown = new AddressSet(viewKnownMem);
|
||||
return;
|
||||
}
|
||||
AddressSetView hostRegs =
|
||||
platform.mapGuestToHost(platform.getLanguage().getRegisterAddresses());
|
||||
AddressSetView overlayRegs =
|
||||
TraceRegisterUtils.getOverlaySet(regs.getAddressSpace(), hostRegs);
|
||||
AddressSetView viewKnownRegs = view.getViewport()
|
||||
.unionedAddresses(snap -> regs.getAddressesWithState(snap, overlayRegs,
|
||||
state -> state == TraceMemoryState.KNOWN));
|
||||
viewKnown = viewKnownRegs.union(viewKnownMem);
|
||||
}
|
||||
|
||||
boolean isRegisterKnown(Register register) {
|
||||
if (viewKnown == null) {
|
||||
return false;
|
||||
}
|
||||
AddressRange range = TraceRegisterUtils.rangeForRegister(register);
|
||||
TraceMemorySpace regs = getRegisterMemorySpace(false);
|
||||
if (regs == null && register.getAddressSpace().isRegisterSpace()) {
|
||||
return false;
|
||||
}
|
||||
AddressRange range =
|
||||
current.getPlatform().getConventionalRegisterRange(regs.getAddressSpace(), register);
|
||||
return viewKnown.contains(range.getMinAddress(), range.getMaxAddress());
|
||||
}
|
||||
|
||||
|
@ -1008,7 +1055,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
if (previous.getThread() == null || current.getThread() == null) {
|
||||
return false;
|
||||
}
|
||||
if (previous.getTrace().getBaseLanguage() != current.getTrace().getBaseLanguage()) {
|
||||
if (previous.getPlatform().getLanguage() != current.getPlatform().getLanguage()) {
|
||||
return false;
|
||||
}
|
||||
if (!isRegisterKnown(register)) {
|
||||
|
@ -1019,8 +1066,10 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
if (prevSpace == null) {
|
||||
return false;
|
||||
}
|
||||
RegisterValue curRegVal = curSpace.getViewValue(current.getViewSnap(), register);
|
||||
RegisterValue prevRegVal = prevSpace.getViewValue(previous.getViewSnap(), register);
|
||||
RegisterValue curRegVal =
|
||||
curSpace.getViewValue(current.getPlatform(), current.getViewSnap(), register);
|
||||
RegisterValue prevRegVal =
|
||||
prevSpace.getViewValue(current.getPlatform(), previous.getViewSnap(), register);
|
||||
return !Objects.equals(curRegVal, prevRegVal);
|
||||
}
|
||||
|
||||
|
@ -1031,8 +1080,10 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
/**
|
||||
* Gather general registers, the program counter, and the stack pointer
|
||||
*
|
||||
* <p>
|
||||
* This excludes the context register
|
||||
*
|
||||
* <p>
|
||||
* TODO: Several pspec files need adjustment to clean up "common registers"
|
||||
*
|
||||
* @param cSpec the compiler spec
|
||||
|
@ -1061,49 +1112,17 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
return result;
|
||||
}
|
||||
|
||||
public LinkedHashSet<Register> computeDefaultRegisterSelection(TraceThread thread) {
|
||||
return collectCommonRegisters(thread.getTrace().getBaseCompilerSpec());
|
||||
public LinkedHashSet<Register> computeDefaultRegisterSelection(TracePlatform platform) {
|
||||
return collectCommonRegisters(platform.getCompilerSpec());
|
||||
}
|
||||
|
||||
public LinkedHashSet<Register> computeDefaultRegisterFavorites(TraceThread thread) {
|
||||
public LinkedHashSet<Register> computeDefaultRegisterFavorites(TracePlatform platform) {
|
||||
LinkedHashSet<Register> favorites = new LinkedHashSet<>();
|
||||
CompilerSpec cSpec = thread.getTrace().getBaseCompilerSpec();
|
||||
favorites.add(cSpec.getLanguage().getProgramCounter());
|
||||
favorites.add(cSpec.getStackPointer());
|
||||
favorites.add(platform.getLanguage().getProgramCounter());
|
||||
favorites.add(platform.getCompilerSpec().getStackPointer());
|
||||
return favorites;
|
||||
}
|
||||
|
||||
public LinkedHashSet<Register> computeDefaultRegistersOld(TraceThread thread) {
|
||||
LinkedHashSet<Register> viewKnown = new LinkedHashSet<>();
|
||||
/**
|
||||
* NOTE: It is rare that this includes registers outside of those common to the view and
|
||||
* target, but in case the user has manually populated such registers, this will ensure they
|
||||
* are visible in the UI.
|
||||
*
|
||||
* Also, in case the current thread is not live, we want the DB values to appear.
|
||||
*/
|
||||
viewKnown.addAll(collectBaseRegistersWithKnownValues(thread));
|
||||
Trace trace = thread.getTrace();
|
||||
TraceRecorder recorder = modelService.getRecorder(trace);
|
||||
if (recorder == null) {
|
||||
viewKnown.addAll(collectCommonRegisters(trace.getBaseCompilerSpec()));
|
||||
return viewKnown;
|
||||
}
|
||||
TargetThread targetThread = recorder.getTargetThread(thread);
|
||||
if (targetThread == null || !recorder.isRegisterBankAccessible(thread, 0)) {
|
||||
return viewKnown;
|
||||
}
|
||||
DebuggerRegisterMapper regMapper = recorder.getRegisterMapper(thread);
|
||||
if (regMapper == null) {
|
||||
return viewKnown;
|
||||
}
|
||||
for (Register onTarget : regMapper.getRegistersOnTarget()) {
|
||||
viewKnown.add(onTarget);
|
||||
viewKnown.addAll(onTarget.getChildRegisters());
|
||||
}
|
||||
return viewKnown;
|
||||
}
|
||||
|
||||
protected static TraceMemorySpace getRegisterMemorySpace(DebuggerCoordinates coords,
|
||||
boolean createIfAbsent) {
|
||||
TraceThread thread = coords.getThread();
|
||||
|
@ -1163,33 +1182,29 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
return result;
|
||||
}
|
||||
|
||||
protected LanguageCompilerSpecPair getLangCSpecPair(Trace trace) {
|
||||
return new LanguageCompilerSpecPair(trace.getBaseLanguage().getLanguageID(),
|
||||
trace.getBaseCompilerSpec().getCompilerSpecID());
|
||||
protected static LanguageCompilerSpecPair getLangCSpecPair(TracePlatform platform) {
|
||||
return new LanguageCompilerSpecPair(platform.getLanguage().getLanguageID(),
|
||||
platform.getCompilerSpec().getCompilerSpecID());
|
||||
}
|
||||
|
||||
protected LanguageCompilerSpecPair getLangCSpecPair(TraceThread thread) {
|
||||
return getLangCSpecPair(thread.getTrace());
|
||||
}
|
||||
|
||||
protected Set<Register> getSelectionFor(TraceThread thread) {
|
||||
protected Set<Register> getSelectionFor(TracePlatform platform) {
|
||||
synchronized (selectionByCSpec) {
|
||||
LanguageCompilerSpecPair lcsp = getLangCSpecPair(thread);
|
||||
LanguageCompilerSpecPair lcsp = getLangCSpecPair(platform);
|
||||
return selectionByCSpec.computeIfAbsent(lcsp,
|
||||
__ -> computeDefaultRegisterSelection(thread));
|
||||
__ -> computeDefaultRegisterSelection(platform));
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<Register> getFavoritesFor(TraceThread thread) {
|
||||
protected Set<Register> getFavoritesFor(TracePlatform platform) {
|
||||
synchronized (favoritesByCSpec) {
|
||||
LanguageCompilerSpecPair lcsp = getLangCSpecPair(thread);
|
||||
LanguageCompilerSpecPair lcsp = getLangCSpecPair(platform);
|
||||
return favoritesByCSpec.computeIfAbsent(lcsp,
|
||||
__ -> computeDefaultRegisterFavorites(thread));
|
||||
__ -> computeDefaultRegisterFavorites(platform));
|
||||
}
|
||||
}
|
||||
|
||||
protected void setFavorite(Register register, boolean favorite) {
|
||||
Set<Register> favorites = getFavoritesFor(current.getThread());
|
||||
Set<Register> favorites = getFavoritesFor(current.getPlatform());
|
||||
if (favorite) {
|
||||
favorites.add(register);
|
||||
}
|
||||
|
@ -1199,13 +1214,13 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
public boolean isFavorite(Register register) {
|
||||
Set<Register> favorites = getFavoritesFor(current.getThread());
|
||||
Set<Register> favorites = getFavoritesFor(current.getPlatform());
|
||||
return favorites.contains(register);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> setSelectedRegistersAndLoad(
|
||||
Collection<Register> selectedRegisters) {
|
||||
Set<Register> selection = getSelectionFor(current.getThread());
|
||||
Set<Register> selection = getSelectionFor(current.getPlatform());
|
||||
selection.clear();
|
||||
selection.addAll(new TreeSet<>(selectedRegisters));
|
||||
return loadRegistersAndValues();
|
||||
|
@ -1226,7 +1241,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
protected void displaySelectedRegisters(Set<Register> selected) {
|
||||
List<Register> regs = currentTrace.getBaseLanguage().getRegisters();
|
||||
List<Register> regs = current.getPlatform().getLanguage().getRegisters();
|
||||
for (Iterator<Entry<Register, RegisterRow>> it = regMap.entrySet().iterator(); it
|
||||
.hasNext();) {
|
||||
Map.Entry<Register, RegisterRow> ent = it.next();
|
||||
|
@ -1246,13 +1261,12 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
protected CompletableFuture<Void> loadRegistersAndValues() {
|
||||
TraceThread curThread = current.getThread();
|
||||
if (curThread == null) {
|
||||
if (current.getThread() == null) {
|
||||
regsTableModel.clear();
|
||||
regMap.clear();
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
Set<Register> selected = getSelectionFor(curThread);
|
||||
Set<Register> selected = getSelectionFor(current.getPlatform());
|
||||
displaySelectedRegisters(selected);
|
||||
return loadValues();
|
||||
}
|
||||
|
@ -1272,6 +1286,30 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
return regs.stream().filter(Register::isBaseRegister).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
protected CompletableFuture<?> readRegistersLegacy(TraceRecorder recorder,
|
||||
TraceThread traceThread, Set<Register> toRead) {
|
||||
DebuggerRegisterMapper regMapper = recorder.getRegisterMapper(traceThread);
|
||||
if (regMapper == null) {
|
||||
Msg.error(this, "Target is live, but we haven't got a register mapper, yet");
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
toRead.retainAll(regMapper.getRegistersOnTarget());
|
||||
TargetRegisterBank bank = recorder.getTargetRegisterBank(traceThread, current.getFrame());
|
||||
if (bank == null || !bank.isValid()) {
|
||||
Msg.error(this, "Current frame's bank does not exist");
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
// TODO: Should probably always be the host platform. I suspect it's ignored anyway.
|
||||
return recorder.captureThreadRegisters(current.getPlatform(), traceThread,
|
||||
current.getFrame(), toRead);
|
||||
}
|
||||
|
||||
protected CompletableFuture<?> readRegistersObjectMode(TraceRecorder recorder,
|
||||
TraceThread traceThread, Set<Register> toRead) {
|
||||
return recorder.captureThreadRegisters(current.getPlatform(), traceThread,
|
||||
current.getFrame(), toRead);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> readRegistersIfLiveAndAccessible() {
|
||||
TraceRecorder recorder = current.getRecorder();
|
||||
if (recorder == null) {
|
||||
|
@ -1289,20 +1327,16 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
if (targetThread == null) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
Set<Register> toRead = new HashSet<>(baseRegisters(getSelectionFor(traceThread)));
|
||||
DebuggerRegisterMapper regMapper = recorder.getRegisterMapper(traceThread);
|
||||
if (regMapper == null) {
|
||||
Msg.error(this, "Target is live, but we haven't got a register mapper, yet");
|
||||
return AsyncUtils.NIL;
|
||||
|
||||
Set<Register> toRead = new HashSet<>(baseRegisters(getSelectionFor(current.getPlatform())));
|
||||
|
||||
CompletableFuture<?> future;
|
||||
if (current.getTrace().getObjectManager().getRootSchema() == null) {
|
||||
future = readRegistersLegacy(recorder, traceThread, toRead);
|
||||
}
|
||||
toRead.retainAll(regMapper.getRegistersOnTarget());
|
||||
TargetRegisterBank bank = recorder.getTargetRegisterBank(traceThread, current.getFrame());
|
||||
if (bank == null || !bank.isValid()) {
|
||||
Msg.error(this, "Current frame's bank does not exist");
|
||||
return AsyncUtils.NIL;
|
||||
else {
|
||||
future = readRegistersObjectMode(recorder, traceThread, toRead);
|
||||
}
|
||||
CompletableFuture<?> future =
|
||||
recorder.captureThreadRegisters(traceThread, current.getFrame(), toRead);
|
||||
return future.exceptionally(ex -> {
|
||||
ex = AsyncUtils.unwrapThrowable(ex);
|
||||
if (ex instanceof DebuggerModelAccessException) {
|
||||
|
|
|
@ -95,8 +95,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
|||
try {
|
||||
// This is on its own task thread, so whatever.
|
||||
// Just don't hang it indefinitely.
|
||||
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY, false)
|
||||
.get(1000, TimeUnit.MILLISECONDS);
|
||||
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY).get(1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
Msg.error("Could not read module header from target", e);
|
||||
|
|
|
@ -30,11 +30,14 @@ import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
|||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceProgramViewListener;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryOperations;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.program.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.schedule.PatchStep;
|
||||
|
@ -146,30 +149,41 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
|
|||
return CompletableFuture
|
||||
.failedFuture(new MemoryAccessException("View is not the present"));
|
||||
}
|
||||
return recorder.writeVariable(coordinates.getThread(), coordinates.getFrame(), address,
|
||||
data);
|
||||
return recorder.writeVariable(coordinates.getPlatform(), coordinates.getThread(),
|
||||
coordinates.getFrame(), address, data);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> writeTraceVariable(DebuggerCoordinates coordinates,
|
||||
Address address, byte[] data) {
|
||||
Address guestAddress, byte[] data) {
|
||||
Trace trace = coordinates.getTrace();
|
||||
TracePlatform platform = coordinates.getPlatform();
|
||||
long snap = coordinates.getViewSnap();
|
||||
Address hostAddress = platform.mapGuestToHost(guestAddress);
|
||||
if (hostAddress == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Guest address " + guestAddress + " is not mapped");
|
||||
}
|
||||
TraceMemoryOperations memOrRegs;
|
||||
Address overlayAddress;
|
||||
try (UndoableTransaction txid =
|
||||
UndoableTransaction.start(trace, "Edit Variable")) {
|
||||
if (address.isRegisterAddress()) {
|
||||
if (hostAddress.isRegisterAddress()) {
|
||||
TraceThread thread = coordinates.getThread();
|
||||
if (thread == null) {
|
||||
throw new IllegalArgumentException("Register edits require a thread.");
|
||||
}
|
||||
memOrRegs = trace.getMemoryManager()
|
||||
TraceMemorySpace regs = trace.getMemoryManager()
|
||||
.getMemoryRegisterSpace(thread, coordinates.getFrame(),
|
||||
true);
|
||||
memOrRegs = regs;
|
||||
overlayAddress = regs.getAddressSpace().getOverlayAddress(hostAddress);
|
||||
}
|
||||
else {
|
||||
memOrRegs = trace.getMemoryManager();
|
||||
overlayAddress = hostAddress;
|
||||
}
|
||||
if (memOrRegs.putBytes(snap, address, ByteBuffer.wrap(data)) != data.length) {
|
||||
if (memOrRegs.putBytes(snap, overlayAddress,
|
||||
ByteBuffer.wrap(data)) != data.length) {
|
||||
return CompletableFuture.failedFuture(new MemoryAccessException());
|
||||
}
|
||||
}
|
||||
|
@ -186,9 +200,9 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
|
|||
// TODO: Well, technically, only for register edits
|
||||
throw new IllegalArgumentException("Emulator edits require a thread.");
|
||||
}
|
||||
Language language = coordinates.getPlatform().getLanguage();
|
||||
TraceSchedule time = coordinates.getTime()
|
||||
.patched(thread, PatchStep.generateSleigh(
|
||||
coordinates.getTrace().getBaseLanguage(), address, data));
|
||||
.patched(thread, language, PatchStep.generateSleigh(language, address, data));
|
||||
|
||||
DebuggerCoordinates withTime = coordinates.time(time);
|
||||
Long found = traceManager.findSnapshot(withTime);
|
||||
|
@ -198,7 +212,7 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
|
|||
// TODO: Could still do it async on another thread, no?
|
||||
// Not sure it buys anything, since program view will call .get on swing thread
|
||||
try {
|
||||
emulationSerivce.emulate(coordinates.getTrace(), time, TaskMonitor.DUMMY);
|
||||
emulationSerivce.emulate(coordinates.getPlatform(), time, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new AssertionError(e);
|
||||
|
|
|
@ -82,12 +82,15 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
protected static final int MAX_CACHE_SIZE = 5;
|
||||
|
||||
protected static class CacheKey implements Comparable<CacheKey> {
|
||||
// TODO: Should key on platform, not trace
|
||||
protected final Trace trace;
|
||||
protected final TracePlatform platform;
|
||||
protected final TraceSchedule time;
|
||||
private final int hashCode;
|
||||
|
||||
public CacheKey(Trace trace, TraceSchedule time) {
|
||||
this.trace = Objects.requireNonNull(trace);
|
||||
public CacheKey(TracePlatform platform, TraceSchedule time) {
|
||||
this.platform = Objects.requireNonNull(platform);
|
||||
this.trace = platform.getTrace();
|
||||
this.time = Objects.requireNonNull(time);
|
||||
this.hashCode = Objects.hash(trace, time);
|
||||
}
|
||||
|
@ -425,7 +428,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Long> backgroundEmulate(Trace trace, TraceSchedule time) {
|
||||
public CompletableFuture<Long> backgroundEmulate(TracePlatform platform, TraceSchedule time) {
|
||||
Trace trace = platform.getTrace();
|
||||
if (!traceManager.getOpenTraces().contains(trace)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot emulate a trace unless it's opened in the tool.");
|
||||
|
@ -433,7 +437,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
if (time.isSnapOnly()) {
|
||||
return CompletableFuture.completedFuture(time.getSnap());
|
||||
}
|
||||
return requests.get(new CacheKey(trace, time));
|
||||
return requests.get(new CacheKey(platform, time));
|
||||
}
|
||||
|
||||
protected TraceSnapshot findScratch(Trace trace, TraceSchedule time) {
|
||||
|
@ -459,12 +463,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
|
||||
protected long doEmulate(CacheKey key, TaskMonitor monitor) throws CancelledException {
|
||||
Trace trace = key.trace;
|
||||
/**
|
||||
* TODO: object and/or platform should somehow be incorporated into the key, the schedule?
|
||||
* something?
|
||||
*/
|
||||
DebuggerCoordinates current = traceManager.resolveTrace(trace);
|
||||
TracePlatform platform = current.getPlatform();
|
||||
TracePlatform platform = key.platform;
|
||||
TraceSchedule time = key.time;
|
||||
|
||||
CachedEmulator ce;
|
||||
|
@ -521,6 +520,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
return;
|
||||
}
|
||||
// Cause object-register support to copy values into new register spaces
|
||||
// TODO: I wish this were not necessary
|
||||
monitor.setMessage("Creating register spaces");
|
||||
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Prepare emulation")) {
|
||||
for (TraceThread thread : time.getThreads(trace)) {
|
||||
|
@ -530,8 +530,9 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
}
|
||||
|
||||
@Override
|
||||
public long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
|
||||
public long emulate(TracePlatform platform, TraceSchedule time, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
Trace trace = platform.getTrace();
|
||||
if (!traceManager.getOpenTraces().contains(trace)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot emulate a trace unless it's opened in the tool.");
|
||||
|
@ -539,12 +540,13 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
if (time.isSnapOnly()) {
|
||||
return time.getSnap();
|
||||
}
|
||||
return doEmulate(new CacheKey(trace, time), monitor);
|
||||
return doEmulate(new CacheKey(platform, time), monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) {
|
||||
CachedEmulator ce = cache.get(new CacheKey(trace, time));
|
||||
CachedEmulator ce =
|
||||
cache.get(new CacheKey(trace.getPlatformManager().getHostPlatform(), time));
|
||||
return ce == null ? null : ce.emulator;
|
||||
}
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ public enum ProgramEmulationUtils {
|
|||
if (stack != null) {
|
||||
CompilerSpec cSpec = trace.getBaseCompilerSpec();
|
||||
Address sp = cSpec.stackGrowsNegative()
|
||||
? stack.getMaxAddress()
|
||||
? stack.getMaxAddress().addWrap(1)
|
||||
: stack.getMinAddress();
|
||||
Register regSP = cSpec.getStackPointer();
|
||||
if (regSP != null) {
|
||||
|
|
|
@ -84,7 +84,7 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc
|
|||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
AddressSetView hostView = platform.mapGuestToHost(guestView);
|
||||
return recorder.readMemoryBlocks(hostView, TaskMonitor.DUMMY, false)
|
||||
return recorder.readMemoryBlocks(hostView, TaskMonitor.DUMMY)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
|
|
|
@ -96,7 +96,7 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist
|
|||
toRead.add(register);
|
||||
}
|
||||
}
|
||||
return recorder.captureThreadRegisters(thread, 0, toRead)
|
||||
return recorder.captureThreadRegisters(platform, thread, 0, toRead)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
|
@ -108,7 +108,7 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist
|
|||
if (!isLive()) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
return recorder.writeRegister(thread, frame, address.getPhysicalAddress(), data)
|
||||
return recorder.writeRegister(platform, thread, frame, address.getPhysicalAddress(), data)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
|
|
|
@ -46,9 +46,9 @@ public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
|||
this.memoryManager = trace.getMemoryManager();
|
||||
}
|
||||
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
||||
TaskMonitor monitor, boolean toMap) {
|
||||
return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor, toMap);
|
||||
public CompletableFuture<Void> captureProcessMemory(AddressSetView set,
|
||||
TaskMonitor monitor) {
|
||||
return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -42,6 +42,7 @@ import ghidra.program.model.lang.RegisterValue;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.trace.model.modules.TraceSection;
|
||||
|
@ -268,12 +269,11 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||
/*---------------- CAPTURE METHODS -------------------*/
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set,
|
||||
TaskMonitor monitor, boolean toMap) {
|
||||
public CompletableFuture<Void> readMemoryBlocks(AddressSetView set, TaskMonitor monitor) {
|
||||
if (set.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(new TreeMap<>());
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return memoryRecorder.captureProcessMemory(set, monitor, toMap);
|
||||
return memoryRecorder.captureProcessMemory(set, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -315,11 +315,10 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Map<Register, RegisterValue>> captureThreadRegisters(
|
||||
TraceThread thread, int frameLevel,
|
||||
Set<Register> registers) {
|
||||
public CompletableFuture<Void> captureThreadRegisters(
|
||||
TracePlatform platform, TraceThread thread, int frameLevel, Set<Register> registers) {
|
||||
DefaultThreadRecorder rec = getThreadRecorder(thread);
|
||||
return rec.captureThreadRegisters(thread, frameLevel, registers);
|
||||
return rec.captureThreadRegisters(thread, frameLevel, registers).thenApply(__ -> null);
|
||||
}
|
||||
|
||||
/*---------------- SNAPSHOT METHODS -------------------*/
|
||||
|
@ -537,8 +536,8 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeThreadRegisters(TraceThread thread, int frameLevel,
|
||||
Map<Register, RegisterValue> values) {
|
||||
public CompletableFuture<Void> writeThreadRegisters(TracePlatform platform, TraceThread thread,
|
||||
int frameLevel, Map<Register, RegisterValue> values) {
|
||||
DefaultThreadRecorder rec = getThreadRecorder(thread);
|
||||
return (rec == null) ? null : rec.writeThreadRegisters(frameLevel, values);
|
||||
}
|
||||
|
|
|
@ -521,10 +521,14 @@ public class TraceObjectManager {
|
|||
}
|
||||
if (added.containsKey(TargetObject.VALUE_ATTRIBUTE_NAME)) {
|
||||
TargetRegister register = (TargetRegister) parent;
|
||||
String valstr = (String) added.get(TargetObject.VALUE_ATTRIBUTE_NAME);
|
||||
byte[] value = new BigInteger(valstr, 16).toByteArray();
|
||||
Object val = added.get(TargetObject.VALUE_ATTRIBUTE_NAME);
|
||||
ManagedThreadRecorder rec = recorder.getThreadRecorderForSuccessor(register);
|
||||
rec.recordRegisterValue(register, value);
|
||||
if (val instanceof String valstr) {
|
||||
rec.recordRegisterValue(register, new BigInteger(valstr, 16).toByteArray());
|
||||
}
|
||||
else if (val instanceof byte[] valarr) {
|
||||
rec.recordRegisterValue(register, valarr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.plugin.core.debug.service.model.record;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -27,6 +28,7 @@ import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
|
|||
import ghidra.app.plugin.core.debug.service.model.PermanentTransactionExecutor;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.app.services.TraceRecorderListener;
|
||||
import ghidra.async.AsyncFence;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.AnnotatedDebuggerAttributeListener;
|
||||
import ghidra.dbg.error.DebuggerMemoryAccessException;
|
||||
|
@ -34,13 +36,16 @@ import ghidra.dbg.error.DebuggerModelAccessException;
|
|||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetEventScope.TargetEventType;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.trace.database.module.TraceObjectSection;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
|
||||
import ghidra.trace.model.modules.*;
|
||||
|
@ -471,13 +476,13 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
|
|||
|
||||
@Override
|
||||
public boolean isRegisterBankAccessible(TargetRegisterBank bank) {
|
||||
// TODO: This seems a little aggressive, but the accessbility thing is already out of hand
|
||||
// TODO: This seems a little aggressive, but the accessibility thing is already out of hand
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisterBankAccessible(TraceThread thread, int frameLevel) {
|
||||
// TODO: This seems a little aggressive, but the accessbility thing is already out of hand
|
||||
// TODO: This seems a little aggressive, but the accessibility thing is already out of hand
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -486,16 +491,88 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
|
|||
return memoryRecorder.getAccessible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Map<Register, RegisterValue>> captureThreadRegisters(
|
||||
TraceThread thread, int frameLevel, Set<Register> registers) {
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
protected TargetRegisterContainer getTargetRegisterContainer(TraceThread thread,
|
||||
int frameLevel) {
|
||||
if (!(thread instanceof TraceObjectThread tot)) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
TraceObject objThread = tot.getObject();
|
||||
TraceObject regContainer = objThread.queryRegisterContainer(frameLevel);
|
||||
if (regContainer == null) {
|
||||
Msg.error(this,
|
||||
"No register container for " + thread + " and frame " + frameLevel + " in trace");
|
||||
return null;
|
||||
}
|
||||
TargetObject result =
|
||||
target.getModel().getModelObject(regContainer.getCanonicalPath().getKeyList());
|
||||
if (result == null) {
|
||||
Msg.error(this,
|
||||
"No register container for " + thread + " and frame " + frameLevel + " on target");
|
||||
return null;
|
||||
}
|
||||
return (TargetRegisterContainer) result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeThreadRegisters(TraceThread thread, int frameLevel,
|
||||
Map<Register, RegisterValue> values) {
|
||||
throw new UnsupportedOperationException();
|
||||
public CompletableFuture<Void> captureThreadRegisters(
|
||||
TracePlatform platform, TraceThread thread, int frameLevel, Set<Register> registers) {
|
||||
TargetRegisterContainer regContainer = getTargetRegisterContainer(thread, frameLevel);
|
||||
/**
|
||||
* TODO: Seems I should be able to single out specific registers.... Is this convention
|
||||
* universal, or do some models allow refreshing on a register-by-register basis? If so,
|
||||
* what communicates that convention?
|
||||
*/
|
||||
if (regContainer == null) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return regContainer.resync();
|
||||
}
|
||||
|
||||
protected static byte[] encodeValue(int byteLength, BigInteger value) {
|
||||
return Utils.bigIntegerToBytes(value, byteLength, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeThreadRegisters(TracePlatform platform, TraceThread thread,
|
||||
int frameLevel, Map<Register, RegisterValue> values) {
|
||||
TargetRegisterContainer regContainer = getTargetRegisterContainer(thread, frameLevel);
|
||||
if (regContainer == null) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
Map<TargetRegisterBank, Map<TargetRegister, byte[]>> writesByBank = new HashMap<>();
|
||||
for (RegisterValue rv : values.values()) {
|
||||
Register register = rv.getRegister();
|
||||
PathMatcher matcher =
|
||||
platform.getConventionalRegisterPath(regContainer.getSchema(), List.of(), register);
|
||||
Collection<TargetObject> regs = matcher.getCachedSuccessors(regContainer).values();
|
||||
if (regs.isEmpty()) {
|
||||
Msg.warn(this, "No register object for " + register);
|
||||
}
|
||||
for (TargetObject objRegUntyped : regs) {
|
||||
TargetRegister objReg = (TargetRegister) objRegUntyped;
|
||||
List<String> pathBank = objReg.getModel()
|
||||
.getRootSchema()
|
||||
.searchForAncestor(TargetRegisterBank.class, objReg.getPath());
|
||||
if (pathBank == null) {
|
||||
Msg.warn(this, "No register bank for " + register);
|
||||
continue;
|
||||
}
|
||||
TargetRegisterBank objBank =
|
||||
(TargetRegisterBank) objReg.getModel().getModelObject(pathBank);
|
||||
if (objBank == null) {
|
||||
Msg.warn(this, "No register bank for " + register);
|
||||
continue;
|
||||
}
|
||||
writesByBank.computeIfAbsent(objBank, __ -> new HashMap<>())
|
||||
.put(objReg, encodeValue(objReg.getByteLength(), rv.getUnsignedValue()));
|
||||
}
|
||||
}
|
||||
AsyncFence fence = new AsyncFence();
|
||||
for (Map.Entry<TargetRegisterBank, Map<TargetRegister, byte[]>> ent : writesByBank
|
||||
.entrySet()) {
|
||||
fence.include(ent.getKey().writeRegisters(ent.getValue()));
|
||||
}
|
||||
return fence.ready();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -509,10 +586,8 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(
|
||||
AddressSetView set, TaskMonitor monitor, boolean returnResult) {
|
||||
return RecorderUtils.INSTANCE.readMemoryBlocks(this, BLOCK_BITS, set, monitor,
|
||||
returnResult);
|
||||
public CompletableFuture<Void> readMemoryBlocks(AddressSetView set, TaskMonitor monitor) {
|
||||
return RecorderUtils.INSTANCE.readMemoryBlocks(this, BLOCK_BITS, set, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model.record;
|
||||
|
||||
import java.util.NavigableMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
|
@ -45,9 +43,8 @@ public enum RecorderUtils {
|
|||
return result;
|
||||
}
|
||||
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(
|
||||
TraceRecorder recorder, int blockBits, AddressSetView set, TaskMonitor monitor,
|
||||
boolean returnResult) {
|
||||
public CompletableFuture<Void> readMemoryBlocks(
|
||||
TraceRecorder recorder, int blockBits, AddressSetView set, TaskMonitor monitor) {
|
||||
|
||||
// NOTE: I don't intend to warn about the number of requests.
|
||||
// They're delivered in serial, and there's a cancel button that works
|
||||
|
@ -61,7 +58,6 @@ public enum RecorderUtils {
|
|||
monitor.initialize(total);
|
||||
monitor.setMessage("Reading memory");
|
||||
// TODO: Read blocks in parallel? Probably NO. Tends to overload the connector.
|
||||
NavigableMap<Address, byte[]> result = returnResult ? new TreeMap<>() : null;
|
||||
return AsyncUtils.each(TypeSpec.VOID, expSet.iterator(), (r, loop) -> {
|
||||
AddressRangeChunker blocks = new AddressRangeChunker(r, blockSize);
|
||||
AsyncUtils.each(TypeSpec.VOID, blocks.iterator(), (blk, inner) -> {
|
||||
|
@ -69,15 +65,11 @@ public enum RecorderUtils {
|
|||
monitor.incrementProgress(1);
|
||||
CompletableFuture<byte[]> future =
|
||||
recorder.readMemory(blk.getMinAddress(), (int) blk.getLength());
|
||||
future.thenAccept(data -> {
|
||||
if (returnResult) {
|
||||
result.put(blk.getMinAddress(), data);
|
||||
}
|
||||
}).exceptionally(e -> {
|
||||
future.exceptionally(e -> {
|
||||
Msg.error(this, "Could not read " + blk + ": " + e);
|
||||
return null; // Continue looping on errors
|
||||
}).thenApply(__ -> !monitor.isCancelled()).handle(inner::repeatWhile);
|
||||
}).thenApply(v -> !monitor.isCancelled()).handle(loop::repeatWhile);
|
||||
}).thenApply(__ -> result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -651,7 +651,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
|||
"Cannot navigate to coordinates with execution schedules, " +
|
||||
"because the emulation service is not available.");
|
||||
}
|
||||
return emulationService.backgroundEmulate(coordinates.getTrace(), coordinates.getTime());
|
||||
return emulationService.backgroundEmulate(coordinates.getPlatform(), coordinates.getTime());
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> prepareViewAndFireEvent(DebuggerCoordinates coordinates) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
import ghidra.app.plugin.core.debug.service.emulation.*;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -83,13 +84,29 @@ public interface DebuggerEmulationService {
|
|||
* costs. On the other hand, the service should be careful to invalidate cached results when the
|
||||
* recorded machine state in a trace changes.
|
||||
*
|
||||
* @param trace the trace containing the initial state
|
||||
* @param platform the trace platform containing the initial state
|
||||
* @param time the time coordinates, including initial snap, steps, and p-code steps
|
||||
* @param monitor a monitor for cancellation and progress reporting
|
||||
* @return the snap in the trace's scratch space where the realized state is stored
|
||||
* @throws CancelledException if the emulation is cancelled
|
||||
*/
|
||||
long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor) throws CancelledException;
|
||||
long emulate(TracePlatform platform, TraceSchedule time, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
|
||||
/**
|
||||
* Emulate using the trace's "host" platform
|
||||
*
|
||||
* @see #emulate(TracePlatform, TraceSchedule, TaskMonitor)
|
||||
* @param trace
|
||||
* @param time
|
||||
* @param monitor
|
||||
* @return
|
||||
* @throws CancelledException
|
||||
*/
|
||||
default long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
return emulate(trace.getPlatformManager().getHostPlatform(), time, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke {@link #emulate(Trace, TraceSchedule, TaskMonitor)} in the background
|
||||
|
@ -97,15 +114,15 @@ public interface DebuggerEmulationService {
|
|||
* <p>
|
||||
* This is the preferred means of performing emulation. Because the underlying emulator may
|
||||
* request a <em>blocking</em> read from a target, it is important that
|
||||
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)} is <em>never</em> called by the Swing
|
||||
* thread.
|
||||
* {@link #emulate(TracePlatform, TraceSchedule, TaskMonitor)} is <em>never</em> called by the
|
||||
* Swing thread.
|
||||
*
|
||||
* @param trace the trace containing the initial state
|
||||
* @param platform the trace platform containing the initial state
|
||||
* @param time the time coordinates, including initial snap, steps, and p-code steps
|
||||
* @return a future which completes with the result of
|
||||
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)}
|
||||
* {@link #emulate(TracePlatform, TraceSchedule, TaskMonitor)}
|
||||
*/
|
||||
CompletableFuture<Long> backgroundEmulate(Trace trace, TraceSchedule time);
|
||||
CompletableFuture<Long> backgroundEmulate(TracePlatform platform, TraceSchedule time);
|
||||
|
||||
/**
|
||||
* The the cached emulator for the given trace and time
|
||||
|
@ -116,6 +133,9 @@ public interface DebuggerEmulationService {
|
|||
* <p>
|
||||
* <b>WARNING:</b> This emulator belongs to this service. Stepping it, or otherwise manipulating
|
||||
* it without the service's knowledge can lead to unintended consequences.
|
||||
* <p>
|
||||
* TODO: Should cache by (Platform, Time) instead, but need a way to distinguish platform in the
|
||||
* trace's time table.
|
||||
*
|
||||
* @param trace the trace containing the initial state
|
||||
* @param time the time coordinates, including initial snap, steps, and p-code steps
|
||||
|
|
|
@ -91,9 +91,8 @@ public interface DebuggerStateEditingService {
|
|||
|
||||
default CompletableFuture<Void> setRegister(RegisterValue value) {
|
||||
Register register = value.getRegister();
|
||||
boolean isBigEndian = getCoordinates().getTrace().getBaseLanguage().isBigEndian();
|
||||
byte[] bytes = Utils.bigIntegerToBytes(value.getUnsignedValue(), register.getNumBytes(),
|
||||
isBigEndian);
|
||||
register.isBigEndian());
|
||||
return setVariable(register.getAddress(), bytes);
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +114,12 @@ public interface DebuggerStateEditingService {
|
|||
|
||||
StateEditor createStateEditor(DebuggerCoordinates coordinates);
|
||||
|
||||
/**
|
||||
* Create a state editor whose coordinates follow the trace manager for the given trace
|
||||
*
|
||||
* @param trace the trace to follow
|
||||
* @return the editor
|
||||
*/
|
||||
StateEditor createStateEditor(Trace trace);
|
||||
|
||||
StateEditingMemoryHandler createStateEditor(TraceProgramView view);
|
||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.program.model.lang.*;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
|
@ -263,15 +264,16 @@ public interface TraceRecorder {
|
|||
* Nevertheless, this method can force the retrieval of a given set of registers from the
|
||||
* target.
|
||||
*
|
||||
* @param platform the platform whose language defines the registers
|
||||
* @param thread the trace thread associated with the desired target thread
|
||||
* @param frameLevel the number of stack frames to "unwind", likely 0
|
||||
* @param registers the <em>base</em> registers, as viewed by the trace
|
||||
* @return a future which completes with the captured values
|
||||
* @return a future which completes when the commands succeed
|
||||
* @throws IllegalArgumentException if no {@link TargetRegisterBank} is known for the given
|
||||
* thread
|
||||
*/
|
||||
CompletableFuture<Map<Register, RegisterValue>> captureThreadRegisters(TraceThread thread,
|
||||
int frameLevel, Set<Register> registers);
|
||||
CompletableFuture<Void> captureThreadRegisters(TracePlatform platform,
|
||||
TraceThread thread, int frameLevel, Set<Register> registers);
|
||||
|
||||
/**
|
||||
* Write a target thread's registers.
|
||||
|
@ -280,6 +282,7 @@ public interface TraceRecorder {
|
|||
* Note that the model and recorder should cause values successfully written on the target to be
|
||||
* updated in the trace. The caller should not update the trace out of band.
|
||||
*
|
||||
* @param platform the platform whose language defines the registers
|
||||
* @param thread the trace thread associated with the desired target thread
|
||||
* @param frameLevel the number of stack frames to "unwind", likely 0
|
||||
* @param values the values to write
|
||||
|
@ -287,8 +290,8 @@ public interface TraceRecorder {
|
|||
* @throws IllegalArgumentException if no {@link TargetRegisterBank} is known for the given
|
||||
* thread
|
||||
*/
|
||||
CompletableFuture<Void> writeThreadRegisters(TraceThread thread, int frameLevel,
|
||||
Map<Register, RegisterValue> values);
|
||||
CompletableFuture<Void> writeThreadRegisters(TracePlatform platform, TraceThread thread,
|
||||
int frameLevel, Map<Register, RegisterValue> values);
|
||||
|
||||
/**
|
||||
* Read (and capture) a range of target memory
|
||||
|
@ -320,20 +323,14 @@ public interface TraceRecorder {
|
|||
*
|
||||
* <p>
|
||||
* This task is relatively error tolerant. If a block or region cannot be captured -- a common
|
||||
* occurrence -- the error is logged, but the task may still complete "successfully." For large
|
||||
* captures, it is recommended to set {@code returnResult} to false. The recorder will capture
|
||||
* the bytes into the trace where they can be retrieved later. For small captures, and where
|
||||
* bypassing the database may offer some advantage, set {@code returnResult} to true, and the
|
||||
* captured bytes will be returned in an interval map. Connected intervals may or may not be
|
||||
* joined.
|
||||
* occurrence -- the error is logged, but the task may still complete "successfully."
|
||||
*
|
||||
* @param set the addresses to capture, as viewed in the trace
|
||||
* @param monitor a monitor for displaying task steps
|
||||
* @param returnResult true to complete with results, false to complete with null
|
||||
* @return a future which completes when the task finishes
|
||||
*/
|
||||
CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set,
|
||||
TaskMonitor monitor, boolean returnResult);
|
||||
CompletableFuture<Void> readMemoryBlocks(AddressSetView set, TaskMonitor monitor);
|
||||
|
||||
/**
|
||||
* Write a variable (memory or register) of the given thread or the process
|
||||
|
@ -350,13 +347,13 @@ public interface TraceRecorder {
|
|||
* @param data the value to write
|
||||
* @return a future which completes when the write is complete
|
||||
*/
|
||||
default CompletableFuture<Void> writeVariable(TraceThread thread, int frameLevel,
|
||||
Address address, byte[] data) {
|
||||
default CompletableFuture<Void> writeVariable(TracePlatform platform, TraceThread thread,
|
||||
int frameLevel, Address address, byte[] data) {
|
||||
if (address.isMemoryAddress()) {
|
||||
return writeMemory(address, data);
|
||||
}
|
||||
if (address.isRegisterAddress()) {
|
||||
return writeRegister(thread, frameLevel, address, data);
|
||||
return writeRegister(platform, thread, frameLevel, address, data);
|
||||
}
|
||||
throw new IllegalArgumentException("Address is not in a recognized space: " + address);
|
||||
}
|
||||
|
@ -370,21 +367,21 @@ public interface TraceRecorder {
|
|||
* @param data the value to write
|
||||
* @return a future which completes when the write is complete
|
||||
*/
|
||||
default CompletableFuture<Void> writeRegister(TraceThread thread, int frameLevel,
|
||||
Address address, byte[] data) {
|
||||
Language lang = getTrace().getBaseLanguage();
|
||||
Register register = lang.getRegister(address, data.length);
|
||||
default CompletableFuture<Void> writeRegister(TracePlatform platform, TraceThread thread,
|
||||
int frameLevel, Address address, byte[] data) {
|
||||
Register register = platform.getLanguage().getRegister(address, data.length);
|
||||
if (register == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot identify the (single) register to write: " + address);
|
||||
}
|
||||
|
||||
RegisterValue rv = new RegisterValue(register,
|
||||
Utils.bytesToBigInteger(data, data.length, lang.isBigEndian(), false));
|
||||
Utils.bytesToBigInteger(data, data.length, register.isBigEndian(), false));
|
||||
TraceMemorySpace regs =
|
||||
getTrace().getMemoryManager().getMemoryRegisterSpace(thread, frameLevel, false);
|
||||
rv = TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, getSnap(), regs, true);
|
||||
return writeThreadRegisters(thread, frameLevel, Map.of(rv.getRegister(), rv));
|
||||
rv = TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, platform, getSnap(), regs,
|
||||
true);
|
||||
return writeThreadRegisters(platform, thread, frameLevel, Map.of(rv.getRegister(), rv));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,6 +47,7 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceLocation;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryOperations;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
|
@ -158,7 +159,7 @@ public interface FlatDebuggerAPI {
|
|||
* Get the current trace
|
||||
*
|
||||
* @see #getCurrentDebuggerCoordinates()
|
||||
* @return the trace
|
||||
* @return the trace, or null
|
||||
*/
|
||||
default Trace getCurrentTrace() {
|
||||
return getTraceManager().getCurrentTrace();
|
||||
|
@ -192,6 +193,45 @@ public interface FlatDebuggerAPI {
|
|||
return trace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current trace platform
|
||||
*
|
||||
* @return the trace platform, or null
|
||||
*/
|
||||
default TracePlatform getCurrentPlatform() {
|
||||
return getTraceManager().getCurrentPlatform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current trace platform, throwing an exception if there isn't one
|
||||
*
|
||||
* @return the trace platform
|
||||
* @throws IllegalStateException if there is no current trace platform
|
||||
*/
|
||||
default TracePlatform requireCurrentPlatform() {
|
||||
TracePlatform platform = getCurrentPlatform();
|
||||
if (platform == null) {
|
||||
// NB: Yes I've left off "platform"
|
||||
// It's less confusing, and if there's a trace, there's always a platform
|
||||
throw new IllegalStateException("There is no current trace");
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that the given platform is not null
|
||||
*
|
||||
* @param platform the platform
|
||||
* @return the platform
|
||||
* @throws IllegalStateException if the platform is null
|
||||
*/
|
||||
default TracePlatform requirePlatform(TracePlatform platform) {
|
||||
if (platform == null) {
|
||||
throw new IllegalStateException("There is no platform");
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current thread
|
||||
*
|
||||
|
@ -594,6 +634,25 @@ public interface FlatDebuggerAPI {
|
|||
return emulateLaunch(requireCurrentProgram(), address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate the given trace platform as specified in the given schedule and display the result in
|
||||
* the UI
|
||||
*
|
||||
* @param platform the trace platform
|
||||
* @param time the schedule of steps
|
||||
* @param monitor a monitor for the emulation
|
||||
* @return true if successful
|
||||
* @throws CancelledException if the user cancelled via the given monitor
|
||||
*/
|
||||
default boolean emulate(TracePlatform platform, TraceSchedule time, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
// Use the script's thread to perform the actual emulation
|
||||
getEmulationService().emulate(platform, time, monitor);
|
||||
// This should just display the cached state
|
||||
getTraceManager().activateTime(time);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate the given trace as specified in the given schedule and display the result in the UI
|
||||
*
|
||||
|
@ -605,11 +664,7 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default boolean emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
// Use the script's thread to perform the actual emulation
|
||||
getEmulationService().emulate(trace, time, monitor);
|
||||
// This should just display the cached state
|
||||
getTraceManager().activateTime(time);
|
||||
return true;
|
||||
return emulate(trace.getPlatformManager().getHostPlatform(), time, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -622,7 +677,7 @@ public interface FlatDebuggerAPI {
|
|||
* @throws IllegalStateException if there is no current trace
|
||||
*/
|
||||
default boolean emulate(TraceSchedule time, TaskMonitor monitor) throws CancelledException {
|
||||
return emulate(requireCurrentTrace(), time, monitor);
|
||||
return emulate(requireCurrentPlatform(), time, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -636,13 +691,13 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default boolean stepEmuInstruction(long count, TaskMonitor monitor) throws CancelledException {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
Trace trace = requireCurrentTrace();
|
||||
TracePlatform platform = requireCurrentPlatform();
|
||||
TraceThread thread = current.getThread();
|
||||
TraceSchedule time = current.getTime();
|
||||
TraceSchedule stepped = count <= 0
|
||||
? time.steppedBackward(trace, -count)
|
||||
? time.steppedBackward(platform.getTrace(), -count)
|
||||
: time.steppedForward(requireThread(thread), count);
|
||||
return emulate(trace, stepped, monitor);
|
||||
return emulate(platform, stepped, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -655,13 +710,13 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default boolean stepEmuPcodeOp(int count, TaskMonitor monitor) throws CancelledException {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
Trace trace = requireCurrentTrace();
|
||||
TracePlatform platform = requireCurrentPlatform();
|
||||
TraceThread thread = current.getThread();
|
||||
TraceSchedule time = current.getTime();
|
||||
TraceSchedule stepped = count <= 0
|
||||
? time.steppedPcodeBackward(-count)
|
||||
: time.steppedPcodeForward(requireThread(thread), count);
|
||||
return emulate(trace, stepped, monitor);
|
||||
return emulate(platform, stepped, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -678,13 +733,13 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default boolean skipEmuInstruction(long count, TaskMonitor monitor) throws CancelledException {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
Trace trace = requireCurrentTrace();
|
||||
TracePlatform platform = requireCurrentPlatform();
|
||||
TraceThread thread = current.getThread();
|
||||
TraceSchedule time = current.getTime();
|
||||
TraceSchedule stepped = count <= 0
|
||||
? time.steppedBackward(trace, -count)
|
||||
? time.steppedBackward(platform.getTrace(), -count)
|
||||
: time.skippedForward(requireThread(thread), count);
|
||||
return emulate(trace, stepped, monitor);
|
||||
return emulate(platform, stepped, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -701,13 +756,13 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default boolean skipEmuPcodeOp(int count, TaskMonitor monitor) throws CancelledException {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
Trace trace = requireCurrentTrace();
|
||||
TracePlatform platform = requireCurrentPlatform();
|
||||
TraceThread thread = current.getThread();
|
||||
TraceSchedule time = current.getTime();
|
||||
TraceSchedule stepped = count <= 0
|
||||
? time.steppedPcodeBackward(-count)
|
||||
: time.skippedPcodeForward(requireThread(thread), count);
|
||||
return emulate(trace, stepped, monitor);
|
||||
return emulate(platform, stepped, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -720,11 +775,11 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default boolean patchEmu(String sleigh, TaskMonitor monitor) throws CancelledException {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
Trace trace = requireCurrentTrace();
|
||||
TracePlatform platform = requireCurrentPlatform();
|
||||
TraceThread thread = current.getThread();
|
||||
TraceSchedule time = current.getTime();
|
||||
TraceSchedule patched = time.patched(requireThread(thread), sleigh);
|
||||
return emulate(trace, patched, monitor);
|
||||
TraceSchedule patched = time.patched(requireThread(thread), platform.getLanguage(), sleigh);
|
||||
return emulate(platform, patched, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -771,7 +826,7 @@ public interface FlatDebuggerAPI {
|
|||
if (recorder.getSnap() != snap) {
|
||||
return;
|
||||
}
|
||||
waitOn(recorder.readMemoryBlocks(new AddressSet(safeRange(start, length)), monitor, false));
|
||||
waitOn(recorder.readMemoryBlocks(new AddressSet(safeRange(start, length)), monitor));
|
||||
waitOn(recorder.getTarget().getModel().flushEvents());
|
||||
waitOn(recorder.flushTransactions());
|
||||
trace.flushEvents();
|
||||
|
@ -903,6 +958,7 @@ public interface FlatDebuggerAPI {
|
|||
/**
|
||||
* Copy registers from target to trace, if applicable and not already cached
|
||||
*
|
||||
* @param platform the platform whose language defines the registers
|
||||
* @param thread the trace thread to update
|
||||
* @param frame the frame level, 0 being the innermost
|
||||
* @param snap the snap, to determine whether target values are applicable
|
||||
|
@ -911,8 +967,8 @@ public interface FlatDebuggerAPI {
|
|||
* @throws ExecutionException if an error occurs
|
||||
* @throws TimeoutException if the operation times out
|
||||
*/
|
||||
default void refreshRegistersIfLive(TraceThread thread, int frame, long snap,
|
||||
Collection<Register> registers)
|
||||
default void refreshRegistersIfLive(TracePlatform platform, TraceThread thread, int frame,
|
||||
long snap, Collection<Register> registers)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
Trace trace = thread.getTrace();
|
||||
TraceRecorder recorder = getModelService().getRecorder(trace);
|
||||
|
@ -924,7 +980,7 @@ public interface FlatDebuggerAPI {
|
|||
}
|
||||
Set<Register> asSet =
|
||||
registers instanceof Set<?> ? (Set<Register>) registers : Set.copyOf(registers);
|
||||
waitOn(recorder.captureThreadRegisters(thread, frame, asSet));
|
||||
waitOn(recorder.captureThreadRegisters(platform, thread, frame, asSet));
|
||||
waitOn(recorder.getTarget().getModel().flushEvents());
|
||||
waitOn(recorder.flushTransactions());
|
||||
trace.flushEvents();
|
||||
|
@ -933,16 +989,17 @@ public interface FlatDebuggerAPI {
|
|||
/**
|
||||
* Read several registers from the given context, refreshing from target if needed
|
||||
*
|
||||
* @param platform the platform whose language defines the registers
|
||||
* @param thread the trace thread
|
||||
* @param frame the source frame level, 0 being the innermost
|
||||
* @param snap the source snap
|
||||
* @param registers the source registers
|
||||
* @return the list of register values, or null on error
|
||||
*/
|
||||
default List<RegisterValue> readRegisters(TraceThread thread, int frame, long snap,
|
||||
Collection<Register> registers) {
|
||||
default List<RegisterValue> readRegisters(TracePlatform platform, TraceThread thread, int frame,
|
||||
long snap, Collection<Register> registers) {
|
||||
try {
|
||||
refreshRegistersIfLive(thread, frame, snap, registers);
|
||||
refreshRegistersIfLive(platform, thread, frame, snap, registers);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
return null;
|
||||
|
@ -961,9 +1018,9 @@ public interface FlatDebuggerAPI {
|
|||
* @see #readRegisters(TraceThread, int, long, Collection)
|
||||
* @return the register's value, or null on error
|
||||
*/
|
||||
default RegisterValue readRegister(TraceThread thread, int frame, long snap,
|
||||
Register register) {
|
||||
List<RegisterValue> result = readRegisters(thread, frame, snap, Set.of(register));
|
||||
default RegisterValue readRegister(TracePlatform platform, TraceThread thread, int frame,
|
||||
long snap, Register register) {
|
||||
List<RegisterValue> result = readRegisters(platform, thread, frame, snap, Set.of(register));
|
||||
return result == null ? null : result.get(0);
|
||||
}
|
||||
|
||||
|
@ -974,8 +1031,8 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default List<RegisterValue> readRegisters(Collection<Register> registers) {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
return readRegisters(requireThread(current.getThread()), current.getFrame(),
|
||||
current.getSnap(), registers);
|
||||
return readRegisters(requireCurrentPlatform(), requireThread(current.getThread()),
|
||||
current.getFrame(), current.getSnap(), registers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1030,6 +1087,27 @@ public interface FlatDebuggerAPI {
|
|||
return readRegisters(validateRegisterNames(requireCurrentTrace().getBaseLanguage(), names));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a register from the current context, refreshing from the target if needed
|
||||
*
|
||||
* @param platform the platform whose language defines the register
|
||||
* @param register the register
|
||||
* @return the value, or null on error
|
||||
*/
|
||||
default RegisterValue readRegister(TracePlatform platform, Register register) {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
if (platform.getTrace() != current.getTrace()) {
|
||||
throw new IllegalArgumentException("Given platform is not from the current trace");
|
||||
}
|
||||
Language language = platform.getLanguage();
|
||||
if (!register.equals(language.getRegister(register.getName()))) {
|
||||
throw new IllegalArgumentException(
|
||||
"Register " + register + " is not in language " + language);
|
||||
}
|
||||
return readRegister(platform, requireThread(current.getThread()), current.getFrame(),
|
||||
current.getSnap(), register);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a register from the current context, refreshing from the target if needed
|
||||
*
|
||||
|
@ -1037,9 +1115,7 @@ public interface FlatDebuggerAPI {
|
|||
* @return the value, or null on error
|
||||
*/
|
||||
default RegisterValue readRegister(Register register) {
|
||||
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
|
||||
return readRegister(requireThread(current.getThread()), current.getFrame(),
|
||||
current.getSnap(), register);
|
||||
return readRegister(requireCurrentPlatform(), register);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1049,7 +1125,9 @@ public interface FlatDebuggerAPI {
|
|||
* @throws IllegalArgumentException if the name is invalid
|
||||
*/
|
||||
default RegisterValue readRegister(String name) {
|
||||
return readRegister(validateRegisterName(requireCurrentTrace().getBaseLanguage(), name));
|
||||
TracePlatform platform = requireCurrentPlatform();
|
||||
Register register = validateRegisterName(platform.getLanguage(), name);
|
||||
return readRegister(platform, register);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1059,10 +1137,10 @@ public interface FlatDebuggerAPI {
|
|||
* @return the program counter, or null if not known
|
||||
*/
|
||||
default Address getProgramCounter(DebuggerCoordinates coordinates) {
|
||||
Language language = requireTrace(coordinates.getTrace()).getBaseLanguage();
|
||||
RegisterValue value =
|
||||
readRegister(requireThread(coordinates.getThread()), coordinates.getFrame(),
|
||||
coordinates.getSnap(), language.getProgramCounter());
|
||||
TracePlatform platform = requirePlatform(coordinates.getPlatform());
|
||||
Language language = platform.getLanguage();
|
||||
RegisterValue value = readRegister(platform, requireThread(coordinates.getThread()),
|
||||
coordinates.getFrame(), coordinates.getSnap(), language.getProgramCounter());
|
||||
if (!value.hasValue()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1085,10 +1163,10 @@ public interface FlatDebuggerAPI {
|
|||
* @return the stack pointer, or null if not known
|
||||
*/
|
||||
default Address getStackPointer(DebuggerCoordinates coordinates) {
|
||||
CompilerSpec cSpec = requireTrace(coordinates.getTrace()).getBaseCompilerSpec();
|
||||
RegisterValue value =
|
||||
readRegister(requireThread(coordinates.getThread()), coordinates.getFrame(),
|
||||
coordinates.getSnap(), cSpec.getStackPointer());
|
||||
TracePlatform platform = requirePlatform(coordinates.getPlatform());
|
||||
CompilerSpec cSpec = platform.getCompilerSpec();
|
||||
RegisterValue value = readRegister(platform, requireThread(coordinates.getThread()),
|
||||
coordinates.getFrame(), coordinates.getSnap(), cSpec.getStackPointer());
|
||||
if (!value.hasValue()) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -605,18 +605,27 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
|
|||
modelService.addModel(mb.testModel);
|
||||
}
|
||||
|
||||
protected TraceRecorder recordAndWaitSync() throws Throwable {
|
||||
createTestModel();
|
||||
protected void populateTestModel() throws Throwable {
|
||||
mb.createTestProcessesAndThreads();
|
||||
mb.createTestThreadRegisterBanks();
|
||||
// NOTE: Test mapper uses TOYBE64
|
||||
mb.testProcess1.regs.addRegistersFromLanguage(getToyBE64Language(),
|
||||
Register::isBaseRegister);
|
||||
mb.createTestThreadRegisterBanks();
|
||||
mb.testProcess1.addRegion(".text", mb.rng(0x00400000, 0x00401000), "rx");
|
||||
mb.testProcess1.addRegion(".data", mb.rng(0x00600000, 0x00601000), "rw");
|
||||
}
|
||||
|
||||
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
|
||||
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
|
||||
protected TargetObject chooseTarget() {
|
||||
return mb.testProcess1;
|
||||
}
|
||||
|
||||
protected TraceRecorder recordAndWaitSync() throws Throwable {
|
||||
createTestModel();
|
||||
populateTestModel();
|
||||
|
||||
TargetObject target = chooseTarget();
|
||||
TraceRecorder recorder = modelService.recordTarget(target,
|
||||
createTargetTraceMapper(target), ActionSource.AUTOMATIC);
|
||||
|
||||
waitRecorder(recorder);
|
||||
return recorder;
|
||||
|
|
|
@ -1272,7 +1272,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
.createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff),
|
||||
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
|
||||
thread1 = tb.getOrAddThread("Thread1", 0);
|
||||
tb.exec(0, 0, thread1, "RIP = 0x00400000;");
|
||||
tb.exec(0, thread1, 0, "RIP = 0x00400000;");
|
||||
}
|
||||
|
||||
TraceThread thread2;
|
||||
|
@ -1282,7 +1282,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
.createRegion(".text", 0, tb2.range(0x200, 0x3ff), TraceMemoryFlag.READ,
|
||||
TraceMemoryFlag.EXECUTE);
|
||||
thread2 = tb2.getOrAddThread("Thread2", 0);
|
||||
tb2.exec(0, 0, thread2, "PC = 0x100;");
|
||||
tb2.exec(0, thread2, 0, "PC = 0x100;");
|
||||
}
|
||||
|
||||
traceManager.openTrace(tb.trace);
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.register;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.mapping.ObjectBasedDebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
|
||||
public class DebuggerRegistersProviderGuestTest extends DebuggerRegistersProviderTest {
|
||||
|
||||
protected TraceGuestPlatform toy;
|
||||
|
||||
@Override
|
||||
protected void createTrace() throws IOException {
|
||||
createTrace("DATA:BE:64:default");
|
||||
}
|
||||
|
||||
public void createToyPlatform() throws Exception {
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
toy = tb.trace.getPlatformManager()
|
||||
.addGuestPlatform(getToyBE64Language().getDefaultCompilerSpec());
|
||||
toy.addMappedRange(tb.addr(0), tb.addr(toy, 0), -1);
|
||||
toy.addMappedRegisterRange();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUpRegistersProviderTest() throws Exception {
|
||||
registersPlugin = addPlugin(tool, DebuggerRegistersPlugin.class);
|
||||
registersProvider = waitForComponentProvider(DebuggerRegistersProvider.class);
|
||||
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
|
||||
editingService = addPlugin(tool, DebuggerStateEditingServicePlugin.class);
|
||||
|
||||
createTrace();
|
||||
createToyPlatform();
|
||||
|
||||
r0 = tb.reg(toy, "r0");
|
||||
pc = toy.getLanguage().getProgramCounter();
|
||||
sp = toy.getCompilerSpec().getStackPointer();
|
||||
contextreg = toy.getLanguage().getContextBaseRegister();
|
||||
|
||||
pch = tb.reg(toy, "pch");
|
||||
pcl = tb.reg(toy, "pcl");
|
||||
|
||||
r0h = tb.reg(toy, "r0h");
|
||||
r0l = tb.reg(toy, "r0l");
|
||||
|
||||
r0Struct = new StructureDataType("r0_struct", 0);
|
||||
r0Struct.add(SignedDWordDataType.dataType, "hi", "");
|
||||
r0Struct.add(DWordDataType.dataType, "lo", "");
|
||||
|
||||
baseRegs = toy.getLanguage()
|
||||
.getRegisters()
|
||||
.stream()
|
||||
.filter(Register::isBaseRegister)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TargetObject chooseTarget() {
|
||||
return mb.testModel.session;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerTargetTraceMapper createTargetTraceMapper(TargetObject target)
|
||||
throws Exception {
|
||||
return new ObjectBasedDebuggerTargetTraceMapper(target,
|
||||
new LanguageID("DATA:BE:64:default"), new CompilerSpecID("pointer64"), Set.of()) {
|
||||
@Override
|
||||
public TraceRecorder startRecording(DebuggerModelServicePlugin service,
|
||||
Trace trace) {
|
||||
useTrace(trace);
|
||||
return super.startRecording(service, trace);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceRecorder recordAndWaitSync() throws Throwable {
|
||||
TraceRecorder recorder = super.recordAndWaitSync();
|
||||
createToyPlatform();
|
||||
return recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TracePlatform getPlatform() {
|
||||
return toy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activateThread(TraceThread thread) {
|
||||
traceManager.activate(traceManager.resolveThread(thread).platform(toy));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addRegisterValues(TraceThread thread, UndoableTransaction tid) {
|
||||
TraceMemorySpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
|
||||
regVals.putBytes(toy, 0, pc, tb.buf(0, 0, 0, 0, 0, 0x40, 0, 0));
|
||||
regVals.putBytes(toy, 0, sp, tb.buf(0x1f, 0, 0, 0, 0, 0, 0, 0));
|
||||
regVals.putBytes(toy, 0, r0, tb.buf(1, 2, 3, 4, 5, 6, 7, 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addRegisterTypes(TraceThread thread, UndoableTransaction tid)
|
||||
throws CodeUnitInsertionException {
|
||||
TraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
|
||||
regCode.definedData().create(toy, Range.atLeast(0L), pc, PointerDataType.dataType);
|
||||
// TODO: Pointer needs to be to ram, not register space
|
||||
regCode.definedData().create(toy, Range.atLeast(0L), r0, r0Struct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testDefaultSelection() throws Exception {
|
||||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
traceManager.activate(traceManager.resolveThread(thread).platform(toy));
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(DebuggerRegistersProvider.collectCommonRegisters(toy.getCompilerSpec()),
|
||||
registersProvider.getSelectionFor(toy));
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
|||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.listing.DBTraceCodeSpace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
|
@ -115,6 +116,14 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
}
|
||||
}
|
||||
|
||||
protected TracePlatform getPlatform() {
|
||||
return tb.host;
|
||||
}
|
||||
|
||||
protected void activateThread(TraceThread thread) {
|
||||
traceManager.activateThread(thread);
|
||||
}
|
||||
|
||||
protected void addRegisterValues(TraceThread thread) {
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
addRegisterValues(thread, tid);
|
||||
|
@ -252,7 +261,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertPCRowValueEmpty();
|
||||
|
@ -269,12 +278,12 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
// TODO: Use another language to test effect of recorded non-common registers
|
||||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(
|
||||
DebuggerRegistersProvider.collectCommonRegisters(tb.trace.getBaseCompilerSpec()),
|
||||
registersProvider.getSelectionFor(thread));
|
||||
registersProvider.getSelectionFor(tb.host));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -283,7 +292,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
|
||||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
assertPCRowValuePopulated();
|
||||
|
@ -299,7 +308,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
mb.testBank1.writeRegister("pc", new byte[] { 0x00, 0x40, 0x00, 0x00 });
|
||||
waitForSwing();
|
||||
|
||||
traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
waitForSwing();
|
||||
|
||||
assertPCRowValuePopulated();
|
||||
|
@ -309,7 +318,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
public void testLiveActivateThenAddValuesPopulatesPanel() throws Throwable {
|
||||
TraceRecorder recorder = recordAndWaitSync();
|
||||
traceManager.openTrace(recorder.getTrace());
|
||||
traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
waitForSwing();
|
||||
|
||||
mb.testBank1.writeRegister("pc", new byte[] { 0x00, 0x40, 0x00, 0x00 });
|
||||
|
@ -326,7 +335,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
addRegisterValues(thread);
|
||||
|
@ -343,7 +352,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
addRegisterTypes(thread);
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertPCRowTypePopulated();
|
||||
|
@ -355,7 +364,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
|
||||
addRegisterValues(thread);
|
||||
addRegisterTypes(thread);
|
||||
|
@ -373,7 +382,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
|
@ -395,7 +404,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
assertEquals(BigInteger.valueOf(0x1234),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
regVals.getValue(getPlatform(), viewSnap, r0).getUnsignedValue());
|
||||
assertEquals(BigInteger.valueOf(0x1234), row.getValue());
|
||||
});
|
||||
}
|
||||
|
@ -411,7 +420,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
|
@ -437,7 +446,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
assertEquals(BigInteger.valueOf(encodeDouble(1234)),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
regVals.getValue(getPlatform(), viewSnap, r0).getUnsignedValue());
|
||||
assertEquals(BigInteger.valueOf(encodeDouble(1234)), row.getValue());
|
||||
});
|
||||
}
|
||||
|
@ -449,7 +458,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
RegisterRow row = findRegisterRow(pc);
|
||||
|
@ -459,7 +468,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
DBTraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
|
||||
assertNotNull(regCode);
|
||||
TraceData data = regCode.data().getForRegister(0L, pc);
|
||||
TraceData data = regCode.data().getForRegister(getPlatform(), 0L, pc);
|
||||
assertTypeEquals(PointerDataType.dataType, data.getDataType());
|
||||
}
|
||||
|
||||
|
@ -469,9 +478,9 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
|
||||
TraceThread thread = addThread();
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
tb.exec(0, 0, thread, "pc = 100;");
|
||||
tb.exec(getPlatform(), 0, thread, 0, "pc = 100;");
|
||||
}
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
RegisterRow row = findRegisterRow(pc);
|
||||
|
@ -481,7 +490,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
DBTraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
|
||||
assertNotNull(regCode);
|
||||
TraceData data = regCode.data().getForRegister(0L, pc);
|
||||
TraceData data = regCode.data().getForRegister(getPlatform(), 0L, pc);
|
||||
assertTypeEquals(LongLongDataType.dataType, data.getDataType());
|
||||
assertEquals("64h", row.getRepresentation());
|
||||
|
||||
|
@ -505,7 +514,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
RegisterRow rowL = findRegisterRow(r0l);
|
||||
|
@ -518,10 +527,11 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
|
||||
assertNotNull(regCode);
|
||||
// It's two units, not a struct with two components
|
||||
assertNull(regCode.data().getForRegister(0L, r0));
|
||||
TraceData dataL = regCode.data().getForRegister(0L, r0l);
|
||||
TracePlatform platform = getPlatform();
|
||||
assertNull(regCode.data().getForRegister(platform, 0L, r0));
|
||||
TraceData dataL = regCode.data().getForRegister(platform, 0L, r0l);
|
||||
assertTypeEquals(PointerDataType.dataType, dataL.getDataType());
|
||||
TraceData dataH = regCode.data().getForRegister(0L, r0h);
|
||||
TraceData dataH = regCode.data().getForRegister(platform, 0L, r0h);
|
||||
assertTypeEquals(PointerDataType.dataType, dataH.getDataType());
|
||||
}
|
||||
|
||||
|
@ -530,7 +540,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
// Group adds into a single transaction
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
addRegisterValues(thread, tid);
|
||||
|
@ -564,7 +574,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
addRegisterValues(thread, tid);
|
||||
|
@ -595,7 +605,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
|
||||
assertFalse(registersProvider.actionEnableEdits.isEnabled());
|
||||
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(registersProvider.actionEnableEdits.isEnabled());
|
||||
|
@ -613,7 +623,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
|
||||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
assertEquals(0, traceManager.getCurrentSnap());
|
||||
|
@ -625,8 +635,9 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
|
||||
TraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
|
||||
regVals.putBytes(1, r0, tb.buf(1, 1, 2, 2, 3, 3, 4, 4));
|
||||
regCode.definedData().create(Range.atLeast(1L), r0, r0Struct);
|
||||
TracePlatform platform = getPlatform();
|
||||
regVals.putBytes(platform, 1, r0, tb.buf(1, 1, 2, 2, 3, 3, 4, 4));
|
||||
regCode.definedData().create(platform, Range.atLeast(1L), r0, r0Struct);
|
||||
}
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
|
@ -641,7 +652,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
addRegisterTypes(thread);
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
traceManager.activateSnap(1);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
|
@ -651,7 +662,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
TraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
|
||||
TraceCodeUnit code = regCode.codeUnits().getContaining(1, r0);
|
||||
TraceCodeUnit code = regCode.codeUnits().getContaining(getPlatform(), 1, r0);
|
||||
code.setEndSnap(0);
|
||||
}
|
||||
waitForDomainObject(tb.trace);
|
||||
|
@ -673,7 +684,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
TraceThread thread = addThread();
|
||||
addRegisterValues(thread);
|
||||
addRegisterTypes(thread);
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
assertR0RowTypePopulated();
|
||||
|
@ -681,7 +692,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
TraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
|
||||
TraceCodeUnit code = regCode.codeUnits().getContaining(1, r0);
|
||||
TraceCodeUnit code = regCode.codeUnits().getContaining(getPlatform(), 1, r0);
|
||||
code.delete();
|
||||
}
|
||||
waitForDomainObject(tb.trace);
|
||||
|
@ -702,7 +713,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
|
||||
assertFalse(registersProvider.actionCreateSnapshot.isEnabled());
|
||||
|
||||
traceManager.activateThread(thread1);
|
||||
activateThread(thread1);
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(registersProvider.actionCreateSnapshot.isEnabled());
|
||||
|
@ -716,7 +727,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
assertEquals("[Registers]", cloned.getTitle());
|
||||
assertEquals("Thread1", cloned.getSubTitle());
|
||||
|
||||
traceManager.activateThread(thread2);
|
||||
activateThread(thread2);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(thread2, registersProvider.current.getThread());
|
||||
|
@ -757,7 +768,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
// Ensure cause is goto PC, not register tracking
|
||||
listingPlugin.setTrackingSpec(
|
||||
LocationTrackingSpec.fromConfigName(NoneLocationTrackingSpec.CONFIG_NAME));
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertPCRowTypePopulated();
|
||||
|
@ -784,7 +795,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
|
||||
assertFalse(registersProvider.actionSelectRegisters.isEnabled());
|
||||
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(registersProvider.regsTableModel.getRowIndex(findRegisterRow(pc)) >= 0);
|
||||
|
@ -823,21 +834,21 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
TraceThread thread1 = addThread();
|
||||
TraceThread thread2 = addThread("Thread2");
|
||||
addRegisterValues(thread1);
|
||||
traceManager.activateThread(thread2);
|
||||
activateThread(thread2);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(thread2, registersProvider.current.getThread());
|
||||
assertPCRowValueEmpty();
|
||||
|
||||
// Should have no effect
|
||||
traceManager.activateThread(thread2);
|
||||
activateThread(thread2);
|
||||
waitForSwing();
|
||||
|
||||
assertPCRowValueEmpty();
|
||||
assertEquals(thread2, registersProvider.current.getThread());
|
||||
|
||||
// Should have effect
|
||||
traceManager.activateThread(thread1);
|
||||
activateThread(thread1);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(thread1, registersProvider.current.getThread());
|
||||
|
@ -867,7 +878,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
assertPCRowValueEmpty();
|
||||
|
||||
// Should just work
|
||||
traceManager.activateThread(thread1);
|
||||
activateThread(thread1);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(thread1, registersProvider.current.getThread());
|
||||
|
@ -880,7 +891,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
addRegisterValues(thread);
|
||||
|
@ -895,9 +906,9 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
|
||||
TraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
|
||||
regVals.putBytes(10, r0, tb.buf(0, 0, 0, 0, 0, 0, 0, 0));
|
||||
regVals.putBytes(getPlatform(), 10, r0, tb.buf(0, 0, 0, 0, 0, 0, 0, 0));
|
||||
// NB. the manager should have split the data unit at the value change
|
||||
TraceCodeUnit cu = regCode.codeUnits().getContaining(10, r0);
|
||||
TraceCodeUnit cu = regCode.codeUnits().getContaining(getPlatform(), 10, r0);
|
||||
assertNotNull(cu);
|
||||
assertEquals(10, cu.getStartSnap());
|
||||
cu.delete();
|
||||
|
@ -922,7 +933,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
addRegisterValues(thread);
|
||||
|
@ -935,11 +946,12 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
TraceMemorySpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, 1, true);
|
||||
regVals.putBytes(0, pc, tb.buf(0, 0, 0, 0, 0, 0x50, 0, 0));
|
||||
regVals.putBytes(getPlatform(), 0, pc, tb.buf(0, 0, 0, 0, 0, 0x50, 0, 0));
|
||||
|
||||
TraceCodeSpace regCode =
|
||||
tb.trace.getCodeManager().getCodeRegisterSpace(thread, 1, true);
|
||||
regCode.definedData().create(Range.atLeast(0L), pc, QWordDataType.dataType);
|
||||
regCode.definedData()
|
||||
.create(getPlatform(), Range.atLeast(0L), pc, QWordDataType.dataType);
|
||||
}
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.junit.Test;
|
|||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.app.services.ActionSource;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.model.TestTargetRegister;
|
||||
import ghidra.dbg.model.*;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.util.CollectionUtils.Delta;
|
||||
import ghidra.program.model.lang.*;
|
||||
|
@ -54,6 +54,18 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
}
|
||||
}
|
||||
|
||||
protected static void assertSameRegister(TargetRegister expected, TargetObject actual) {
|
||||
if (actual instanceof TestTargetRegister tr) {
|
||||
assertEquals(expected, tr);
|
||||
}
|
||||
else if (actual instanceof TestTargetRegisterValue rv) {
|
||||
assertEquals(expected, rv.desc);
|
||||
}
|
||||
else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpMapperTest() throws Throwable {
|
||||
createTestModel();
|
||||
|
@ -98,8 +110,8 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
TestTargetRegister tEAX =
|
||||
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("EAX"));
|
||||
|
||||
assertEquals(tRAX, waitForValue(() -> rm.getTargetRegister("rax")));
|
||||
assertEquals(tEAX, waitForValue(() -> rm.getTargetRegister("eax"))); // Seems reasonable
|
||||
assertSameRegister(tRAX, waitForValue(() -> rm.getTargetRegister("rax")));
|
||||
assertSameRegister(tEAX, waitForValue(() -> rm.getTargetRegister("eax"))); // Seems reasonable
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -130,7 +142,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
Register lRAX = Objects.requireNonNull(getSLEIGH_X86_64_LANGUAGE().getRegister("RAX"));
|
||||
|
||||
TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lRAX));
|
||||
assertEquals(tRAX, tReg);
|
||||
assertSameRegister(tRAX, tReg);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -175,7 +187,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
TestTargetRegister tEAX =
|
||||
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("EAX"));
|
||||
|
||||
assertEquals(tEAX, waitForValue(() -> rm.getTargetRegister("eax")));
|
||||
assertSameRegister(tEAX, waitForValue(() -> rm.getTargetRegister("eax")));
|
||||
assertNull(rm.getTargetRegister("rax"));
|
||||
}
|
||||
|
||||
|
@ -211,7 +223,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
Register lRAX = Objects.requireNonNull(getSLEIGH_X86_64_LANGUAGE().getRegister("RAX"));
|
||||
|
||||
TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lRAX));
|
||||
assertEquals(tEAX, tReg);
|
||||
assertSameRegister(tEAX, tReg);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -256,7 +268,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
// NOTE: This is not allowed, but still generates a courtesy warning
|
||||
assertNull(rm.targetToTrace("eax", genBytes4()));
|
||||
|
||||
Delta<?, ?> delta = mb.testProcess1.regs.changeElements(List.of("RAX"), List.of(), "WoW64");
|
||||
Delta<?, ?> delta = mb.testProcess1.regs.removeRegister(lRAX, "WoW64");
|
||||
assertFalse(delta.removed.isEmpty());
|
||||
waitForPass(() -> assertNull(rm.getTargetRegister("rax")));
|
||||
|
||||
|
@ -287,7 +299,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
|
|||
assertEquals("RAX", ent.getKey());
|
||||
assertArrayEquals(genBytes8(), ent.getValue());
|
||||
|
||||
Delta<?, ?> delta = mb.testProcess1.regs.changeElements(List.of("RAX"), List.of(), "WoW64");
|
||||
Delta<?, ?> delta = mb.testProcess1.regs.removeRegister(lRAX, "WoW64");
|
||||
assertFalse(delta.removed.isEmpty());
|
||||
waitForPass(() -> assertNull(rm.getTargetRegister("rax")));
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.service.editing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.mapping.ObjectBasedDebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class DebuggerStateEditingServiceGuestTest extends DebuggerStateEditingServiceTest {
|
||||
protected TraceGuestPlatform platform;
|
||||
|
||||
public void createToyPlatform() {
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
platform = tb.trace.getPlatformManager()
|
||||
.addGuestPlatform(getToyBE64Language().getDefaultCompilerSpec());
|
||||
platform.addMappedRegisterRange();
|
||||
platform.addMappedRange(tb.addr(0), tb.addr(platform, 0), -1);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createAndOpenTrace() throws IOException {
|
||||
createAndOpenTrace("DATA:BE:64:default");
|
||||
createToyPlatform();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activateTrace() {
|
||||
traceManager.activatePlatform(platform);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TracePlatform getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TargetObject chooseTarget() {
|
||||
return mb.testModel.session;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerTargetTraceMapper createTargetTraceMapper(TargetObject target)
|
||||
throws Exception {
|
||||
return new ObjectBasedDebuggerTargetTraceMapper(target,
|
||||
new LanguageID("DATA:BE:64:default"), new CompilerSpecID("pointer64"), Set.of()) {
|
||||
@Override
|
||||
public TraceRecorder startRecording(DebuggerModelServicePlugin service,
|
||||
Trace trace) {
|
||||
useTrace(trace);
|
||||
return super.startRecording(service, trace);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceRecorder recordAndWaitSync() throws Throwable {
|
||||
TraceRecorder recorder = super.recordAndWaitSync();
|
||||
createToyPlatform();
|
||||
return recorder;
|
||||
}
|
||||
}
|
|
@ -23,8 +23,7 @@ import java.nio.ByteBuffer;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.plugin.assembler.Assembler;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.app.services.DebuggerStateEditingService;
|
||||
|
@ -32,24 +31,35 @@ import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
|
|||
import ghidra.app.services.DebuggerStateEditingService.StateEditor;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.pcode.exec.DebuggerPcodeUtils;
|
||||
import ghidra.pcode.exec.PcodeExecutor;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
private DebuggerStateEditingService editingService;
|
||||
protected DebuggerStateEditingService editingService;
|
||||
|
||||
private Register r0;
|
||||
private Register r0h;
|
||||
private RegisterValue rv1234;
|
||||
private RegisterValue rv5678;
|
||||
private RegisterValue rvHigh1234;
|
||||
protected Register r0;
|
||||
protected Register r0h;
|
||||
protected RegisterValue rv1234;
|
||||
protected RegisterValue rv5678;
|
||||
protected RegisterValue rvHigh1234;
|
||||
|
||||
protected StateEditor createStateEditor() {
|
||||
return editingService.createStateEditor(tb.trace);
|
||||
}
|
||||
|
||||
protected void activateTrace() {
|
||||
traceManager.activateTrace(tb.trace);
|
||||
}
|
||||
|
||||
protected TracePlatform getPlatform() {
|
||||
return tb.trace.getPlatformManager().getHostPlatform();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpEditorTest() throws Exception {
|
||||
|
@ -71,9 +81,11 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
* only work if they don't refer to any register.
|
||||
*/
|
||||
createAndOpenTrace();
|
||||
activateTrace();
|
||||
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
}
|
||||
|
||||
|
@ -82,7 +94,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
createAndOpenTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
}
|
||||
|
||||
|
@ -95,9 +110,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. TraceManager should automatically activate the first thread
|
||||
tb.getOrAddThread("Threads[0]", 0);
|
||||
}
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
waitForSwing();
|
||||
|
||||
|
@ -120,9 +136,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. TraceManager should automatically activate the first thread
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
}
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
waitForSwing();
|
||||
|
||||
|
@ -131,7 +148,9 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
assertTrue(DBTraceUtils.isScratch(snap));
|
||||
|
||||
RegisterValue value =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0);
|
||||
tb.trace.getMemoryManager()
|
||||
.getMemoryRegisterSpace(thread, false)
|
||||
.getValue(getPlatform(), snap, r0);
|
||||
assertEquals(rv1234, value);
|
||||
}
|
||||
|
||||
|
@ -143,14 +162,14 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
// NB. TraceManager should automatically activate the first thread
|
||||
TraceThread thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(
|
||||
env.getTool(), DebuggerCoordinates.NOWHERE.thread(thread));
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
|
||||
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
|
||||
executor.executeSleigh("pc = 0x00400000;");
|
||||
Assembler asm = Assemblers.getAssembler(getPlatform().getLanguage());
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, tb.addr(getPlatform(), 0x00400000));
|
||||
buf.assemble("imm r0,#123");
|
||||
tb.trace.getMemoryManager()
|
||||
.putBytes(0, tb.addr(0x00400000), ByteBuffer.wrap(buf.getBytes()));
|
||||
tb.exec(getPlatform(), 0, thread, 0, "pc = 0x00400000;");
|
||||
}
|
||||
traceManager.activateTrace(tb.trace);
|
||||
activateTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
waitForSwing();
|
||||
|
||||
|
@ -158,7 +177,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
traceManager.activateTime(step1);
|
||||
waitForPass(() -> assertEquals(step1, traceManager.getCurrent().getTime()));
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00600000), tb.arr(1, 2, 3, 4)));
|
||||
waitForSwing();
|
||||
|
||||
|
@ -181,14 +200,14 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
// NB. TraceManager should automatically activate the first thread
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(
|
||||
env.getTool(), DebuggerCoordinates.NOWHERE.thread(thread));
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
|
||||
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
|
||||
executor.executeSleigh("pc = 0x00400000;");
|
||||
Assembler asm = Assemblers.getAssembler(getPlatform().getLanguage());
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, tb.addr(getPlatform(), 0x00400000));
|
||||
buf.assemble("imm r0,#123");
|
||||
tb.trace.getMemoryManager()
|
||||
.putBytes(0, tb.addr(0x00400000), ByteBuffer.wrap(buf.getBytes()));
|
||||
tb.exec(getPlatform(), 0, thread, 0, "pc = 0x00400000;");
|
||||
}
|
||||
traceManager.activateTrace(tb.trace);
|
||||
activateTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
waitForSwing();
|
||||
|
||||
|
@ -196,7 +215,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
traceManager.activateTime(step1);
|
||||
waitForPass(() -> assertEquals(step1, traceManager.getCurrent().getTime()));
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
waitForSwing();
|
||||
|
||||
|
@ -205,8 +224,9 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
long snap = current.getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(snap));
|
||||
|
||||
RegisterValue value =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0);
|
||||
RegisterValue value = tb.trace.getMemoryManager()
|
||||
.getMemoryRegisterSpace(thread, false)
|
||||
.getValue(getPlatform(), snap, r0);
|
||||
assertEquals(rv1234, value);
|
||||
}
|
||||
|
||||
|
@ -219,9 +239,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. TraceManager should automatically activate the first thread
|
||||
tb.getOrAddThread("Threads[0]", 0);
|
||||
}
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
waitOn(editor.setVariable(tb.addr(0x00400002), tb.arr(5, 6, 7, 8)));
|
||||
waitForSwing();
|
||||
|
@ -246,9 +267,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. TraceManager should automatically activate the first thread
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
}
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
waitOn(editor.setRegister(rv5678));
|
||||
waitForSwing();
|
||||
|
@ -258,8 +280,9 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
assertTrue(DBTraceUtils.isScratch(snap));
|
||||
assertEquals(1, current.getTime().patchCount()); // Check coalesced
|
||||
|
||||
RegisterValue value =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0);
|
||||
RegisterValue value = tb.trace.getMemoryManager()
|
||||
.getMemoryRegisterSpace(thread, false)
|
||||
.getValue(getPlatform(), snap, r0);
|
||||
assertEquals(rv5678, value);
|
||||
}
|
||||
|
||||
|
@ -268,8 +291,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. Definitely no thread required
|
||||
createAndOpenTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE);
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
// NB. Editor creates its own transaction
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
waitForSwing();
|
||||
|
@ -288,8 +313,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. Definitely no thread required
|
||||
createAndOpenTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE);
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
// NB. Editor creates its own transaction
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
}
|
||||
|
@ -305,9 +332,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
// NB. TraceManager should automatically activate the first thread
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
}
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
// NB. Editor creates its own transaction
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
waitForSwing();
|
||||
|
@ -316,20 +344,22 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
long snap = current.getViewSnap();
|
||||
assertEquals(0, snap);
|
||||
|
||||
RegisterValue value =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0);
|
||||
RegisterValue value = tb.trace.getMemoryManager()
|
||||
.getMemoryRegisterSpace(thread, false)
|
||||
.getValue(getPlatform(), snap, r0);
|
||||
assertEquals(rv1234, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteTargetMemory() throws Throwable {
|
||||
TraceRecorder recorder = recordAndWaitSync();
|
||||
traceManager.openTrace(recorder.getTrace());
|
||||
traceManager.openTrace(tb.trace);
|
||||
activateTrace();
|
||||
traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
waitForSwing();
|
||||
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
|
||||
assertArrayEquals(mb.arr(1, 2, 3, 4),
|
||||
|
@ -341,12 +371,14 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
TraceRecorder recorder = recordAndWaitSync();
|
||||
TargetRegisterBank bank =
|
||||
(TargetRegisterBank) mb.testThread1.getCachedAttribute("RegisterBank");
|
||||
traceManager.openTrace(recorder.getTrace());
|
||||
traceManager.openTrace(tb.trace);
|
||||
activateTrace();
|
||||
waitForSwing();
|
||||
traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
waitForSwing();
|
||||
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
|
||||
assertArrayEquals(mb.arr(0, 0, 0, 0, 0, 0, 4, 0xd2), waitOn(bank.readRegister("r0")));
|
||||
|
@ -357,19 +389,20 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
TraceRecorder recorder = recordAndWaitSync();
|
||||
TargetRegisterBank bank =
|
||||
(TargetRegisterBank) mb.testThread1.getCachedAttribute("RegisterBank");
|
||||
traceManager.openTrace(recorder.getTrace());
|
||||
traceManager.openTrace(tb.trace);
|
||||
activateTrace();
|
||||
TraceThread thread = recorder.getTraceThread(mb.testThread1);
|
||||
traceManager.activateThread(thread);
|
||||
waitForSwing();
|
||||
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
waitForPass(() -> {
|
||||
TraceMemorySpace regs =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
assertNotNull(regs);
|
||||
RegisterValue value = regs.getValue(traceManager.getCurrentSnap(), r0);
|
||||
RegisterValue value = regs.getValue(getPlatform(), traceManager.getCurrentSnap(), r0);
|
||||
assertEquals(rv1234, value);
|
||||
});
|
||||
waitOn(editor.setRegister(rvHigh1234));
|
||||
|
@ -380,64 +413,70 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
@Test(expected = MemoryAccessException.class)
|
||||
public void testWriteTargetMemoryNotPresentErr() throws Throwable {
|
||||
TraceRecorder recorder = recordAndWaitSync();
|
||||
traceManager.openTrace(recorder.getTrace());
|
||||
traceManager.openTrace(tb.trace);
|
||||
activateTrace();
|
||||
traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
waitForSwing();
|
||||
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
|
||||
|
||||
traceManager.activateSnap(traceManager.getCurrentSnap() - 1);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
}
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void testWriteTargetRegisterNotPresentErr() throws Throwable {
|
||||
TraceRecorder recorder = recordAndWaitSync();
|
||||
traceManager.openTrace(recorder.getTrace());
|
||||
traceManager.openTrace(tb.trace);
|
||||
activateTrace();
|
||||
traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
|
||||
waitForSwing();
|
||||
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
|
||||
|
||||
traceManager.activateSnap(traceManager.getCurrentSnap() - 1);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
}
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void testWriteTargetMemoryNotAliveErr() throws Throwable {
|
||||
createAndOpenTrace();
|
||||
activateTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TARGET);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
}
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void testWriteTargetRegisterNotAliveErr() throws Throwable {
|
||||
createAndOpenTrace();
|
||||
activateTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TARGET);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
}
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void testWriteReadOnlyMemoryErr() throws Throwable {
|
||||
createAndOpenTrace();
|
||||
activateTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.READ_ONLY);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
|
||||
}
|
||||
|
||||
@Test(expected = MemoryAccessException.class)
|
||||
public void testWriteReadOnlyRegisterErr() throws Throwable {
|
||||
createAndOpenTrace();
|
||||
activateTrace();
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.READ_ONLY);
|
||||
|
||||
StateEditor editor = editingService.createStateEditor(tb.trace);
|
||||
StateEditor editor = createStateEditor();
|
||||
waitOn(editor.setRegister(rv1234));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
|||
}
|
||||
|
||||
long scratch =
|
||||
emulationPlugin.emulate(tb.trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
|
||||
emulationPlugin.emulate(platform, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
|
||||
TraceMemorySpace regs = mem.getMemoryRegisterSpace(thread, false);
|
||||
assertEquals("deadbeefcafebabe",
|
||||
regs.getViewValue(platform, scratch, tb.reg(platform, "RAX"))
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model.record;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.oneOf;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -39,6 +39,7 @@ import ghidra.dbg.target.TargetEventScope.TargetEventType;
|
|||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
|
@ -334,9 +335,9 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
|
|||
is(oneOf(null, TraceMemoryState.UNKNOWN)));
|
||||
|
||||
byte[] data = new byte[10];
|
||||
assertNull(waitOn(recorder.readMemoryBlocks(
|
||||
waitOn(recorder.readMemoryBlocks(
|
||||
tb.set(tb.range(0x00400123, 0x00400123), tb.range(0x00600ffe, 0x00601000)),
|
||||
TaskMonitor.DUMMY, false)));
|
||||
TaskMonitor.DUMMY));
|
||||
flushAndWait();
|
||||
assertEquals(Set.of(
|
||||
stateEntry(0x00400000, 0x00400fff, TraceMemoryState.KNOWN),
|
||||
|
@ -493,15 +494,22 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
|
|||
public void testRecordRegisters() throws Throwable {
|
||||
startRecording();
|
||||
mb.createTestProcessesAndThreads();
|
||||
// TODO: Adjust schema to reflect merging of container and bank
|
||||
// TODO: Other bank placements. Will need different schemas, though :/
|
||||
mb.createTestThreadRegisterBanks();
|
||||
|
||||
TestTargetRegisterValue targetPC = new TestTargetRegisterValue(mb.testBank1, "pc", true,
|
||||
BigInteger.valueOf(0x00400123), 8);
|
||||
Language toy = getToyBE64Language();
|
||||
mb.testProcess1.regs.addRegister(toy.getRegister("pc"));
|
||||
mb.testProcess1.regs.addRegister(toy.getRegister("r0"));
|
||||
TestTargetRegisterValue targetPC =
|
||||
(TestTargetRegisterValue) mb.testBank1.getCachedAttribute("pc");
|
||||
targetPC.changeAttributes(List.of(),
|
||||
Map.of(TargetRegister.VALUE_ATTRIBUTE_NAME, tb.arr(0, 0, 0, 0, 0, 0x40, 0x01, 0x23)),
|
||||
"Write PC=0x00400123");
|
||||
TestTargetRegisterValue targetR0 =
|
||||
new TestTargetRegisterValue(mb.testBank1, "r0", false, BigInteger.ZERO, 8);
|
||||
mb.testBank1.setElements(Set.of(targetPC, targetR0), "Test registers");
|
||||
(TestTargetRegisterValue) mb.testBank1.getCachedAttribute("r0");
|
||||
targetR0.changeAttributes(List.of(),
|
||||
Map.of(TargetRegister.VALUE_ATTRIBUTE_NAME, tb.arr(0, 0, 0, 0, 0, 0, 0, 0)),
|
||||
"Write R0=0x00000000");
|
||||
flushAndWait();
|
||||
|
||||
TraceObjectThread thread = (TraceObjectThread) recorder.getTraceThread(mb.testThread1);
|
||||
|
@ -517,18 +525,16 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
|
|||
.findAny()
|
||||
.orElseThrow();
|
||||
|
||||
TraceObject pc = traceBank.getElement(recorder.getSnap(), "pc").getChild();
|
||||
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0x40, 0x01, 0x023),
|
||||
TraceObject pc = traceBank.getAttribute(recorder.getSnap(), "pc").getChild();
|
||||
assertNotNull(pc);
|
||||
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0x40, 0x01, 0x23),
|
||||
(byte[]) pc.getAttribute(recorder.getSnap(), TargetObject.VALUE_ATTRIBUTE_NAME)
|
||||
.getValue());
|
||||
TraceObject r0 = traceBank.getElement(recorder.getSnap(), "r0").getChild();
|
||||
TraceObject r0 = traceBank.getAttribute(recorder.getSnap(), "r0").getChild();
|
||||
assertNotNull(r0);
|
||||
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
(byte[]) r0.getAttribute(recorder.getSnap(), TargetObject.VALUE_ATTRIBUTE_NAME)
|
||||
.getValue());
|
||||
// TODO: Test interpretation, once mapping scheme is worked out
|
||||
// TODO: How to annotate values with types, etc?
|
||||
// TODO: Perhaps byte-array values are allocated in memory-like byte store?
|
||||
// TODO: Brings endianness into the picture :/
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -31,7 +31,7 @@ import ghidra.dbg.util.PathUtils;
|
|||
public interface TargetRegister extends TargetObject {
|
||||
|
||||
String CONTAINER_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "container";
|
||||
String LENGTH_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "length";
|
||||
String BIT_LENGTH_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "length";
|
||||
|
||||
/**
|
||||
* Get the container of this register.
|
||||
|
@ -60,12 +60,25 @@ public interface TargetRegister extends TargetObject {
|
|||
* @return the length of the register
|
||||
*/
|
||||
@TargetAttributeType(
|
||||
name = LENGTH_ATTRIBUTE_NAME,
|
||||
name = BIT_LENGTH_ATTRIBUTE_NAME,
|
||||
required = true,
|
||||
fixed = true,
|
||||
hidden = true)
|
||||
default int getBitLength() {
|
||||
return getTypedAttributeNowByName(LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
|
||||
return getTypedAttributeNowByName(BIT_LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length, in bytes, of the register
|
||||
*
|
||||
* <p>
|
||||
* For registers whose bit lengths are not a multiple of 8, this should be the minimum number of
|
||||
* bytes required to bit all the bits, i.e., it should divide by 8 rounding up.
|
||||
*
|
||||
* @return the length of the register
|
||||
*/
|
||||
default int getByteLength() {
|
||||
return (getBitLength() + 7) / 8;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -317,12 +317,8 @@ public interface TargetObjectSchema {
|
|||
* @return the named schema
|
||||
*/
|
||||
default SchemaName getElementSchema(String index) {
|
||||
for (Entry<String, SchemaName> ent : getElementSchemas().entrySet()) {
|
||||
if (ent.getKey().equals(index)) {
|
||||
return ent.getValue();
|
||||
}
|
||||
}
|
||||
return getDefaultElementSchema();
|
||||
SchemaName schemaName = getElementSchemas().get(index);
|
||||
return schemaName == null ? getDefaultElementSchema() : schemaName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -365,12 +361,8 @@ public interface TargetObjectSchema {
|
|||
* @return the attribute schema
|
||||
*/
|
||||
default AttributeSchema getAttributeSchema(String name) {
|
||||
for (Entry<String, AttributeSchema> ent : getAttributeSchemas().entrySet()) {
|
||||
if (ent.getKey().equals(name)) {
|
||||
return ent.getValue();
|
||||
}
|
||||
}
|
||||
return getDefaultAttributeSchema();
|
||||
AttributeSchema attributeSchema = getAttributeSchemas().get(name);
|
||||
return attributeSchema == null ? getDefaultAttributeSchema() : attributeSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -935,46 +927,46 @@ public interface TargetObjectSchema {
|
|||
* This places some conventional restrictions / expectations on models where registers are given
|
||||
* on a frame-by-frame basis. The schema should present the {@link TargetRegisterContainer} as
|
||||
* the same object or a successor to {@link TargetStackFrame}, which must in turn be a successor
|
||||
* to {@link TargetThread}. The frame level (usually an index) must be in the path from thread
|
||||
* to stack frame. There can be no wild cards between the frame and the register container. For
|
||||
* example, the container for {@code Threads[1]} may be {@code Threads[1].Stack[n].Registers},
|
||||
* where {@code n} is the frame level. {@code Threads[1]} would have the {@link TargetThread}
|
||||
* to {@link TargetStack}. The frame level (an index) must be in the path from stack to frame.
|
||||
* There can be no wild cards between the frame and the register container. For example, the
|
||||
* container for {@code Threads[1]} may be {@code Threads[1].Stack[n].Registers}, where
|
||||
* {@code n} is the frame level. {@code Threads[1].Stack} would have the {@link TargetStack}
|
||||
* interface, {@code Threads[1].Stack[0]} would have the {@link TargetStackFrame} interface, and
|
||||
* {@code Threads[1].Stack[0].Registers} would have the {@link TargetRegisterContainer}
|
||||
* interface. Note it is not sufficient for {@link TargetRegisterContainer} to be a successor of
|
||||
* {@link TargetThread} with a single index between. There <em>must</em> be an intervening
|
||||
* {@link TargetStack} with a single index between. There <em>must</em> be an intervening
|
||||
* {@link TargetStackFrame}, and the frame level (index) must precede it.
|
||||
*
|
||||
* @param frameLevel the frameLevel, must be 0 if not applicable
|
||||
* @param frameLevel the frame level. May be ignored if not applicable
|
||||
* @path the path of the seed object relative to the root
|
||||
* @return the predicates where the register container should be found, possibly empty
|
||||
*/
|
||||
default PathPredicates searchForRegisterContainer(int frameLevel, List<String> path) {
|
||||
List<String> simple = searchForSuitable(TargetRegisterContainer.class, path);
|
||||
if (simple != null) {
|
||||
return frameLevel == 0 ? PathPredicates.pattern(simple) : PathPredicates.EMPTY;
|
||||
return PathPredicates.pattern(simple);
|
||||
}
|
||||
List<String> threadPath = searchForAncestor(TargetThread.class, path);
|
||||
if (threadPath == null) {
|
||||
List<String> stackPath = searchForSuitable(TargetStack.class, path);
|
||||
if (stackPath == null) {
|
||||
return PathPredicates.EMPTY;
|
||||
}
|
||||
PathPattern framePatternRelThread =
|
||||
getSuccessorSchema(threadPath).searchFor(TargetStackFrame.class, false)
|
||||
PathPattern framePatternRelStack =
|
||||
getSuccessorSchema(stackPath).searchFor(TargetStackFrame.class, false)
|
||||
.getSingletonPattern();
|
||||
if (framePatternRelThread == null) {
|
||||
if (framePatternRelStack == null) {
|
||||
return PathPredicates.EMPTY;
|
||||
}
|
||||
|
||||
if (framePatternRelThread.countWildcards() != 1) {
|
||||
if (framePatternRelStack.countWildcards() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PathMatcher result = new PathMatcher();
|
||||
for (String index : List.of(Integer.toString(frameLevel),
|
||||
"0x" + Integer.toHexString(frameLevel))) {
|
||||
List<String> framePathRelThread =
|
||||
framePatternRelThread.applyKeys(index).getSingletonPath();
|
||||
List<String> framePath = PathUtils.extend(threadPath, framePathRelThread);
|
||||
List<String> framePathRelStack =
|
||||
framePatternRelStack.applyKeys(index).getSingletonPath();
|
||||
List<String> framePath = PathUtils.extend(stackPath, framePathRelStack);
|
||||
List<String> regsPath =
|
||||
searchForSuitable(TargetRegisterContainer.class, framePath);
|
||||
if (regsPath != null) {
|
||||
|
@ -983,4 +975,36 @@ public interface TargetObjectSchema {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the frame level of the object at the given path relative to this schema
|
||||
*
|
||||
* <p>
|
||||
* If there is no {@link TargetStackFrame} in the path, this will return 0 since it is not
|
||||
* applicable to the object. If there is a stack frame in the path, this will examine its
|
||||
* ancestry, up to and excluding the {@link TargetStack} for an index. If there isn't a stack in
|
||||
* the path, it is assumed to be an ancestor of this schema, meaning the examination will
|
||||
* exhaust the ancestry provided in the path. If no index is found, an exception is thrown,
|
||||
* because the frame level is applicable, but couldn't be computed from the path given. In that
|
||||
* case, the client should include more ancestry in the path. Ideally, this is invoked relative
|
||||
* to the root schema.
|
||||
*
|
||||
* @param path the path
|
||||
* @return the frame level, or 0 if not applicable
|
||||
* @throws IllegalArgumentException if frame level is applicable but not given in the path
|
||||
*/
|
||||
default int computeFrameLevel(List<String> path) {
|
||||
List<String> framePath = searchForAncestor(TargetStackFrame.class, path);
|
||||
if (framePath == null) {
|
||||
return 0;
|
||||
}
|
||||
List<String> stackPath = searchForAncestor(TargetStack.class, framePath);
|
||||
for (int i = stackPath == null ? 0 : stackPath.size(); i < framePath.size(); i++) {
|
||||
String key = framePath.get(i);
|
||||
if (PathUtils.isIndex(key)) {
|
||||
return Integer.decode(PathUtils.parseIndex(key));
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No index between stack and frame");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,27 +15,35 @@
|
|||
*/
|
||||
package ghidra.dbg.model;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import ghidra.dbg.error.DebuggerRegisterAccessException;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.dbg.target.TargetRegisterContainer;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
|
||||
extends DefaultTestTargetObject<TestTargetObject, P> implements TargetRegisterBank {
|
||||
extends DefaultTestTargetObject<TestTargetObject, P>
|
||||
implements TargetRegisterBank, TargetRegisterContainer {
|
||||
|
||||
protected final TestTargetRegisterContainer regs;
|
||||
// TODO: Remove the separate descriptors idea
|
||||
protected final TestTargetRegisterContainer descs;
|
||||
public final Map<String, byte[]> regVals = new HashMap<>();
|
||||
|
||||
public AbstractTestTargetRegisterBank(P parent, String name, String typeHint,
|
||||
TestTargetRegisterContainer regs) {
|
||||
super(parent, name, typeHint);
|
||||
this.regs = regs;
|
||||
this.descs = regs;
|
||||
this.descs.addBank(this);
|
||||
changeAttributes(List.of(), Map.of(
|
||||
DESCRIPTIONS_ATTRIBUTE_NAME, regs //
|
||||
), "Initialized");
|
||||
DESCRIPTIONS_ATTRIBUTE_NAME, this),
|
||||
"Initialized");
|
||||
initializeValues();
|
||||
}
|
||||
|
||||
public abstract TestTargetThread getThread();
|
||||
|
@ -43,18 +51,19 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
|
|||
@Override
|
||||
public CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed(
|
||||
Collection<String> names) {
|
||||
if (!regs.getDescs().keySet().containsAll(names)) {
|
||||
if (!descs.getDescs().keySet().containsAll(names)) {
|
||||
throw new DebuggerRegisterAccessException("No such register");
|
||||
}
|
||||
Map<String, byte[]> result = new LinkedHashMap<>();
|
||||
for (String n : names) {
|
||||
byte[] v = regVals.get(n);
|
||||
if (v == null) {
|
||||
v = regs.getDescs().get(n).defaultValue();
|
||||
v = descs.getDescs().get(n).defaultValue();
|
||||
}
|
||||
result.put(n, v);
|
||||
}
|
||||
return model.gateFuture(regs.getModel().future(result).thenApply(__ -> {
|
||||
populateObjectValues(result, "Read registers");
|
||||
return model.gateFuture(descs.getModel().future(result).thenApply(__ -> {
|
||||
listeners.fire.registersUpdated(this, result);
|
||||
return result;
|
||||
}));
|
||||
|
@ -62,14 +71,14 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
|
|||
|
||||
protected CompletableFuture<Void> writeRegs(Map<String, byte[]> values,
|
||||
Consumer<Address> setPC) {
|
||||
if (!regs.getDescs().keySet().containsAll(values.keySet())) {
|
||||
if (!descs.getDescs().keySet().containsAll(values.keySet())) {
|
||||
throw new DebuggerRegisterAccessException("No such register");
|
||||
}
|
||||
Map<String, byte[]> updates = new LinkedHashMap<>();
|
||||
CompletableFuture<Void> future = regs.getModel().future(null);
|
||||
CompletableFuture<Void> future = descs.getModel().future(null);
|
||||
for (Map.Entry<String, byte[]> ent : values.entrySet()) {
|
||||
String n = ent.getKey();
|
||||
TestTargetRegister desc = regs.getDescs().get(n);
|
||||
TestTargetRegister desc = descs.getDescs().get(n);
|
||||
byte[] v = desc.normalizeValue(ent.getValue());
|
||||
regVals.put(n, v);
|
||||
updates.put(n, v);
|
||||
|
@ -79,12 +88,53 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
|
|||
});
|
||||
}
|
||||
}
|
||||
populateObjectValues(updates, "Write registers");
|
||||
future.thenAccept(__ -> {
|
||||
listeners.fire.registersUpdated(this, updates);
|
||||
});
|
||||
return model.gateFuture(future);
|
||||
}
|
||||
|
||||
protected void addObjectValues(Collection<TestTargetRegister> descs, String reason) {
|
||||
Set<TestTargetRegisterValue> objVals = new HashSet<>();
|
||||
for (TestTargetRegister rd : descs) {
|
||||
if (attributes.containsKey(rd.getName())) {
|
||||
continue;
|
||||
}
|
||||
TestTargetRegisterValue tv = new TestTargetRegisterValue(this, rd, (BigInteger) null);
|
||||
objVals.add(tv);
|
||||
}
|
||||
changeAttributes(List.of(), objVals, Map.of(), reason);
|
||||
}
|
||||
|
||||
protected void removeObjectValues(Collection<TestTargetRegister> descs, String reason) {
|
||||
List<String> toRemove = new ArrayList<>();
|
||||
for (TestTargetRegister rd : descs) {
|
||||
toRemove.add(PathUtils.parseIndex(rd.getName()));
|
||||
}
|
||||
changeAttributes(toRemove, Map.of(), reason);
|
||||
}
|
||||
|
||||
protected void populateObjectValues(Map<String, byte[]> values, String reason) {
|
||||
Set<TestTargetRegisterValue> objVals = new HashSet<>();
|
||||
for (Map.Entry<String, byte[]> ent : values.entrySet()) {
|
||||
TestTargetRegister rd = descs.getDescs().get(ent.getKey());
|
||||
byte[] value = ent.getValue();
|
||||
TestTargetRegisterValue tv = new TestTargetRegisterValue(this, rd,
|
||||
value == null ? null : Utils.bytesToBigInteger(value, rd.byteLength, true, false));
|
||||
objVals.add(tv);
|
||||
}
|
||||
changeAttributes(List.of(), objVals, Map.of(), reason);
|
||||
}
|
||||
|
||||
protected void initializeValues() {
|
||||
Map<String, byte[]> values = new HashMap<>();
|
||||
for (TestTargetRegister desc : descs.getDescs().values()) {
|
||||
values.put(desc.getIndex(), null);
|
||||
}
|
||||
populateObjectValues(values, "Populate");
|
||||
}
|
||||
|
||||
public void setFromBank(AbstractTestTargetRegisterBank<?> bank) {
|
||||
//Map<String, byte[]> updates = new HashMap<>();
|
||||
//updates.putAll(bank.regVals);
|
||||
|
@ -98,4 +148,12 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
|
|||
kit.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public void addRegisterDescs(Collection<TestTargetRegister> added, String reason) {
|
||||
addObjectValues(added, reason);
|
||||
}
|
||||
|
||||
public void removeRegisterDescs(Collection<TestTargetRegister> removed, String reason) {
|
||||
removeObjectValues(removed, reason);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ public class TestTargetRegister
|
|||
|
||||
changeAttributes(List.of(), Map.of(
|
||||
CONTAINER_ATTRIBUTE_NAME, parent,
|
||||
LENGTH_ATTRIBUTE_NAME, byteLength //
|
||||
), "Initialized");
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, byteLength * 8),
|
||||
"Initialized");
|
||||
}
|
||||
|
||||
public byte[] normalizeValue(byte[] value) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.*;
|
|||
import java.util.function.Predicate;
|
||||
|
||||
import ghidra.dbg.target.TargetRegisterContainer;
|
||||
import ghidra.dbg.util.CollectionUtils.Delta;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
||||
|
@ -26,6 +27,8 @@ public class TestTargetRegisterContainer
|
|||
extends DefaultTestTargetObject<TestTargetRegister, TestTargetProcess>
|
||||
implements TargetRegisterContainer {
|
||||
|
||||
private final Set<AbstractTestTargetRegisterBank<?>> banks = new HashSet<>();
|
||||
|
||||
public TestTargetRegisterContainer(TestTargetProcess parent) {
|
||||
super(parent, "Registers", "RegisterContainer");
|
||||
}
|
||||
|
@ -43,13 +46,50 @@ public class TestTargetRegisterContainer
|
|||
}
|
||||
add.add(getModel().newTestTargetRegister(this, register));
|
||||
}
|
||||
changeElements(List.of(), add, "Added registers from Ghidra language: " + language);
|
||||
String reason = "Added registers from Ghidra language: " + language;
|
||||
changeElements(List.of(), add, reason);
|
||||
List<AbstractTestTargetRegisterBank<?>> banks;
|
||||
synchronized (this.banks) {
|
||||
banks = List.copyOf(this.banks);
|
||||
}
|
||||
for (AbstractTestTargetRegisterBank<?> bank : banks) {
|
||||
bank.addRegisterDescs(add, reason);
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
public TestTargetRegister addRegister(Register register) {
|
||||
TestTargetRegister tr = getModel().newTestTargetRegister(this, register);
|
||||
changeElements(List.of(), List.of(tr), "Added " + register + " from Ghidra language");
|
||||
TestTargetRegister tr =
|
||||
getModel().newTestTargetRegister(this, Objects.requireNonNull(register));
|
||||
String reason = "Added " + register + " from Ghidra language";
|
||||
changeElements(List.of(), List.of(tr), reason);
|
||||
List<AbstractTestTargetRegisterBank<?>> banks;
|
||||
synchronized (this.banks) {
|
||||
banks = List.copyOf(this.banks);
|
||||
}
|
||||
for (AbstractTestTargetRegisterBank<?> bank : banks) {
|
||||
bank.addRegisterDescs(List.of(tr), reason);
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
|
||||
public Delta<TestTargetRegister, TestTargetRegister> removeRegister(Register register,
|
||||
String reason) {
|
||||
Delta<TestTargetRegister, TestTargetRegister> result =
|
||||
changeElements(List.of(register.getName()), List.of(), reason);
|
||||
List<AbstractTestTargetRegisterBank<?>> banks;
|
||||
synchronized (this.banks) {
|
||||
banks = List.copyOf(this.banks);
|
||||
}
|
||||
for (AbstractTestTargetRegisterBank<?> bank : banks) {
|
||||
bank.removeRegisterDescs(result.removed.values(), reason);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addBank(AbstractTestTargetRegisterBank<?> bank) {
|
||||
synchronized (this.banks) {
|
||||
this.banks.add(bank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,44 +22,42 @@ import java.util.Map;
|
|||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
|
||||
public class TestTargetRegisterValue
|
||||
extends DefaultTestTargetObject<TestTargetObject, AbstractTestTargetRegisterBank<?>>
|
||||
implements TargetRegister {
|
||||
|
||||
public static TestTargetRegisterValue fromRegisterValue(
|
||||
AbstractTestTargetRegisterBank<?> parent, RegisterValue rv) {
|
||||
Register register = rv.getRegister();
|
||||
return new TestTargetRegisterValue(parent, PathUtils.makeKey(register.getName()),
|
||||
register.isProgramCounter(), rv.getUnsignedValue(), register.getBitLength() + 7 / 8);
|
||||
public final TestTargetRegister desc;
|
||||
|
||||
public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent,
|
||||
TestTargetRegister desc, BigInteger value) {
|
||||
this(parent, desc,
|
||||
value == null ? null : Utils.bigIntegerToBytes(value, desc.byteLength, true));
|
||||
}
|
||||
|
||||
protected final int byteLength;
|
||||
protected final boolean isPC;
|
||||
public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent,
|
||||
TestTargetRegister desc, byte[] value) {
|
||||
super(parent, PathUtils.parseIndex(desc.getName()), "Register");
|
||||
this.desc = desc;
|
||||
|
||||
public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent, String name,
|
||||
boolean isPC, BigInteger value, int byteLength) {
|
||||
this(parent, name, isPC, Utils.bigIntegerToBytes(value, byteLength, true));
|
||||
}
|
||||
|
||||
public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent, String name,
|
||||
boolean isPC, byte[] value) {
|
||||
super(parent, name, "Register");
|
||||
this.byteLength = value.length;
|
||||
this.isPC = isPC;
|
||||
|
||||
changeAttributes(List.of(), Map.of(
|
||||
CONTAINER_ATTRIBUTE_NAME, parent,
|
||||
LENGTH_ATTRIBUTE_NAME, byteLength,
|
||||
VALUE_ATTRIBUTE_NAME, value //
|
||||
), "Initialized");
|
||||
if (value == null) {
|
||||
changeAttributes(List.of(), Map.of(
|
||||
CONTAINER_ATTRIBUTE_NAME, parent,
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, desc.byteLength * 8),
|
||||
"Populated");
|
||||
}
|
||||
else {
|
||||
changeAttributes(List.of(), Map.of(
|
||||
CONTAINER_ATTRIBUTE_NAME, parent,
|
||||
BIT_LENGTH_ATTRIBUTE_NAME, desc.byteLength * 8,
|
||||
VALUE_ATTRIBUTE_NAME, value),
|
||||
"Initialized");
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(BigInteger value) {
|
||||
changeAttributes(List.of(), Map.of(
|
||||
VALUE_ATTRIBUTE_NAME, Utils.bigIntegerToBytes(value, byteLength, true) //
|
||||
), "Set value");
|
||||
VALUE_ATTRIBUTE_NAME, Utils.bigIntegerToBytes(value, desc.byteLength, true)),
|
||||
"Set value");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ import ghidra.dbg.util.PathUtils;
|
|||
|
||||
public class TestTargetThread
|
||||
extends DefaultTestTargetObject<TestTargetObject, TestTargetThreadContainer>
|
||||
implements TargetThread, TargetExecutionStateful, TargetSteppable, TargetResumable,
|
||||
TargetInterruptible, TargetKillable {
|
||||
implements TargetThread, TargetAggregate, TargetExecutionStateful, TargetSteppable,
|
||||
TargetResumable, TargetInterruptible, TargetKillable {
|
||||
|
||||
public static final TargetStepKindSet SUPPORTED_KINDS =
|
||||
TargetStepKindSet.of(TargetStepKind.values());
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Memory" schema="Memory" required="yes" fixed="yes" />
|
||||
<attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Registers" schema="RegisterContainer" required="yes" fixed="yes" />
|
||||
<!-- <attribute name="Registers" schema="RegisterContainer" required="yes" fixed="yes" /> -->
|
||||
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Devices" schema="OBJECT" />
|
||||
<attribute name="Environment" schema="OBJECT" />
|
||||
|
@ -171,6 +171,7 @@
|
|||
</schema>
|
||||
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Thread" />
|
||||
<interface name="Aggregate" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<interface name="Steppable" />
|
||||
<interface name="Resumable" />
|
||||
|
@ -269,7 +270,7 @@
|
|||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="ANY" />
|
||||
</schema>
|
||||
<schema name="RegisterContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<schema name="RegisterContainer" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="RegisterContainer" />
|
||||
<element schema="RegisterDescriptor" />
|
||||
<attribute name="_descriptions" schema="RegisterContainer" />
|
||||
|
@ -280,12 +281,13 @@
|
|||
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="ANY" />
|
||||
</schema>
|
||||
<schema name="RegisterBank" elementResync="NEVER" attributeResync="NEVER">
|
||||
<schema name="RegisterBank" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="RegisterBank" />
|
||||
<interface name="RegisterContainer" />
|
||||
<!-- NB: registers are attributes, not elements here -->
|
||||
<element schema="RegisterDescriptor" />
|
||||
<element schema="VOID" />
|
||||
<attribute schema="RegisterValue" />
|
||||
<attribute name="_descriptions" schema="RegisterContainer" />
|
||||
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
|
@ -294,9 +296,9 @@
|
|||
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="OBJECT" />
|
||||
</schema>
|
||||
<schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Stack" />
|
||||
<element schema="StackFrame" />
|
||||
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
|
@ -394,4 +396,18 @@
|
|||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
</context>
|
||||
<schema name="RegisterValue" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Register" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="_length" schema="INT" fixed="yes" hidden="yes" />
|
||||
<attribute name="_container" schema="RegisterContainer" />
|
||||
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_value" schema="ANY" required="yes" hidden="yes" />
|
||||
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
</context>
|
|
@ -25,6 +25,7 @@ import ghidra.trace.model.memory.TraceMemoryState;
|
|||
import ghidra.trace.model.property.TracePropertyMap;
|
||||
import ghidra.trace.model.property.TracePropertyMapSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
|
||||
/**
|
||||
|
@ -128,9 +129,7 @@ public class DefaultPcodeTraceRegistersAccess extends AbstractPcodeTraceDataAcce
|
|||
return null; // client should bail anyway
|
||||
}
|
||||
AddressSpace space = ops.getAddressSpace();
|
||||
return new AddressRangeImpl(
|
||||
space.getOverlayAddress(range.getMinAddress()),
|
||||
space.getOverlayAddress(range.getMaxAddress()));
|
||||
return TraceRegisterUtils.getOverlayRange(space, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -140,12 +139,6 @@ public class DefaultPcodeTraceRegistersAccess extends AbstractPcodeTraceDataAcce
|
|||
return null; // client should bail anyway
|
||||
}
|
||||
AddressSpace space = ops.getAddressSpace();
|
||||
AddressSet result = new AddressSet();
|
||||
for (AddressRange rng : set) {
|
||||
result.add(
|
||||
space.getOverlayAddress(rng.getMinAddress()),
|
||||
space.getOverlayAddress(rng.getMaxAddress()));
|
||||
}
|
||||
return result;
|
||||
return TraceRegisterUtils.getOverlaySet(space, set);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,6 +227,20 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
}
|
||||
}
|
||||
|
||||
protected AddressSpace doCreateOverlaySpace(String name, AddressSpace base) {
|
||||
TraceAddressFactory factory = trace.getInternalAddressFactory();
|
||||
OverlayAddressSpace space =
|
||||
factory.addOverlayAddressSpace(name, true, base, base.getMinAddress().getOffset(),
|
||||
base.getMaxAddress().getOffset());
|
||||
// Only if it succeeds do we store the record
|
||||
DBTraceOverlaySpaceEntry ent = overlayStore.create();
|
||||
ent.set(space.getName(), base.getName());
|
||||
trace.updateViewsAddSpaceBlock(space);
|
||||
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null,
|
||||
trace, null, space));
|
||||
return space;
|
||||
}
|
||||
|
||||
public AddressSpace createOverlayAddressSpace(String name, AddressSpace base)
|
||||
throws DuplicateNameException {
|
||||
// TODO: Exclusive lock?
|
||||
|
@ -235,17 +249,19 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
if (factory.getAddressSpace(name) != null) {
|
||||
throw new DuplicateNameException("Address space " + name + " already exists.");
|
||||
}
|
||||
return doCreateOverlaySpace(name, base);
|
||||
}
|
||||
}
|
||||
|
||||
OverlayAddressSpace space =
|
||||
factory.addOverlayAddressSpace(name, true, base, base.getMinAddress().getOffset(),
|
||||
base.getMaxAddress().getOffset());
|
||||
// Only if it succeeds do we store the record
|
||||
DBTraceOverlaySpaceEntry ent = overlayStore.create();
|
||||
ent.set(space.getName(), base.getName());
|
||||
trace.updateViewsAddSpaceBlock(space);
|
||||
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null,
|
||||
trace, null, space));
|
||||
return space;
|
||||
public AddressSpace getOrCreateOverlayAddressSpace(String name, AddressSpace base) {
|
||||
// TODO: Exclusive lock?
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
TraceAddressFactory factory = trace.getInternalAddressFactory();
|
||||
AddressSpace space = factory.getAddressSpace(name);
|
||||
if (space != null) {
|
||||
return space.getPhysicalSpace() == base ? space : null;
|
||||
}
|
||||
return doCreateOverlaySpace(name, base);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public enum DBTraceObjectRegisterSupport {
|
|||
static class LazyValues {
|
||||
private final TraceObjectValue registerValue;
|
||||
private BigInteger value;
|
||||
private int length = -1;
|
||||
private int bitLength = -1;
|
||||
private byte[] be;
|
||||
private byte[] le;
|
||||
|
||||
|
@ -85,7 +85,11 @@ public enum DBTraceObjectRegisterSupport {
|
|||
"Invalid register value " + s + ". Must be hex digits only.");
|
||||
}
|
||||
}
|
||||
if (val instanceof Byte b) {
|
||||
else if (val instanceof byte[] arr) {
|
||||
// NOTE: Reg object values are always big endian
|
||||
return new BigInteger(1, arr);
|
||||
}
|
||||
else if (val instanceof Byte b) {
|
||||
return BigInteger.valueOf(b);
|
||||
}
|
||||
else if (val instanceof Short s) {
|
||||
|
@ -106,16 +110,16 @@ public enum DBTraceObjectRegisterSupport {
|
|||
"'");
|
||||
}
|
||||
|
||||
int getRegisterValueLength() throws RegisterValueException {
|
||||
Object objLength = registerValue.getParent()
|
||||
.getValue(registerValue.getMinSnap(), TargetRegister.LENGTH_ATTRIBUTE_NAME)
|
||||
int getRegisterValueBitLength() throws RegisterValueException {
|
||||
Object objBitLength = registerValue.getParent()
|
||||
.getValue(registerValue.getMinSnap(), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME)
|
||||
.getValue();
|
||||
if (!(objLength instanceof Number)) {
|
||||
if (!(objBitLength instanceof Number)) {
|
||||
throw new RegisterValueException(
|
||||
"Register length is not numeric: (" + objLength.getClass() + ") '" + objLength +
|
||||
"'");
|
||||
"Register length is not numeric: (" + objBitLength.getClass() + ") '" +
|
||||
objBitLength + "'");
|
||||
}
|
||||
return ((Number) objLength).intValue();
|
||||
return ((Number) objBitLength).intValue();
|
||||
}
|
||||
|
||||
BigInteger getValue() throws RegisterValueException {
|
||||
|
@ -125,25 +129,29 @@ public enum DBTraceObjectRegisterSupport {
|
|||
return value = convertRegisterValueToBigInteger();
|
||||
}
|
||||
|
||||
int getLength() throws RegisterValueException {
|
||||
if (length != -1) {
|
||||
return length;
|
||||
int getBitLength() throws RegisterValueException {
|
||||
if (bitLength != -1) {
|
||||
return bitLength;
|
||||
}
|
||||
return length = getRegisterValueLength();
|
||||
return bitLength = getRegisterValueBitLength();
|
||||
}
|
||||
|
||||
int getByteLength() throws RegisterValueException {
|
||||
return (getBitLength() + 7) / 8;
|
||||
}
|
||||
|
||||
byte[] getBytesBigEndian() throws RegisterValueException {
|
||||
if (be != null) {
|
||||
return be;
|
||||
}
|
||||
return be = Utils.bigIntegerToBytes(getValue(), getLength(), true);
|
||||
return be = Utils.bigIntegerToBytes(getValue(), getByteLength(), true);
|
||||
}
|
||||
|
||||
byte[] getBytesLittleEndian() throws RegisterValueException {
|
||||
if (le != null) {
|
||||
return le;
|
||||
}
|
||||
return le = Utils.bigIntegerToBytes(getValue(), getLength(), false);
|
||||
return le = Utils.bigIntegerToBytes(getValue(), getByteLength(), false);
|
||||
}
|
||||
|
||||
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
|
||||
|
@ -160,14 +168,10 @@ public enum DBTraceObjectRegisterSupport {
|
|||
return null;
|
||||
}
|
||||
String pathStr = container.getCanonicalPath().toString();
|
||||
AddressSpace space = object.getTrace().getBaseAddressFactory().getAddressSpace(pathStr);
|
||||
if (space == null) {
|
||||
return null;
|
||||
}
|
||||
if (!space.isRegisterSpace()) {
|
||||
return null;
|
||||
}
|
||||
return space;
|
||||
Trace trace = object.getTrace();
|
||||
return trace.getMemoryManager()
|
||||
.getOrCreateOverlayAddressSpace(pathStr,
|
||||
trace.getBaseAddressFactory().getRegisterSpace());
|
||||
}
|
||||
|
||||
protected AddressSpace findRegisterOverlay(TraceObjectValue objectValue) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.trace.database.guest;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
|
@ -30,6 +31,7 @@ import ghidra.program.model.symbol.SourceType;
|
|||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
@ -78,6 +80,48 @@ public interface InternalTracePlatform extends TracePlatform {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
default String getConventionalRegisterObjectName(Register register) {
|
||||
Address pmin = mapGuestToHost(register.getAddress());
|
||||
if (pmin == null) {
|
||||
return register.getName();
|
||||
}
|
||||
TraceSymbolManager symbolManager = getTrace().getSymbolManager();
|
||||
TraceNamespaceSymbol nsRegMap = symbolManager.namespaces().getGlobalNamed(regMap(register));
|
||||
Collection<? extends TraceLabelSymbol> labels = symbolManager.labels()
|
||||
.getAt(0, null, pmin, false)
|
||||
.stream()
|
||||
.filter(s -> s.getParentNamespace() == nsRegMap)
|
||||
.toList();
|
||||
if (labels.isEmpty()) {
|
||||
return register.getName();
|
||||
}
|
||||
// primary is listed first, so take it
|
||||
return labels.iterator().next().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
||||
Register register) {
|
||||
PathMatcher matcher = schema.searchFor(TargetRegister.class, path, true);
|
||||
if (matcher.isEmpty()) {
|
||||
return matcher;
|
||||
}
|
||||
String name = getConventionalRegisterObjectName(register);
|
||||
return matcher.applyKeys(Align.RIGHT, List.of(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(TraceObject container, Register register) {
|
||||
return getConventionalRegisterPath(container.getTargetSchema(),
|
||||
container.getCanonicalPath().getKeyList(), register);
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(TargetObject container, Register register) {
|
||||
return getConventionalRegisterPath(container.getSchema(), container.getPath(), register);
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(AddressSpace space, Register register) {
|
||||
List<String> path = PathUtils.parse(space.getName());
|
||||
|
@ -87,20 +131,7 @@ public interface InternalTracePlatform extends TracePlatform {
|
|||
}
|
||||
TargetObjectSchema schema = rootSchema
|
||||
.getSuccessorSchema(path);
|
||||
PathMatcher matcher = schema.searchFor(TargetRegister.class, path, true);
|
||||
if (matcher.isEmpty()) {
|
||||
return matcher;
|
||||
}
|
||||
Address pmin = mapGuestToHost(register.getAddress());
|
||||
TraceSymbolManager symbolManager = getTrace().getSymbolManager();
|
||||
TraceNamespaceSymbol nsRegMap = symbolManager.namespaces().getGlobalNamed(regMap(register));
|
||||
Collection<? extends TraceLabelSymbol> labels = symbolManager.labels()
|
||||
.getAt(0, null, pmin, false)
|
||||
.stream()
|
||||
.filter(s -> s.getParentNamespace() == nsRegMap)
|
||||
.toList();
|
||||
String name = labels.isEmpty() ? register.getName() : labels.iterator().next().getName();
|
||||
return matcher.applyKeys(Align.RIGHT, List.of(name));
|
||||
return getConventionalRegisterPath(schema, path, register);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -55,6 +55,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView<T extends DBTraceCo
|
|||
Collections2.transform(manager.getActiveMemorySpaces(), this::getView);
|
||||
}
|
||||
|
||||
public AddressSpace getSpace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TraceBaseCodeUnitsView#getTrace()
|
||||
*/
|
||||
|
|
|
@ -47,6 +47,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
|
|||
this.space = space;
|
||||
}
|
||||
|
||||
public AddressSpace getSpace() {
|
||||
return getAddressSpace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address space for this view
|
||||
*
|
||||
|
|
|
@ -16,15 +16,14 @@
|
|||
package ghidra.trace.database.listing;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.listing.TraceCodeManager;
|
||||
import ghidra.trace.model.listing.TraceCodeUnitsView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
|
||||
/**
|
||||
* The implementation of {@link TraceCodeManager#codeUnits()}
|
||||
*/
|
||||
public class DBTraceCodeUnitsMemoryView extends
|
||||
AbstractWithUndefinedDBTraceCodeUnitsMemoryView<DBTraceCodeUnitAdapter, DBTraceCodeUnitsView>
|
||||
implements TraceCodeUnitsView {
|
||||
implements TraceCodeUnitsView, InternalBaseCodeUnitsView<TraceCodeUnit> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -20,15 +20,14 @@ import java.util.List;
|
|||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.listing.TraceCodeUnitsView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
|
||||
/**
|
||||
* The implementation of {@link TraceCodeSpace#codeUnits()}
|
||||
*/
|
||||
public class DBTraceCodeUnitsView extends
|
||||
AbstractComposedDBTraceCodeUnitsView<DBTraceCodeUnitAdapter, AbstractSingleDBTraceCodeUnitsView<? extends DBTraceCodeUnitAdapter>>
|
||||
implements TraceCodeUnitsView {
|
||||
implements TraceCodeUnitsView, InternalBaseCodeUnitsView<TraceCodeUnit> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -15,15 +15,14 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import ghidra.trace.model.listing.TraceCodeManager;
|
||||
import ghidra.trace.model.listing.TraceDataView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
|
||||
/**
|
||||
* The implementation of {@link TraceCodeManager#data()}
|
||||
*/
|
||||
public class DBTraceDataMemoryView
|
||||
extends AbstractWithUndefinedDBTraceCodeUnitsMemoryView<DBTraceDataAdapter, DBTraceDataView>
|
||||
implements TraceDataView {
|
||||
implements TraceDataView, InternalBaseCodeUnitsView<TraceData> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -20,15 +20,14 @@ import java.util.List;
|
|||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.listing.TraceDataView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
|
||||
/**
|
||||
* The implementation of {@link TraceCodeSpace#data()}
|
||||
*/
|
||||
public class DBTraceDataView extends
|
||||
AbstractComposedDBTraceCodeUnitsView<DBTraceDataAdapter, AbstractSingleDBTraceCodeUnitsView<? extends DBTraceDataAdapter>>
|
||||
implements TraceDataView {
|
||||
implements TraceDataView, InternalBaseCodeUnitsView<TraceData> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -17,12 +17,10 @@ package ghidra.trace.database.listing;
|
|||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.listing.TraceCodeManager;
|
||||
import ghidra.trace.model.listing.TraceDefinedDataView;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -31,7 +29,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class DBTraceDefinedDataMemoryView
|
||||
extends AbstractBaseDBTraceCodeUnitsMemoryView<DBTraceData, DBTraceDefinedDataView>
|
||||
implements TraceDefinedDataView {
|
||||
implements InternalTraceDefinedDataView {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -28,7 +28,6 @@ import ghidra.trace.model.Trace.TraceCodeChangeType;
|
|||
import ghidra.trace.model.Trace.TraceCompositeDataChangeType;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.listing.TraceDefinedDataView;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
|
@ -36,7 +35,7 @@ import ghidra.util.LockHold;
|
|||
* The implementation of {@link TraceCodeSpace#definedData()}
|
||||
*/
|
||||
public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<DBTraceData>
|
||||
implements TraceDefinedDataView {
|
||||
implements InternalTraceDefinedDataView {
|
||||
/**
|
||||
* Construct the view
|
||||
*
|
||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.trace.database.listing;
|
|||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.listing.TraceCodeManager;
|
||||
import ghidra.trace.model.listing.TraceDefinedUnitsView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -28,7 +27,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class DBTraceDefinedUnitsMemoryView extends
|
||||
AbstractBaseDBTraceCodeUnitsMemoryView<AbstractDBTraceCodeUnit<?>, DBTraceDefinedUnitsView>
|
||||
implements TraceDefinedUnitsView {
|
||||
implements TraceDefinedUnitsView, InternalTraceBaseDefinedUnitsView<TraceCodeUnit> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -22,8 +22,7 @@ import com.google.common.collect.Range;
|
|||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.listing.TraceDefinedUnitsView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -33,7 +32,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class DBTraceDefinedUnitsView extends
|
||||
AbstractComposedDBTraceCodeUnitsView<AbstractDBTraceCodeUnit<?>, AbstractBaseDBTraceDefinedUnitsView<? extends AbstractDBTraceCodeUnit<?>>>
|
||||
implements TraceDefinedUnitsView {
|
||||
implements TraceDefinedUnitsView, InternalTraceBaseDefinedUnitsView<TraceCodeUnit> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -25,8 +25,7 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceCodeManager;
|
||||
import ghidra.trace.model.listing.TraceInstructionsView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -36,7 +35,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class DBTraceInstructionsMemoryView
|
||||
extends AbstractBaseDBTraceCodeUnitsMemoryView<DBTraceInstruction, DBTraceInstructionsView>
|
||||
implements TraceInstructionsView {
|
||||
implements TraceInstructionsView, InternalTraceBaseDefinedUnitsView<TraceInstruction> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -46,7 +46,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
* The implementation of {@link TraceCodeSpace#instructions()}
|
||||
*/
|
||||
public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView<DBTraceInstruction>
|
||||
implements TraceInstructionsView {
|
||||
implements TraceInstructionsView, InternalTraceBaseDefinedUnitsView<TraceInstruction> {
|
||||
|
||||
protected static <T> T replaceIfNotNull(T cur, T rep) {
|
||||
return rep != null ? rep : cur;
|
||||
|
|
|
@ -15,15 +15,14 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import ghidra.trace.model.listing.TraceCodeManager;
|
||||
import ghidra.trace.model.listing.TraceUndefinedDataView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
|
||||
/**
|
||||
* The implementation of {@link TraceCodeManager#undefinedData()}
|
||||
*/
|
||||
public class DBTraceUndefinedDataMemoryView extends
|
||||
AbstractWithUndefinedDBTraceCodeUnitsMemoryView<UndefinedDBTraceData, DBTraceUndefinedDataView>
|
||||
implements TraceUndefinedDataView {
|
||||
implements TraceUndefinedDataView, InternalBaseCodeUnitsView<TraceData> {
|
||||
|
||||
/**
|
||||
* Construct the view
|
||||
|
|
|
@ -26,15 +26,15 @@ import com.google.common.collect.Range;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.listing.TraceUndefinedDataView;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.util.*;
|
||||
|
||||
/**
|
||||
* The implementation of {@link TraceCodeSpace#undefinedData()}
|
||||
*/
|
||||
public class DBTraceUndefinedDataView extends
|
||||
AbstractSingleDBTraceCodeUnitsView<UndefinedDBTraceData> implements TraceUndefinedDataView {
|
||||
AbstractSingleDBTraceCodeUnitsView<UndefinedDBTraceData>
|
||||
implements TraceUndefinedDataView, InternalBaseCodeUnitsView<TraceData> {
|
||||
|
||||
protected final static int CACHE_MAX_SNAPS = 5;
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* ###
|
||||
* 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.trace.database.listing;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
|
||||
public interface InternalBaseCodeUnitsView<T extends TraceCodeUnit>
|
||||
extends TraceBaseCodeUnitsView<T> {
|
||||
AddressSpace getSpace();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
default T getForRegister(TracePlatform platform, long snap, Register register) {
|
||||
// Find a code unit which contains the register completely
|
||||
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
|
||||
T candidate = getContaining(snap, range.getMinAddress());
|
||||
if (candidate == null) {
|
||||
return null;
|
||||
}
|
||||
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
|
||||
if (cmpMax > 0) {
|
||||
return null;
|
||||
}
|
||||
if (cmpMax == 0 && candidate.getMinAddress().equals(range.getMinAddress())) {
|
||||
return candidate;
|
||||
}
|
||||
if (!(candidate instanceof TraceData)) {
|
||||
return null;
|
||||
}
|
||||
TraceData data = (TraceData) candidate;
|
||||
// Cast because if candidate is TraceData, T is, too
|
||||
// NOTE: It may not be a primitive
|
||||
return (T) TraceRegisterUtils.seekComponent(data, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
default T getContaining(TracePlatform platform, long snap, Register register) {
|
||||
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
|
||||
T candidate = getContaining(snap, range.getMinAddress());
|
||||
if (candidate == null) {
|
||||
return null;
|
||||
}
|
||||
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
|
||||
if (cmpMax > 0) {
|
||||
return null;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* ###
|
||||
* 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.trace.database.listing;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceBaseDefinedUnitsView;
|
||||
import ghidra.trace.model.listing.TraceCodeUnit;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public interface InternalTraceBaseDefinedUnitsView<T extends TraceCodeUnit>
|
||||
extends TraceBaseDefinedUnitsView<T>, InternalBaseCodeUnitsView<T> {
|
||||
|
||||
@Override
|
||||
default void clear(TracePlatform platform, Range<Long> span, Register register,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
|
||||
clear(span, range, true, monitor);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/* ###
|
||||
* 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.trace.database.listing;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceData;
|
||||
import ghidra.trace.model.listing.TraceDefinedDataView;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
|
||||
public interface InternalTraceDefinedDataView
|
||||
extends TraceDefinedDataView, InternalTraceBaseDefinedUnitsView<TraceData> {
|
||||
|
||||
@Override
|
||||
default TraceData create(TracePlatform platform, Range<Long> lifespan, Register register,
|
||||
DataType dataType) throws CodeUnitInsertionException {
|
||||
TraceRegisterUtils.requireByteBound(register);
|
||||
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
|
||||
return create(lifespan, range.getMinAddress(), dataType, (int) range.getLength());
|
||||
}
|
||||
}
|
|
@ -77,6 +77,11 @@ public class DBTraceMemoryManager extends AbstractDBTraceSpaceBasedManager<DBTra
|
|||
return overlayAdapter.createOverlayAddressSpace(name, base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSpace getOrCreateOverlayAddressSpace(String name, AddressSpace base) {
|
||||
return overlayAdapter.getOrCreateOverlayAddressSpace(name, base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteOverlayAddressSpace(String name) {
|
||||
overlayAdapter.deleteOverlayAddressSpace(name);
|
||||
|
|
|
@ -60,14 +60,14 @@ public class DBTraceObjectRegister implements TraceObjectRegister, DBTraceObject
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
public int getBitLength() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, computeMinSnap(),
|
||||
TargetRegister.LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
|
||||
TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Range<Long> lifespan, byte[] value) {
|
||||
int length = getLength();
|
||||
int length = getByteLength();
|
||||
if (length != 0 && value.length != length) {
|
||||
throw new IllegalArgumentException("Length must match the register");
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public class DBTraceObjectRegister implements TraceObjectRegister, DBTraceObject
|
|||
if (val instanceof String) {
|
||||
// Always base 16. Model API says byte array for register value is big endian.
|
||||
BigInteger bigVal = new BigInteger((String) val, 16);
|
||||
return Utils.bigIntegerToBytes(bigVal, getLength(), true);
|
||||
return Utils.bigIntegerToBytes(bigVal, getByteLength(), true);
|
||||
}
|
||||
throw new ClassCastException("Cannot convert " + val + " to byte array for register value");
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ import db.DBHandle;
|
|||
import db.DBRecord;
|
||||
import generic.CatenatedCollection;
|
||||
import ghidra.dbg.target.TargetRegisterContainer;
|
||||
import ghidra.dbg.util.PathPattern;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.database.*;
|
||||
|
@ -35,7 +33,6 @@ import ghidra.trace.database.thread.DBTraceThreadManager;
|
|||
import ghidra.trace.model.stack.TraceObjectStackFrame;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
|
@ -247,22 +244,6 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
return getForRegisterSpace(frame.getStack().getThread(), frame.getLevel(), createIfAbsent);
|
||||
}
|
||||
|
||||
private TraceObject searchForRegisterContainer(TraceObject object, int frameLevel) {
|
||||
PathPredicates regsMatcher = object.getRoot()
|
||||
.getTargetSchema()
|
||||
.searchForRegisterContainer(frameLevel, object.getCanonicalPath().getKeyList());
|
||||
|
||||
for (PathPattern regsPattern : regsMatcher.getPatterns()) {
|
||||
TraceObject regsObj = trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(
|
||||
TraceObjectKeyPath.of(regsPattern.getSingletonPath()));
|
||||
if (regsObj != null) {
|
||||
return regsObj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private M doGetForRegisterSpaceFoundContainer(TraceObject object, TraceObject objRegs,
|
||||
boolean createIfAbsent) {
|
||||
String name = objRegs.getCanonicalPath().toString();
|
||||
|
@ -284,21 +265,15 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
}
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
AddressSpace as = trace.getBaseAddressFactory().getAddressSpace(name);
|
||||
if (as == null) {
|
||||
as = trace.getMemoryManager()
|
||||
.createOverlayAddressSpace(name,
|
||||
trace.getBaseAddressFactory().getRegisterSpace());
|
||||
}
|
||||
AddressSpace as = trace.getMemoryManager()
|
||||
.getOrCreateOverlayAddressSpace(name,
|
||||
trace.getBaseAddressFactory().getRegisterSpace());
|
||||
M space = getForSpace(as, createIfAbsent);
|
||||
synchronized (regSpacesByObject) {
|
||||
regSpacesByObject.put(object, space);
|
||||
}
|
||||
return space;
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new AssertionError(e); // I checked for it first, with a lock
|
||||
}
|
||||
}
|
||||
|
||||
protected M getForRegisterSpaceObjectThread(TraceObjectThread thread, int frameLevel,
|
||||
|
@ -319,7 +294,7 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
if (object.getTargetSchema().getInterfaces().contains(TargetRegisterContainer.class)) {
|
||||
return doGetForRegisterSpaceFoundContainer(object, object, createIfAbsent);
|
||||
}
|
||||
TraceObject objRegs = searchForRegisterContainer(object, frameLevel);
|
||||
TraceObject objRegs = object.queryRegisterContainer(frameLevel);
|
||||
if (objRegs != null) {
|
||||
return doGetForRegisterSpaceFoundContainer(object, objRegs, createIfAbsent);
|
||||
}
|
||||
|
|
|
@ -36,10 +36,18 @@ public interface DBTraceSpaceBased extends DBTraceSpaceKey {
|
|||
return false;
|
||||
}
|
||||
|
||||
default String explainLanguages(AddressSpace space) {
|
||||
if (space.getName().equals(getAddressSpace().getName())) {
|
||||
return ". It's likely they come from different languages. Check the platform.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
default long assertInSpace(Address addr) {
|
||||
if (!isMySpace(addr.getAddressSpace())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Address '" + addr + "' is not in this space: '" + getAddressSpace() + "'");
|
||||
"Address '" + addr + "' is not in this space: '" + getAddressSpace() + "'" +
|
||||
explainLanguages(addr.getAddressSpace()));
|
||||
}
|
||||
return addr.getOffset();
|
||||
}
|
||||
|
@ -47,7 +55,8 @@ public interface DBTraceSpaceBased extends DBTraceSpaceKey {
|
|||
default void assertInSpace(AddressRange range) {
|
||||
if (!isMySpace(range.getAddressSpace())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Address Range '" + range + "' is not in this space: '" + getAddressSpace() + "'");
|
||||
"Address Range '" + range + "' is not in this space: '" + getAddressSpace() + "'" +
|
||||
explainLanguages(range.getAddressSpace()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -859,13 +859,25 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
.map(o -> o.queryInterface(ifClass));
|
||||
}
|
||||
|
||||
// TODO: Post filter until GP-1301
|
||||
private boolean isActuallyInterface(TraceObjectValPath path,
|
||||
Class<? extends TargetObject> targetIf) {
|
||||
TraceObjectValue lastEntry = path.getLastEntry();
|
||||
if (lastEntry == null) {
|
||||
// TODO: This assumes the client will call getDestination(this)
|
||||
return this.getTargetSchema().getInterfaces().contains(targetIf);
|
||||
}
|
||||
if (!lastEntry.isObject()) {
|
||||
return false;
|
||||
}
|
||||
return lastEntry.getChild().getTargetSchema().getInterfaces().contains(targetIf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends TraceObjectValPath> querySuccessorsTargetInterface(Range<Long> span,
|
||||
Class<? extends TargetObject> targetIf) {
|
||||
PathMatcher matcher = getTargetSchema().searchFor(targetIf, true);
|
||||
// TODO: Post filter until GP-1301
|
||||
return getSuccessors(span, matcher).filter(
|
||||
p -> p.getDestination(this).getTargetSchema().getInterfaces().contains(targetIf));
|
||||
return getSuccessors(span, matcher).filter(p -> isActuallyInterface(p, targetIf));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,13 +15,18 @@
|
|||
*/
|
||||
package ghidra.trace.model.guest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.symbol.TraceLabelSymbol;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
/**
|
||||
* A platform within a trace
|
||||
|
@ -119,7 +124,7 @@ public interface TracePlatform {
|
|||
* Translate a set from host to guest
|
||||
*
|
||||
* <p>
|
||||
* Only those ranges (or parts of ranges) that map are included.
|
||||
* Only those ranges (or parts of ranges) that mapped are included.
|
||||
*
|
||||
* @param hostSet the host set
|
||||
* @return the guest set
|
||||
|
@ -149,7 +154,7 @@ public interface TracePlatform {
|
|||
* Translate a set from guest to host
|
||||
*
|
||||
* <p>
|
||||
* Only those ranges (or parts of ranges) that map are included.
|
||||
* Only those ranges (or parts of ranges) that mapped are included.
|
||||
*
|
||||
* @param guestSet the guest set
|
||||
* @return the host set
|
||||
|
@ -165,13 +170,57 @@ public interface TracePlatform {
|
|||
*/
|
||||
AddressRange getConventionalRegisterRange(AddressSpace overlay, Register register);
|
||||
|
||||
/**
|
||||
* Get the name or index of the register object for the given platform register
|
||||
*
|
||||
* <p>
|
||||
* This will check for a label in the host physical space, allowing a mapper to specify an
|
||||
* alternative register object name. See {@link #addRegisterMapOverride(Register, String)}.
|
||||
*
|
||||
* @param register the platform register
|
||||
* @return the mapped name
|
||||
*/
|
||||
String getConventionalRegisterObjectName(Register register);
|
||||
|
||||
/**
|
||||
* Get the expected path where an object defining the register value would be
|
||||
*
|
||||
* <p>
|
||||
* This will check for a label in the host physical space, allowing a mapper to specify an
|
||||
* alternative register name.
|
||||
* alternative register object name. See {@link #addRegisterMapOverride(Register, String)}.
|
||||
*
|
||||
* @param schema the schema of the register container
|
||||
* @param path the path to the register container
|
||||
* @param register the platform register
|
||||
* @return the path matcher, possibly empty
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
|
||||
Register register);
|
||||
|
||||
/**
|
||||
* Get the expected path where an object defining the register value would be
|
||||
*
|
||||
* @see #getConventionalRegisterPath(TargetObjectSchema, List, Register)
|
||||
* @param container the register container
|
||||
* @param register the platform register
|
||||
* @return that path matcher, possibly empty, or null if the trace has no root schema
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(TraceObject container, Register register);
|
||||
|
||||
/**
|
||||
* Get the expected path where an object defining the register value would be
|
||||
*
|
||||
* @see #getConventionalRegisterPath(TargetObjectSchema, List, Register)
|
||||
* @param container the target register container
|
||||
* @param register the platform register
|
||||
* @return the path matcher, possibly empty
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(TargetObject container, Register register);
|
||||
|
||||
/**
|
||||
* Get the expected path where an object defining the register value would be
|
||||
*
|
||||
* @see #getConventionalRegisterPath(TargetObjectSchema, List, Register)
|
||||
* @param overlay the overlay space allocated for a thread or frame
|
||||
* @param register the platform register
|
||||
* @return the path matcher, or null if there is no root schema
|
||||
|
|
|
@ -15,15 +15,13 @@
|
|||
*/
|
||||
package ghidra.trace.model.listing;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
import ghidra.util.IntersectionAddressSetView;
|
||||
import ghidra.util.UnionAddressSetView;
|
||||
|
@ -251,15 +249,6 @@ public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
|
|||
*/
|
||||
boolean intersectsRange(TraceAddressSnapRange range);
|
||||
|
||||
/**
|
||||
* Get the set of registers for the trace's base language
|
||||
*
|
||||
* @return the register set
|
||||
*/
|
||||
default Set<Register> getRegisters() {
|
||||
return new HashSet<>(getTrace().getBaseLanguage().getRegisters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unit (or component of a structure) which spans exactly the addresses of the given
|
||||
* register
|
||||
|
@ -267,28 +256,32 @@ public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
|
|||
* @param register the register
|
||||
* @return the unit or {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default T getForRegister(long snap, Register register) {
|
||||
// Find a code unit which contains the register completely
|
||||
T candidate = getContaining(snap, register.getAddress());
|
||||
if (candidate == null) {
|
||||
return null;
|
||||
}
|
||||
AddressRange range = TraceRegisterUtils.rangeForRegister(register);
|
||||
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
|
||||
if (cmpMax > 0) {
|
||||
return null;
|
||||
}
|
||||
if (cmpMax == 0 && candidate.getMinAddress().equals(register.getAddress())) {
|
||||
return candidate;
|
||||
}
|
||||
if (!(candidate instanceof TraceData)) {
|
||||
return null;
|
||||
}
|
||||
TraceData data = (TraceData) candidate;
|
||||
// Cast because if candidate is TraceData, T is, too
|
||||
// NOTE: It may not be a primitive
|
||||
return (T) TraceRegisterUtils.seekComponent(data, range);
|
||||
return getForRegister(getTrace().getPlatformManager().getHostPlatform(), snap, register);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unit (or component of a structure) which spans exactly the addresses of the given
|
||||
* platform register
|
||||
*
|
||||
* @param platform the platform whose language defines the register
|
||||
* @param register the register
|
||||
* @return the unit or {@code null}
|
||||
*/
|
||||
T getForRegister(TracePlatform platform, long snap, Register register);
|
||||
|
||||
/**
|
||||
* Get the unit which completely contains the given register
|
||||
*
|
||||
* <p>
|
||||
* This does not descend into structures.
|
||||
*
|
||||
* @param snap the snap during which the unit must be alive
|
||||
* @param register the register
|
||||
* @return the unit or {@code unit}
|
||||
*/
|
||||
default T getContaining(long snap, Register register) {
|
||||
return getContaining(getTrace().getPlatformManager().getHostPlatform(), snap, register);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,21 +290,12 @@ public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
|
|||
* <p>
|
||||
* This does not descend into structures.
|
||||
*
|
||||
* @platform the platform whose language defines the register
|
||||
* @param snap the snap during which the unit must be alive
|
||||
* @param register the register
|
||||
* @return the unit or {@code unit}
|
||||
*/
|
||||
default T getContaining(long snap, Register register) {
|
||||
T candidate = getContaining(snap, register.getAddress());
|
||||
if (candidate == null) {
|
||||
return null;
|
||||
}
|
||||
AddressRange range = TraceRegisterUtils.rangeForRegister(register);
|
||||
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
|
||||
if (cmpMax > 0) {
|
||||
return null;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
T getContaining(TracePlatform platform, long snap, Register register);
|
||||
|
||||
/**
|
||||
* Get the live units whose start addresses are within the given register
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.google.common.collect.Range;
|
|||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -64,4 +65,19 @@ public interface TraceBaseDefinedUnitsView<T extends TraceCodeUnit>
|
|||
throws CancelledException {
|
||||
clear(span, TraceRegisterUtils.rangeForRegister(register), true, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the units contained within the given span and platform register
|
||||
*
|
||||
* <p>
|
||||
* Any units alive before the given span are truncated instead of deleted.
|
||||
*
|
||||
* @param platform the platform whose language defines the register
|
||||
* @param span the span to clear
|
||||
* @param register the register
|
||||
* @param monitor a monitor for progress and cancellation
|
||||
* @throws CancelledException if the clear is cancelled
|
||||
*/
|
||||
void clear(TracePlatform platform, Range<Long> span, Register register, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
|
||||
/**
|
||||
|
@ -75,17 +75,24 @@ public interface TraceDefinedDataView extends TraceBaseDefinedUnitsView<TraceDat
|
|||
*/
|
||||
default TraceData create(Range<Long> lifespan, Register register, DataType dataType)
|
||||
throws CodeUnitInsertionException {
|
||||
// TODO: A better way to handle memory-mapped registers?
|
||||
Trace trace = getTrace();
|
||||
if (register.getAddressSpace() != trace
|
||||
.getBaseLanguage()
|
||||
.getAddressFactory()
|
||||
.getRegisterSpace()) {
|
||||
return trace.getCodeManager()
|
||||
.definedData()
|
||||
.create(lifespan, register.getAddress(), dataType, register.getNumBytes());
|
||||
}
|
||||
TraceRegisterUtils.requireByteBound(register);
|
||||
return create(lifespan, register.getAddress(), dataType, register.getNumBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a data unit on the given platform register
|
||||
*
|
||||
* <p>
|
||||
* If the register is memory mapped, this will delegate to the appropriate space. In those
|
||||
* cases, the assignment affects all threads.
|
||||
*
|
||||
* @param platform the platform whose language defines the register
|
||||
* @param lifespan the span for which the unit is effective
|
||||
* @param register the register to assign a data type
|
||||
* @param dataType the data type for the register
|
||||
* @return the new data unit
|
||||
* @throws CodeUnitInsertionException if there's a conflict
|
||||
*/
|
||||
TraceData create(TracePlatform platform, Range<Long> lifespan, Register register,
|
||||
DataType dataType) throws CodeUnitInsertionException;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,12 @@ public interface TraceMemoryManager extends TraceMemoryOperations {
|
|||
* space named after the path of each memory being recorded. Of course, the mapping still needs
|
||||
* to occur between the trace and parts of the display and during emulation.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: We are also moving away from (space, thread, frame) triples to uniquely identify
|
||||
* register storage. Instead, that will be encoded into the address space itself. Register
|
||||
* overlays will overlay register space as be named after the register container object, which
|
||||
* subsumes thread and frame when applicable.
|
||||
*
|
||||
* @param name the name of the new address space
|
||||
* @param base the space after which this is modeled
|
||||
* @return the create space
|
||||
|
@ -57,6 +63,21 @@ public interface TraceMemoryManager extends TraceMemoryOperations {
|
|||
AddressSpace createOverlayAddressSpace(String name, AddressSpace base)
|
||||
throws DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Get or create an overlay address space
|
||||
*
|
||||
* <p>
|
||||
* If the space already exists, and it overlays the given base, the existing space is returned.
|
||||
* If it overlays a different space, null is returned. If the space does not exist, it is
|
||||
* created with the given base space.
|
||||
*
|
||||
* @see #createOverlayAddressSpace(String, AddressSpace)
|
||||
* @param name the name of the address space
|
||||
* @param base the expected base space
|
||||
* @return the space, or null
|
||||
*/
|
||||
AddressSpace getOrCreateOverlayAddressSpace(String name, AddressSpace base);
|
||||
|
||||
/**
|
||||
* Delete an overlay address space
|
||||
*
|
||||
|
|
|
@ -27,7 +27,7 @@ import ghidra.trace.model.thread.TraceObjectThread;
|
|||
targetIf = TargetRegister.class,
|
||||
shortName = "register",
|
||||
fixedKeys = {
|
||||
TargetRegister.LENGTH_ATTRIBUTE_NAME
|
||||
TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME
|
||||
})
|
||||
public interface TraceObjectRegister extends TraceObjectInterface {
|
||||
String KEY_STATE = "_state";
|
||||
|
@ -36,7 +36,11 @@ public interface TraceObjectRegister extends TraceObjectInterface {
|
|||
|
||||
String getName();
|
||||
|
||||
int getLength();
|
||||
int getBitLength();
|
||||
|
||||
default int getByteLength() {
|
||||
return (getBitLength() + 7) / 8;
|
||||
}
|
||||
|
||||
void setValue(Range<Long> lifespan, byte[] value);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.google.common.collect.RangeSet;
|
|||
import ghidra.dbg.target.TargetMethod;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathPattern;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
|
@ -548,4 +549,25 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
}
|
||||
return getTrace().getObjectManager().getObjectByCanonicalPath(TraceObjectKeyPath.of(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a suitable register container
|
||||
*
|
||||
* @see TargetObjectSchema#searchForRegisterContainer(int, List)
|
||||
* @param frameLevel the frame level. Must be 0 if not applicable
|
||||
* @return the register container, or null
|
||||
*/
|
||||
default TraceObject queryRegisterContainer(int frameLevel) {
|
||||
PathPredicates regsMatcher = getRoot().getTargetSchema()
|
||||
.searchForRegisterContainer(frameLevel, getCanonicalPath().getKeyList());
|
||||
for (PathPattern regsPattern : regsMatcher.getPatterns()) {
|
||||
TraceObject regsObj = getTrace().getObjectManager()
|
||||
.getObjectByCanonicalPath(
|
||||
TraceObjectKeyPath.of(regsPattern.getSingletonPath()));
|
||||
if (regsObj != null) {
|
||||
return regsObj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.trace.model.time.schedule;
|
|||
import java.util.*;
|
||||
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
|
@ -523,16 +524,16 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||
* @param sleigh a single line of sleigh, excluding the terminating semicolon.
|
||||
* @return the resulting schedule
|
||||
*/
|
||||
public TraceSchedule patched(TraceThread thread, String sleigh) {
|
||||
public TraceSchedule patched(TraceThread thread, Language language, String sleigh) {
|
||||
if (!this.pSteps.isNop()) {
|
||||
Sequence pTicks = this.pSteps.clone();
|
||||
pTicks.advance(new PatchStep(thread.getKey(), sleigh));
|
||||
pTicks.coalescePatches(thread.getTrace().getBaseLanguage());
|
||||
pTicks.coalescePatches(language);
|
||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
||||
}
|
||||
Sequence ticks = this.steps.clone();
|
||||
ticks.advance(new PatchStep(keyOf(thread), sleigh));
|
||||
ticks.coalescePatches(thread.getTrace().getBaseLanguage());
|
||||
ticks.coalescePatches(language);
|
||||
return new TraceSchedule(snap, ticks, new Sequence());
|
||||
}
|
||||
|
||||
|
@ -543,20 +544,20 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||
* @param sleigh the lines of sleigh, excluding the terminating semicolons.
|
||||
* @return the resulting schedule
|
||||
*/
|
||||
public TraceSchedule patched(TraceThread thread, List<String> sleigh) {
|
||||
public TraceSchedule patched(TraceThread thread, Language language, List<String> sleigh) {
|
||||
if (!this.pSteps.isNop()) {
|
||||
Sequence pTicks = this.pSteps.clone();
|
||||
for (String line : sleigh) {
|
||||
pTicks.advance(new PatchStep(thread.getKey(), line));
|
||||
}
|
||||
pTicks.coalescePatches(thread.getTrace().getBaseLanguage());
|
||||
pTicks.coalescePatches(language);
|
||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
||||
}
|
||||
Sequence ticks = this.steps.clone();
|
||||
for (String line : sleigh) {
|
||||
ticks.advance(new PatchStep(thread.getKey(), line));
|
||||
}
|
||||
ticks.coalescePatches(thread.getTrace().getBaseLanguage());
|
||||
ticks.coalescePatches(language);
|
||||
return new TraceSchedule(snap, ticks, new Sequence());
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceData;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
|
@ -39,6 +40,27 @@ public enum TraceRegisterUtils {
|
|||
return new AddressRangeImpl(address, address.add(register.getNumBytes() - 1));
|
||||
}
|
||||
|
||||
public static AddressRange getOverlayRange(AddressSpace space, AddressRange range) {
|
||||
AddressSpace physical = space.getPhysicalSpace();
|
||||
if (physical == space || physical != range.getAddressSpace()) {
|
||||
return range;
|
||||
}
|
||||
return new AddressRangeImpl(
|
||||
space.getAddress(range.getMinAddress().getOffset()),
|
||||
space.getAddress(range.getMaxAddress().getOffset()));
|
||||
}
|
||||
|
||||
public static AddressSetView getOverlaySet(AddressSpace space, AddressSetView set) {
|
||||
if (!space.isOverlaySpace()) {
|
||||
return set;
|
||||
}
|
||||
AddressSet result = new AddressSet();
|
||||
for (AddressRange rng : set) {
|
||||
result.add(getOverlayRange(space, rng));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] padOrTruncate(byte[] arr, int length) {
|
||||
if (arr.length == length) {
|
||||
return arr;
|
||||
|
@ -141,8 +163,8 @@ public enum TraceRegisterUtils {
|
|||
return new RegisterValue(register, addr.getOffsetAsBigInteger());
|
||||
}
|
||||
|
||||
public static RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv, long snap,
|
||||
TraceMemorySpace regs, boolean requireKnown) {
|
||||
public static RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv,
|
||||
TracePlatform platform, long snap, TraceMemorySpace regs, boolean requireKnown) {
|
||||
Register reg = rv.getRegister();
|
||||
if (reg.isBaseRegister()) {
|
||||
return rv;
|
||||
|
@ -154,11 +176,11 @@ public enum TraceRegisterUtils {
|
|||
return rv.getBaseRegisterValue();
|
||||
}
|
||||
if (requireKnown) {
|
||||
if (TraceMemoryState.KNOWN != regs.getState(snap, reg.getBaseRegister())) {
|
||||
if (TraceMemoryState.KNOWN != regs.getState(platform, snap, reg.getBaseRegister())) {
|
||||
throw new IllegalStateException("Must fetch base register before setting a child");
|
||||
}
|
||||
}
|
||||
return regs.getValue(snap, reg.getBaseRegister()).combineValues(rv);
|
||||
return regs.getValue(platform, snap, reg.getBaseRegister()).combineValues(rv);
|
||||
}
|
||||
|
||||
public static ByteBuffer prepareBuffer(Register register) {
|
||||
|
|
|
@ -128,11 +128,11 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||
* Manipulate the trace's memory and registers using Sleigh
|
||||
*
|
||||
* @param snap the snap to modify
|
||||
* @param frame the frame to modify
|
||||
* @param thread the thread to modify, can be {@code null} if only memory is used
|
||||
* @param frame the frame to modify
|
||||
* @param sleigh the Sleigh source
|
||||
*/
|
||||
public void exec(long snap, int frame, TraceThread thread, String sleigh) {
|
||||
public void exec(long snap, TraceThread thread, int frame, String sleigh) {
|
||||
PcodeProgram program = SleighProgramCompiler.compileProgram((SleighLanguage) language,
|
||||
"builder", sleigh, PcodeUseropLibrary.nil());
|
||||
TraceSleighUtils.buildByteExecutor(trace, snap, thread, frame)
|
||||
|
@ -144,8 +144,8 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||
*
|
||||
* @param platform the platform whose language to use
|
||||
* @param snap the snap to modify
|
||||
* @param frame the frame to modify
|
||||
* @param thread the thread to modify, can be {@code null} if only memory is used
|
||||
* @param frame the frame to modify
|
||||
* @param sleigh the lines of Sleigh, including semicolons.
|
||||
*/
|
||||
public void exec(TracePlatform platform, long snap, TraceThread thread, int frame,
|
||||
|
|
|
@ -76,7 +76,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
.createOverlayAddressSpace("Targets[0].Threads[0].Registers",
|
||||
b.trace.getBaseAddressFactory().getRegisterSpace());
|
||||
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
getSLEIGH_X86_64_LANGUAGE().getCompilerSpecByID(new CompilerSpecID("gcc")));
|
||||
amd64.addMappedRegisterRange();
|
||||
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
RAX = amd64.getLanguage().getRegister("RAX");
|
||||
amd64.addRegisterMapOverride(RAX, "orig_rax");
|
||||
|
||||
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
|
||||
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
avr8.addMappedRange(b.addr(0),
|
||||
avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000);
|
||||
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
avr8.addMappedRange(b.addr(overlay, 0),
|
||||
avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000);
|
||||
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
|
||||
}
|
||||
|
||||
|
@ -251,7 +251,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
R0 = avr8.getLanguage().getRegister("R0");
|
||||
avr8.addRegisterMapOverride(R0, "orig_r0");
|
||||
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
|
||||
}
|
||||
|
||||
|
@ -285,7 +285,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
getSLEIGH_X86_64_LANGUAGE().getCompilerSpecByID(new CompilerSpecID("gcc")));
|
||||
amd64.addMappedRegisterRange();
|
||||
|
||||
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
|
||||
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
|
||||
|
||||
RAX = amd64.getLanguage().getRegister("RAX");
|
||||
|
@ -321,7 +321,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
avr8.addMappedRange(b.addr(0),
|
||||
avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000);
|
||||
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
|
||||
|
||||
R0 = avr8.getLanguage().getRegister("R0");
|
||||
|
@ -352,7 +352,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
.createOverlayAddressSpace("Targets[0].Threads[0].Registers",
|
||||
b.trace.getBaseAddressFactory().getRegisterSpace());
|
||||
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
|
||||
|
||||
amd64 = b.trace.getPlatformManager()
|
||||
|
@ -382,7 +382,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
TraceObjectKeyPath.parse("Targets[0].Threads[0].Registers.User[RAX]"));
|
||||
regRAX.insert(Range.atLeast(0L), ConflictResolution.DENY);
|
||||
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8);
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
|
||||
regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
|
||||
|
||||
amd64 = b.trace.getPlatformManager()
|
||||
|
|
|
@ -445,15 +445,16 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
thread = tb.trace.getThreadManager().createThread("Threads[0]", 0);
|
||||
}
|
||||
TraceSchedule time = TraceSchedule.parse("0");
|
||||
time = time.patched(thread, "r0l=1");
|
||||
time = time.patched(thread, tb.language, "r0l=1");
|
||||
assertEquals("0:t0-{r0l=0x1}", time.toString());
|
||||
time = time.patched(thread, "r0h=2");
|
||||
time = time.patched(thread, tb.language, "r0h=2");
|
||||
assertEquals("0:t0-{r0=0x200000001}", time.toString());
|
||||
time = time.patched(thread, "r1l=3").patched(thread, "*[ram]:4 0xcafe:8=0xdeadbeef");
|
||||
time = time.patched(thread, tb.language, "r1l=3")
|
||||
.patched(thread, tb.language, "*[ram]:4 0xcafe:8=0xdeadbeef");
|
||||
assertEquals("0:t0-{*:4 0xcafe:8=0xdeadbeef};t0-{r0=0x200000001};t0-{r1l=0x3}",
|
||||
time.toString());
|
||||
|
||||
time = time.patched(thread, "*:8 0xcb00:8 = 0x1122334455667788");
|
||||
time = time.patched(thread, tb.language, "*:8 0xcb00:8 = 0x1122334455667788");
|
||||
assertEquals("0:t0-{*:8 0xcafe:8=0xdead112233445566};t0-{*:2 0xcb06:8=0x7788};" +
|
||||
"t0-{r0=0x200000001};t0-{r1l=0x3}", time.toString());
|
||||
}
|
||||
|
|
|
@ -86,6 +86,16 @@ public class CachedAddressSetView implements AddressSetView {
|
|||
maxAddress = delegate.getMaxAddress();
|
||||
}
|
||||
|
||||
protected static void addMixed(AddressSet set, Address min, Address max) {
|
||||
if (min.getAddressSpace() == max.getAddressSpace()) {
|
||||
set.add(min, max);
|
||||
}
|
||||
else {
|
||||
set.add(min, min.getAddressSpace().getMaxAddress());
|
||||
set.add(max.getAddressSpace().getMinAddress(), max);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ensureKnown(Address min, Address max) {
|
||||
if (minAddress == null) {
|
||||
return;
|
||||
|
@ -101,21 +111,21 @@ public class CachedAddressSetView implements AddressSetView {
|
|||
if (rangesBackward.hasNext()) {
|
||||
AddressRange prev = rangesBackward.next();
|
||||
cache.add(prev);
|
||||
known.add(prev.getMinAddress(), min);
|
||||
addMixed(known, prev.getMinAddress(), min);
|
||||
}
|
||||
else {
|
||||
known.add(minAddress, min);
|
||||
addMixed(known, minAddress, min);
|
||||
}
|
||||
AddressRangeIterator rangesForward = delegate.getAddressRanges(min, true);
|
||||
while (true) {
|
||||
if (!rangesForward.hasNext()) {
|
||||
known.add(min, maxAddress);
|
||||
addMixed(known, min, maxAddress);
|
||||
break;
|
||||
}
|
||||
AddressRange next = rangesForward.next();
|
||||
cache.add(next);
|
||||
if (next.getMaxAddress().compareTo(max) >= 0) {
|
||||
known.add(min, next.getMaxAddress());
|
||||
addMixed(known, min, next.getMaxAddress());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
|
|||
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000)),
|
||||
new ProgramLocation(program, tb.addr(0x00400000)), 0x1000, false);
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
tb.exec(0, 0, thread, "RIP = 0x55550000;");
|
||||
tb.exec(0, thread, 0, "RIP = 0x55550000;");
|
||||
}
|
||||
waitForDomainObject(tb.trace);
|
||||
waitForPass(() -> assertEquals(new ProgramLocation(program, tb.addr(0x00400000)),
|
||||
|
|
Loading…
Reference in New Issue
Block a user