mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 14:54:29 +08:00
Merge remote-tracking branch
'origin/GP-1678_Dan_objectRecorder--SQUASHED' Conflicts: Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
This commit is contained in:
+1
-1
@@ -78,6 +78,6 @@ public abstract class AbstractModelForDbgengProcessActivationTest
|
||||
.collect(Collectors.toList())).trim();
|
||||
String procId = getIdFromCapture(line);
|
||||
assertEquals(expected.getPath(),
|
||||
getProcessPattern().applyIndices(procId).getSingletonPath());
|
||||
getProcessPattern().applyKeys(procId).getSingletonPath());
|
||||
}
|
||||
}
|
||||
|
||||
+7
-16
@@ -15,28 +15,19 @@
|
||||
*/
|
||||
package agent.frida.model;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import agent.frida.model.iface2.FridaModelTargetProcess;
|
||||
import agent.frida.model.iface2.FridaModelTargetThreadContainer;
|
||||
import agent.frida.model.impl.FridaModelTargetThreadContainerImpl;
|
||||
import ghidra.dbg.agent.DefaultTargetModelRoot;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelRegistersTest;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelTest;
|
||||
import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen;
|
||||
import ghidra.dbg.test.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForFridaX64RegistersTest
|
||||
@@ -94,7 +85,7 @@ public abstract class AbstractModelForFridaX64RegistersTest
|
||||
for (Entry<String, byte[]> ent : getRegisterWrites().entrySet()) {
|
||||
String regName = ent.getKey();
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
path, pred -> pred.applyIndices(regName), false);
|
||||
path, pred -> pred.applyKeys(regName), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
assertEquals(ent.getValue().length, (reg.getBitLength() + 7) / 8);
|
||||
}
|
||||
@@ -134,7 +125,7 @@ public abstract class AbstractModelForFridaX64RegistersTest
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
for (String name : exp.keySet()) {
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
||||
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
byte[] bytes = waitOn(bank.readRegister(reg));
|
||||
read.put(name, bytes);
|
||||
@@ -163,7 +154,7 @@ public abstract class AbstractModelForFridaX64RegistersTest
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
for (String name : write.keySet()) {
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
||||
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
waitOn(bank.writeRegister(reg, write.get(name)));
|
||||
|
||||
|
||||
+1
-1
@@ -83,6 +83,6 @@ public abstract class AbstractModelForGdbFrameActivationTest
|
||||
assertFalse(line.contains("\n"));
|
||||
assertTrue(line.startsWith("#"));
|
||||
String frameLevel = line.substring(1).split("\\s+")[0];
|
||||
assertEquals(expected.getPath(), STACK_PATTERN.applyIndices(frameLevel).getSingletonPath());
|
||||
assertEquals(expected.getPath(), STACK_PATTERN.applyKeys(frameLevel).getSingletonPath());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,6 +74,6 @@ public abstract class AbstractModelForGdbInferiorActivationTest
|
||||
.filter(l -> l.trim().startsWith("*"))
|
||||
.collect(Collectors.toList())).trim();
|
||||
String inferiorId = line.split("\\s+")[1];
|
||||
assertEquals(expected.getPath(), INF_PATTERN.applyIndices(inferiorId).getSingletonPath());
|
||||
assertEquals(expected.getPath(), INF_PATTERN.applyKeys(inferiorId).getSingletonPath());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -87,6 +87,6 @@ public abstract class AbstractModelForGdbThreadActivationTest
|
||||
.collect(Collectors.toList()));
|
||||
String threadGid = line.split("\\s+")[2];
|
||||
assertEquals(expected.getPath(),
|
||||
THREAD_PATTERN.applyIndices(threadGid, threadGid).getSingletonPath());
|
||||
THREAD_PATTERN.applyKeys(threadGid, threadGid).getSingletonPath());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ public abstract class AbstractModelForLldbScenarioX64RegistersTest
|
||||
for (String name : toWrite.keySet()) {
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
||||
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
waitOn(bank.writeRegister(reg, toWrite.get(name)));
|
||||
}
|
||||
|
||||
+3
-3
@@ -81,7 +81,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
||||
for (Entry<String, byte[]> ent : getRegisterWrites().entrySet()) {
|
||||
String regName = ent.getKey();
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
path, pred -> pred.applyIndices(regName), false);
|
||||
path, pred -> pred.applyKeys(regName), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
assertEquals(ent.getValue().length, (reg.getBitLength() + 7) / 8);
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
for (String name : exp.keySet()) {
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
||||
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
byte[] bytes = waitOn(bank.readRegister(reg));
|
||||
read.put(name, bytes);
|
||||
@@ -149,7 +149,7 @@ public abstract class AbstractModelForLldbX64RegistersTest
|
||||
for (TargetRegisterBank bank : banks.values()) {
|
||||
for (String name : write.keySet()) {
|
||||
Map<List<String>, TargetRegister> regs = m.findAll(TargetRegister.class,
|
||||
bank.getPath(), pred -> pred.applyIndices(name), false);
|
||||
bank.getPath(), pred -> pred.applyKeys(name), false);
|
||||
for (TargetRegister reg : regs.values()) {
|
||||
waitOn(bank.writeRegister(reg, write.get(name)));
|
||||
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||
Trace trace = current.getTrace();
|
||||
TraceRecorder recorder = current.getRecorder();
|
||||
BackgroundUtils.async(tool, trace, NAME, true, true, false,
|
||||
(__, monitor) -> recorder.captureProcessMemory(selection, monitor, false));
|
||||
(__, monitor) -> recorder.readMemoryBlocks(selection, monitor, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +78,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||
}
|
||||
TraceRecorder recorder = current.getRecorder();
|
||||
// TODO: Either allow partial, or provide action to intersect with accessible
|
||||
if (!recorder.getAccessibleProcessMemory().contains(selection)) {
|
||||
if (!recorder.getAccessibleMemory().contains(selection)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ public class PCLocationTrackingSpec implements RegisterLocationTrackingSpec {
|
||||
if (frame == null) {
|
||||
return null;
|
||||
}
|
||||
return frame.getProgramCounter();
|
||||
return frame.getProgramCounter(snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -56,7 +56,7 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
}
|
||||
TraceRecorder recorder = coordinates.getRecorder();
|
||||
AddressSet visibleAccessible =
|
||||
recorder.getAccessibleProcessMemory().intersect(visible);
|
||||
recorder.getAccessibleMemory().intersect(visible);
|
||||
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
||||
AddressSetView alreadyKnown =
|
||||
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
||||
@@ -67,6 +67,6 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
|
||||
return recorder.captureProcessMemory(toRead, TaskMonitor.DUMMY, false);
|
||||
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
}
|
||||
TraceRecorder recorder = coordinates.getRecorder();
|
||||
AddressSet visibleAccessible =
|
||||
recorder.getAccessibleProcessMemory().intersect(visible);
|
||||
recorder.getAccessibleMemory().intersect(visible);
|
||||
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
|
||||
AddressSetView alreadyKnown =
|
||||
mm.getAddressesWithState(coordinates.getSnap(), visibleAccessible,
|
||||
@@ -92,6 +92,6 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
|
||||
return recorder.captureProcessMemory(toRead, TaskMonitor.DUMMY, false);
|
||||
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -811,7 +811,7 @@ public class DebuggerCopyIntoProgramDialog extends DialogComponentProvider {
|
||||
synchronized (this) {
|
||||
monitor.checkCanceled();
|
||||
CompletableFuture<NavigableMap<Address, byte[]>> recCapture =
|
||||
recorder.captureProcessMemory(new AddressSet(range), monitor, false);
|
||||
recorder.readMemoryBlocks(new AddressSet(range), monitor, false);
|
||||
this.captureTask = recCapture.thenCompose(__ -> {
|
||||
return recorder.getTarget().getModel().flushEvents();
|
||||
}).thenCompose(__ -> {
|
||||
|
||||
+16
-3
@@ -854,6 +854,14 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
|
||||
}
|
||||
|
||||
public boolean isRoot(ActionContext context) {
|
||||
TargetObject object = this.getObjectFromContext(context);
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
return object.isRoot();
|
||||
}
|
||||
|
||||
public boolean isInstance(ActionContext context, Class<? extends TargetObject> clazz) {
|
||||
TargetObject object = this.getObjectFromContext(context);
|
||||
if (object == null) {
|
||||
@@ -1128,8 +1136,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
.popupMenuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex)
|
||||
.popupMenuIcon(AbstractRecordAction.ICON)
|
||||
.helpLocation(new HelpLocation(plugin.getName(), "record"))
|
||||
.enabledWhen(ctx -> isInstance(ctx, TargetProcess.class))
|
||||
.popupWhen(ctx -> isInstance(ctx, TargetProcess.class))
|
||||
.enabledWhen(ctx -> isInstance(ctx, TargetProcess.class) || isRoot(ctx))
|
||||
.popupWhen(ctx -> isInstance(ctx, TargetProcess.class) || isRoot(ctx))
|
||||
.onAction(ctx -> performStartRecording(ctx))
|
||||
.enabled(true)
|
||||
.buildAndInstallLocal(this);
|
||||
@@ -1592,7 +1600,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
}
|
||||
}
|
||||
|
||||
public void startRecording(TargetProcess targetObject, boolean prompt) {
|
||||
public void startRecording(TargetObject targetObject, boolean prompt) {
|
||||
TraceRecorder rec = modelService.getRecorder(targetObject);
|
||||
if (rec != null) {
|
||||
return; // Already being recorded
|
||||
@@ -1630,6 +1638,11 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
}
|
||||
|
||||
public void performStartRecording(ActionContext context) {
|
||||
TargetObject maybeRoot = getObjectFromContext(context);
|
||||
if (maybeRoot.isRoot()) {
|
||||
startRecording(maybeRoot, true);
|
||||
return;
|
||||
}
|
||||
performAction(context, false, TargetProcess.class, proc -> {
|
||||
TargetProcess valid = DebugModelConventions.liveProcessOrNull(proc);
|
||||
if (valid != null) {
|
||||
|
||||
+1
@@ -410,6 +410,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
||||
return;
|
||||
}
|
||||
if (currentStack == stack) {
|
||||
stackTableModel.fireTableDataChanged();
|
||||
return;
|
||||
}
|
||||
currentStack = stack;
|
||||
|
||||
+17
-8
@@ -28,41 +28,50 @@ import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class StackFrameRow {
|
||||
public static class Synthetic extends StackFrameRow {
|
||||
private Address pc;
|
||||
|
||||
public Synthetic(DebuggerStackProvider provider, Address pc) {
|
||||
super(provider, pc);
|
||||
super(provider);
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
public void updateProgramCounter(Address pc) {
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getProgramCounter() {
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
|
||||
private final DebuggerStackProvider provider;
|
||||
|
||||
final TraceStackFrame frame;
|
||||
private int level;
|
||||
Address pc;
|
||||
|
||||
public StackFrameRow(DebuggerStackProvider provider, TraceStackFrame frame) {
|
||||
this.provider = provider;
|
||||
this.frame = frame;
|
||||
this.level = frame.getLevel();
|
||||
this.pc = frame.getProgramCounter();
|
||||
}
|
||||
|
||||
private StackFrameRow(DebuggerStackProvider provider, Address pc) {
|
||||
private StackFrameRow(DebuggerStackProvider provider) {
|
||||
this.provider = provider;
|
||||
this.frame = null;
|
||||
this.level = 0;
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
public int getFrameLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public long getSnap() {
|
||||
return provider.current.getSnap();
|
||||
}
|
||||
|
||||
public Address getProgramCounter() {
|
||||
return pc;
|
||||
return frame.getProgramCounter(getSnap());
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
@@ -88,11 +97,12 @@ public class StackFrameRow {
|
||||
if (curThread == null) {
|
||||
return null;
|
||||
}
|
||||
Address pc = getProgramCounter();
|
||||
if (pc == null) {
|
||||
return null;
|
||||
}
|
||||
TraceLocation dloc = new DefaultTraceLocation(curThread.getTrace(),
|
||||
curThread, Range.singleton(provider.current.getSnap()), pc);
|
||||
curThread, Range.singleton(getSnap()), pc);
|
||||
ProgramLocation sloc = provider.mappingService.getOpenMappedLocation(dloc);
|
||||
if (sloc == null) {
|
||||
return null;
|
||||
@@ -103,6 +113,5 @@ public class StackFrameRow {
|
||||
protected void update() {
|
||||
assert frame != null; // Should never update a synthetic stack
|
||||
level = frame.getLevel();
|
||||
pc = frame.getProgramCounter();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -578,11 +578,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
||||
if (someLoc == null) {
|
||||
return null;
|
||||
}
|
||||
TraceProgramView view = current.getView();
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
if (someLoc.getProgram() instanceof TraceProgramView) {
|
||||
Program program = someLoc.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
if (program instanceof TraceProgramView) {
|
||||
return someLoc;
|
||||
}
|
||||
return mappingService.getDynamicLocationFromStatic(view, someLoc);
|
||||
|
||||
+4
-3
@@ -101,6 +101,7 @@ public interface DebuggerMappingOpinion extends ExtensionPoint {
|
||||
*/
|
||||
public default Set<DebuggerMappingOffer> getOffers(TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
// TODO: Remove this check?
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -117,13 +118,13 @@ public interface DebuggerMappingOpinion extends ExtensionPoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce this opinion's offers for the given environment and target process
|
||||
* Produce this opinion's offers for the given environment and target
|
||||
*
|
||||
* @param env the environment associated with the target
|
||||
* @param process the target process
|
||||
* @param target the target (usually a process)
|
||||
* @param includeOverrides true to include override offers, i.e., those with negative confidence
|
||||
* @return the offers, possibly empty, but never null
|
||||
*/
|
||||
Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides);
|
||||
}
|
||||
|
||||
+9
-4
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.mapping;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public interface DebuggerMemoryMapper {
|
||||
/**
|
||||
@@ -33,7 +32,10 @@ public interface DebuggerMemoryMapper {
|
||||
* @param traceRange the range in the view's address space
|
||||
* @return the "same range" in the target's address space
|
||||
*/
|
||||
AddressRange traceToTarget(AddressRange traceRange);
|
||||
default AddressRange traceToTarget(AddressRange traceRange) {
|
||||
return new AddressRangeImpl(traceToTarget(traceRange.getMinAddress()),
|
||||
traceToTarget(traceRange.getMaxAddress()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the given address from the target process into the trace
|
||||
@@ -49,5 +51,8 @@ public interface DebuggerMemoryMapper {
|
||||
* @param targetRange the range in the target's address space
|
||||
* @return the "same range" in the trace's address space
|
||||
*/
|
||||
AddressRange targetToTrace(AddressRange targetRange);
|
||||
default AddressRange targetToTrace(AddressRange targetRange) {
|
||||
return new AddressRangeImpl(targetToTrace(targetRange.getMinAddress()),
|
||||
targetToTrace(targetRange.getMaxAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
-12
@@ -45,12 +45,6 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
||||
public Address traceToTarget(Address traceAddr) {
|
||||
assert isInFactory(traceAddr, traceAddressFactory);
|
||||
return toSameNamedSpace(traceAddr, targetAddressFactory);
|
||||
};
|
||||
|
||||
@Override
|
||||
public AddressRange traceToTarget(AddressRange traceRange) {
|
||||
return new AddressRangeImpl(traceToTarget(traceRange.getMinAddress()),
|
||||
traceToTarget(traceRange.getMaxAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,10 +59,4 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
||||
assert isInFactory(targetAddr, targetAddressFactory);
|
||||
return toSameNamedSpace(targetAddr, traceAddressFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange targetToTrace(AddressRange targetRange) {
|
||||
return new AddressRangeImpl(targetToTrace(targetRange.getMinAddress()),
|
||||
targetToTrace(targetRange.getMaxAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -35,12 +35,12 @@ public class DefaultDebuggerTargetTraceMapper implements DebuggerTargetTraceMapp
|
||||
protected final Set<String> extraRegNames;
|
||||
|
||||
public DefaultDebuggerTargetTraceMapper(TargetObject target, LanguageID langID,
|
||||
CompilerSpecID csId, Collection<String> extraRegNames)
|
||||
CompilerSpecID csID, Collection<String> extraRegNames)
|
||||
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||
this.target = target;
|
||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||
this.language = langServ.getLanguage(langID);
|
||||
this.cSpec = language.getCompilerSpecByID(csId);
|
||||
this.cSpec = language.getCompilerSpecByID(csID);
|
||||
|
||||
this.extraRegNames = Set.copyOf(extraRegNames);
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.lang.*;
|
||||
|
||||
public class ObjectBasedDebuggerMappingOffer extends DefaultDebuggerMappingOffer {
|
||||
private static final String DESCRIPTION =
|
||||
"EXPERIMENTAL: Object-based recording, deferred mapping";
|
||||
private static final int CONFIDENCE = -100; // TODO: Increase this when it becomes preferred
|
||||
protected static final LanguageID LANGID_DATA64 = new LanguageID("DATA:BE:64:default");
|
||||
protected static final CompilerSpecID CSID_PTR64 = new CompilerSpecID("pointer64");
|
||||
|
||||
public ObjectBasedDebuggerMappingOffer(TargetObject target) {
|
||||
// TODO: Is extraRegNames relevant?
|
||||
super(target, CONFIDENCE, DESCRIPTION, LANGID_DATA64, CSID_PTR64, Set.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerTargetTraceMapper createMapper()
|
||||
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||
return new ObjectBasedDebuggerTargetTraceMapper(target, langID, csID, extraRegNames);
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
|
||||
public class ObjectBasedDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> getOffers(TargetObject target, boolean includeOverrides) {
|
||||
// TODO: Remove this check
|
||||
if (!includeOverrides) {
|
||||
return Set.of();
|
||||
}
|
||||
// TODO: Do I want to require it to record the whole model?
|
||||
// If not, I need to figure out how to locate object dependencies and still record them.
|
||||
if (!target.isRoot()) {
|
||||
return Set.of();
|
||||
}
|
||||
return Set.of(new ObjectBasedDebuggerMappingOffer(target));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class ObjectBasedDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
||||
protected final Trace trace;
|
||||
protected final AddressSpace base;
|
||||
|
||||
protected final Map<Integer, AddressSpace> targetToTraceSpaces = new HashMap<>();
|
||||
protected final Map<Integer, AddressSpace> traceToTargetSpaces = new HashMap<>();
|
||||
|
||||
public ObjectBasedDebuggerMemoryMapper(Trace trace) {
|
||||
this.trace = trace;
|
||||
this.base = trace.getBaseAddressFactory().getDefaultAddressSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address traceToTarget(Address traceAddr) {
|
||||
AddressSpace traceSpace = traceAddr.getAddressSpace();
|
||||
int traceIdHash = System.identityHashCode(traceSpace);
|
||||
AddressSpace targetSpace;
|
||||
synchronized (traceToTargetSpaces) {
|
||||
targetSpace = traceToTargetSpaces.get(traceIdHash);
|
||||
}
|
||||
/**
|
||||
* Can only be null if space is the default space or some non-physical space. In that case,
|
||||
* the target hasn't defined a space with that name, so no mapping.
|
||||
*/
|
||||
if (targetSpace == null) {
|
||||
return null;
|
||||
}
|
||||
return targetSpace.getAddress(traceAddr.getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address targetToTrace(Address targetAddr) {
|
||||
AddressSpace targetSpace = targetAddr.getAddressSpace();
|
||||
int targetIdHash = System.identityHashCode(targetSpace);
|
||||
AddressSpace traceSpace;
|
||||
synchronized (traceToTargetSpaces) {
|
||||
traceSpace = targetToTraceSpaces.get(targetIdHash);
|
||||
if (traceSpace == null) {
|
||||
traceSpace = createSpace(targetSpace.getName());
|
||||
targetToTraceSpaces.put(targetIdHash, traceSpace);
|
||||
traceToTargetSpaces.put(System.identityHashCode(traceSpace),
|
||||
targetSpace);
|
||||
}
|
||||
}
|
||||
return traceSpace.getAddress(targetAddr.getOffset());
|
||||
}
|
||||
|
||||
protected AddressSpace createSpace(String name) {
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(trace, "Create space for mapping", true)) {
|
||||
AddressFactory factory = trace.getBaseAddressFactory();
|
||||
AddressSpace space = factory.getAddressSpace(name);
|
||||
if (space == null) {
|
||||
return trace.getMemoryManager().createOverlayAddressSpace(name, base);
|
||||
}
|
||||
// Let the default space suffice for its own name
|
||||
// NB. if overlay already exists, we've already issued a warning
|
||||
if (space == base || space.isOverlaySpace()) {
|
||||
return space;
|
||||
}
|
||||
// Otherwise, do not allow non-physical spaces to be used by accident.
|
||||
return trace.getMemoryManager().createOverlayAddressSpace('_' + name, base);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.model.record.ObjectBasedTraceRecorder;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
public class ObjectBasedDebuggerTargetTraceMapper extends DefaultDebuggerTargetTraceMapper {
|
||||
|
||||
protected ObjectBasedDebuggerMemoryMapper memoryMapper;
|
||||
|
||||
public ObjectBasedDebuggerTargetTraceMapper(TargetObject target, LanguageID langID,
|
||||
CompilerSpecID csID, Collection<String> extraRegNames)
|
||||
throws LanguageNotFoundException, CompilerSpecNotFoundException {
|
||||
super(target, langID, csID, extraRegNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerMemoryMapper createMemoryMapper(TargetMemory memory) {
|
||||
// TODO: Validate regions to not overlap?
|
||||
// Could probably do that in unit testing of model instead
|
||||
return memoryMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerRegisterMapper createRegisterMapper(TargetRegisterContainer registers) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceRecorder startRecording(DebuggerModelServicePlugin service, Trace trace) {
|
||||
this.memoryMapper = new ObjectBasedDebuggerMemoryMapper(trace);
|
||||
return new ObjectBasedTraceRecorder(service, trace, target, this);
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -19,7 +19,8 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class OverridesDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!includeOverrides) {
|
||||
return Set.of();
|
||||
@@ -65,7 +66,7 @@ public class OverridesDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
// ALL THE SPECS!!!
|
||||
new LanguageCompilerSpecQuery(null, null, null, null, null))
|
||||
.stream()
|
||||
.map(lcsp -> offerForLanguageAndCSpec(process, endian, lcsp))
|
||||
.map(lcsp -> offerForLanguageAndCSpec(target, endian, lcsp))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -49,24 +49,28 @@ public class FridaArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
|
||||
protected static class FridaAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
||||
public FridaAarch64MacosOffer(TargetProcess process) {
|
||||
super(process, 50, "AARCH64/Frida on macos", LANG_ID_AARCH64,COMP_ID_DEFAULT,
|
||||
super(process, 50, "AARCH64/Frida on macos", LANG_ID_AARCH64, COMP_ID_DEFAULT,
|
||||
Set.of("cpsr"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includesOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
||||
return Set.of();
|
||||
}
|
||||
String arch = env.getArchitecture();
|
||||
boolean is64Bit = arch.contains("AARCH64") || arch.contains("arm64") || arch.contains("arm");
|
||||
boolean is64Bit =
|
||||
arch.contains("AARCH64") || arch.contains("arm64") || arch.contains("arm");
|
||||
String os = env.getOperatingSystem();
|
||||
if (os.contains("macos")) {
|
||||
if (is64Bit) {
|
||||
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
||||
return Set.of(new FridaAarch64MacosOffer(process));
|
||||
return Set.of(new FridaAarch64MacosOffer((TargetProcess) target));
|
||||
}
|
||||
}
|
||||
return Set.of();
|
||||
|
||||
+6
-3
@@ -49,14 +49,17 @@ public class LldbArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
|
||||
protected static class LldbAarch64MacosOffer extends DefaultDebuggerMappingOffer {
|
||||
public LldbAarch64MacosOffer(TargetProcess process) {
|
||||
super(process, 50, "AARCH64/LLDB on macos", LANG_ID_AARCH64,COMP_ID_DEFAULT,
|
||||
super(process, 50, "AARCH64/LLDB on macos", LANG_ID_AARCH64, COMP_ID_DEFAULT,
|
||||
Set.of("cpsr"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includesOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -66,7 +69,7 @@ public class LldbArmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
if (os.contains("macos")) {
|
||||
if (is64Bit) {
|
||||
Msg.info(this, "Using os=" + os + " arch=" + arch);
|
||||
return Set.of(new LldbAarch64MacosOffer(process));
|
||||
return Set.of(new LldbAarch64MacosOffer((TargetProcess) target));
|
||||
}
|
||||
}
|
||||
return Set.of();
|
||||
|
||||
+5
-2
@@ -75,15 +75,18 @@ public class DbgengX64DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (env == null || !env.getDebugger().toLowerCase().contains("dbg")) {
|
||||
return Set.of();
|
||||
}
|
||||
boolean is64Bit =
|
||||
env.getArchitecture().contains("x86_64") || env.getArchitecture().contains("x64_32");
|
||||
if (is64Bit) {
|
||||
return Set.of(new DbgI386X86_64WindowsOffer(process));
|
||||
return Set.of(new DbgI386X86_64WindowsOffer((TargetProcess) target));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
||||
try {
|
||||
// This is on its own task thread, so whatever.
|
||||
// Just don't hang it indefinitely.
|
||||
recorder.captureProcessMemory(set, TaskMonitor.DUMMY, false)
|
||||
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY, false)
|
||||
.get(1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
|
||||
+12
-7
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.frida;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.util.Msg;
|
||||
@@ -71,16 +70,22 @@ public class FridaX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("frida")) {
|
||||
return Set.of();
|
||||
}
|
||||
String arch = env.getArchitecture();
|
||||
boolean is32Bit = arch.contains("ia32") ||arch.contains("x86-32") || arch.contains("i386") ||
|
||||
arch.contains("x86_32");
|
||||
boolean is64Bit = arch.contains("x64") ||arch.contains("x86-64") || arch.contains("x64-32") ||
|
||||
arch.contains("x86_64") || arch.contains("x64_32") || arch.contains("i686");
|
||||
boolean is32Bit =
|
||||
arch.contains("ia32") || arch.contains("x86-32") || arch.contains("i386") ||
|
||||
arch.contains("x86_32");
|
||||
boolean is64Bit =
|
||||
arch.contains("x64") || arch.contains("x86-64") || arch.contains("x64-32") ||
|
||||
arch.contains("x86_64") || arch.contains("x64_32") || arch.contains("i686");
|
||||
String os = env.getOperatingSystem();
|
||||
if (os.contains("darwin")) {
|
||||
if (is64Bit) {
|
||||
|
||||
+4
-3
@@ -21,7 +21,8 @@ import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
|
||||
@@ -81,7 +82,7 @@ public class DefaultGdbDebuggerMappingOpinion implements DebuggerMappingOpinion
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!isGdb(env)) {
|
||||
return Set.of();
|
||||
@@ -90,7 +91,7 @@ public class DefaultGdbDebuggerMappingOpinion implements DebuggerMappingOpinion
|
||||
String arch = env.getArchitecture();
|
||||
|
||||
return getCompilerSpecsForGnu(arch, endian).stream()
|
||||
.flatMap(lcsp -> offersForLanguageAndCSpec(process, arch, endian, lcsp).stream())
|
||||
.flatMap(lcsp -> offersForLanguageAndCSpec(target, arch, endian, lcsp).stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
@@ -39,8 +38,11 @@ public class GdbM68kDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -54,7 +56,7 @@ public class GdbM68kDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
String arch = env.getArchitecture();
|
||||
if (arch.startsWith("m68k")) {
|
||||
return Set.of(new GdbM68kBELinux32DefOffer(process));
|
||||
return Set.of(new GdbM68kBELinux32DefOffer((TargetProcess) target));
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
@@ -79,8 +78,12 @@ public class GdbMipsDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
|
||||
@@ -65,8 +64,12 @@ public class GdbPowerPCDebuggerMappingOpinion implements DebuggerMappingOpinion
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+5
-1
@@ -100,8 +100,12 @@ public class GdbX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("gdb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+5
-2
@@ -64,8 +64,11 @@ public class JdiDalvikDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().contains("Java Debug Interface")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -73,6 +76,6 @@ public class JdiDalvikDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
return Set.of();
|
||||
}
|
||||
// NOTE: Not worried about JRE version
|
||||
return Set.of(new DalvikDebuggerMappingOffer(process));
|
||||
return Set.of(new DalvikDebuggerMappingOffer((TargetProcess) target));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -64,8 +64,11 @@ public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!env.getDebugger().contains("Java Debug Interface")) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -73,6 +76,6 @@ public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
return Set.of();
|
||||
}
|
||||
// NOTE: Not worried about JRE version
|
||||
return Set.of(new JavaDebuggerMappingOffer(process));
|
||||
return Set.of(new JavaDebuggerMappingOffer((TargetProcess) target));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.platform.lldb;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.util.Msg;
|
||||
@@ -71,8 +70,12 @@ public class LldbX86DebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetProcess process,
|
||||
public Set<DebuggerMappingOffer> offersForEnv(TargetEnvironment env, TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
if (!(target instanceof TargetProcess)) {
|
||||
return Set.of();
|
||||
}
|
||||
TargetProcess process = (TargetProcess) target;
|
||||
if (!env.getDebugger().toLowerCase().contains("lldb")) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
+5
@@ -245,6 +245,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||
Msg.info(this, "Ignoring " + tb +
|
||||
" changed until service has finished loading its trace");
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
// TODO: This catch clause should not be necessary.
|
||||
Msg.error(this,
|
||||
"!!!! Object-based breakpoint emitted event without a spec: " + tb);
|
||||
}
|
||||
}
|
||||
|
||||
private void breakpointLifespanChanged(TraceAddressSpace spaceIsNull,
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public class ReadsTargetMemoryPcodeExecutorState
|
||||
if (!isLive()) {
|
||||
return false;
|
||||
}
|
||||
waitTimeout(recorder.captureProcessMemory(unknown, TaskMonitor.DUMMY, false));
|
||||
waitTimeout(recorder.readMemoryBlocks(unknown, TaskMonitor.DUMMY, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+5
-56
@@ -21,11 +21,11 @@ import java.util.concurrent.CompletableFuture;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedMemoryRecorder;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.app.plugin.core.debug.service.model.record.RecorderUtils;
|
||||
import ghidra.dbg.target.TargetMemory;
|
||||
import ghidra.dbg.target.TargetMemoryRegion;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.util.Msg;
|
||||
@@ -35,20 +35,7 @@ import ghidra.util.task.TaskMonitor;
|
||||
public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
||||
|
||||
// For large memory captures
|
||||
private static final int BLOCK_SIZE = 4096;
|
||||
private static final long BLOCK_MASK = -1L << 12;
|
||||
|
||||
protected static AddressSetView expandToBlocks(AddressSetView asv) {
|
||||
AddressSet result = new AddressSet();
|
||||
// Not terribly efficient, but this is one range most of the time
|
||||
for (AddressRange range : asv) {
|
||||
AddressSpace space = range.getAddressSpace();
|
||||
Address min = space.getAddress(range.getMinAddress().getOffset() & BLOCK_MASK);
|
||||
Address max = space.getAddress(range.getMaxAddress().getOffset() | ~BLOCK_MASK);
|
||||
result.add(new AddressRangeImpl(min, max));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private static final int BLOCK_BITS = 12; // 4096 bytes
|
||||
|
||||
private final DefaultTraceRecorder recorder;
|
||||
private final Trace trace;
|
||||
@@ -62,45 +49,7 @@ public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
|
||||
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
||||
TaskMonitor monitor, boolean toMap) {
|
||||
// TODO: Figure out how to display/select per-thread memory.
|
||||
// Probably need a thread parameter passed in then?
|
||||
// NOTE: That thread memory will already be chained to process memory. Good.
|
||||
|
||||
// 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
|
||||
|
||||
int total = 0;
|
||||
AddressSetView expSet = expandToBlocks(set)
|
||||
.intersect(trace.getMemoryManager().getRegionsAddressSet(recorder.getSnap()));
|
||||
for (AddressRange r : expSet) {
|
||||
total += Long.divideUnsigned(r.getLength() + BLOCK_SIZE - 1, BLOCK_SIZE);
|
||||
}
|
||||
monitor.initialize(total);
|
||||
monitor.setMessage("Capturing memory");
|
||||
// TODO: Read blocks in parallel? Probably NO. Tends to overload the agent.
|
||||
NavigableMap<Address, byte[]> result = toMap ? new TreeMap<>() : null;
|
||||
return AsyncUtils.each(TypeSpec.VOID, expSet.iterator(), (r, loop) -> {
|
||||
AddressRangeChunker blocks = new AddressRangeChunker(r, BLOCK_SIZE);
|
||||
AsyncUtils.each(TypeSpec.VOID, blocks.iterator(), (vBlk, inner) -> {
|
||||
// The listener in the recorder will copy to the Trace.
|
||||
monitor.incrementProgress(1);
|
||||
AddressRange tBlk = recorder.getMemoryMapper().traceToTarget(vBlk);
|
||||
recorder.getProcessMemory()
|
||||
.readMemory(tBlk.getMinAddress(), (int) tBlk.getLength())
|
||||
.thenAccept(data -> {
|
||||
if (toMap) {
|
||||
result.put(tBlk.getMinAddress(), data);
|
||||
}
|
||||
})
|
||||
.exceptionally(e -> {
|
||||
Msg.error(this, "Error reading block " + tBlk + ": " + e);
|
||||
// NOTE: Above may double log, since recorder listens for errors, too
|
||||
return null; // Continue looping on errors
|
||||
})
|
||||
.thenApply(__ -> !monitor.isCancelled())
|
||||
.handle(inner::repeatWhile);
|
||||
}).thenApply(v -> !monitor.isCancelled()).handle(loop::repeatWhile);
|
||||
}).thenApply(__ -> result);
|
||||
return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor, toMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public class DefaultStackRecorder implements ManagedStackRecorder {
|
||||
|
||||
public void doRecordFrame(TraceStack traceStack, int frameLevel, Address pc) {
|
||||
TraceStackFrame traceFrame = traceStack.getFrame(frameLevel, true);
|
||||
traceFrame.setProgramCounter(pc);
|
||||
traceFrame.setProgramCounter(null, pc); // Not object-based, so span=null
|
||||
}
|
||||
|
||||
public void recordFrame(TargetStackFrame frame) {
|
||||
|
||||
+11
-14
@@ -22,6 +22,8 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.*;
|
||||
import ghidra.app.plugin.core.debug.service.model.record.DataTypeRecorder;
|
||||
import ghidra.app.plugin.core.debug.service.model.record.SymbolRecorder;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.app.services.TraceRecorderListener;
|
||||
import ghidra.async.AsyncLazyValue;
|
||||
@@ -64,11 +66,11 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
TraceObjectManager objectManager;
|
||||
|
||||
DefaultBreakpointRecorder breakpointRecorder;
|
||||
DefaultDataTypeRecorder datatypeRecorder;
|
||||
DataTypeRecorder datatypeRecorder;
|
||||
DefaultMemoryRecorder memoryRecorder;
|
||||
DefaultModuleRecorder moduleRecorder;
|
||||
DefaultProcessRecorder processRecorder;
|
||||
DefaultSymbolRecorder symbolRecorder;
|
||||
SymbolRecorder symbolRecorder;
|
||||
DefaultTimeRecorder timeRecorder;
|
||||
|
||||
//protected final PermanentTransactionExecutor seqTx;
|
||||
@@ -94,10 +96,10 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
|
||||
this.processRecorder = new DefaultProcessRecorder(this);
|
||||
this.breakpointRecorder = new DefaultBreakpointRecorder(this);
|
||||
this.datatypeRecorder = new DefaultDataTypeRecorder(this);
|
||||
this.datatypeRecorder = new DataTypeRecorder(this);
|
||||
this.memoryRecorder = new DefaultMemoryRecorder(this);
|
||||
this.moduleRecorder = new DefaultModuleRecorder(this);
|
||||
this.symbolRecorder = new DefaultSymbolRecorder(this);
|
||||
this.symbolRecorder = new SymbolRecorder(this);
|
||||
this.timeRecorder = new DefaultTimeRecorder(this);
|
||||
this.objectManager = new TraceObjectManager(target, mapper, this);
|
||||
}
|
||||
@@ -266,7 +268,7 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
/*---------------- CAPTURE METHODS -------------------*/
|
||||
|
||||
@Override
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set,
|
||||
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set,
|
||||
TaskMonitor monitor, boolean toMap) {
|
||||
if (set.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(new TreeMap<>());
|
||||
@@ -499,12 +501,6 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
|
||||
/*---------------- LISTENER METHODS -------------------*/
|
||||
|
||||
// UNUSED?
|
||||
@Override
|
||||
public TraceEventListener getListenerForRecord() {
|
||||
return objectManager.getEventListener();
|
||||
}
|
||||
|
||||
public ListenerSet<TraceRecorderListener> getListeners() {
|
||||
return objectManager.getListeners();
|
||||
}
|
||||
@@ -526,17 +522,17 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getAccessibleProcessMemory() {
|
||||
public AddressSetView getAccessibleMemory() {
|
||||
return processRecorder.getAccessibleProcessMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> readProcessMemory(Address start, int length) {
|
||||
public CompletableFuture<byte[]> readMemory(Address start, int length) {
|
||||
return processRecorder.readProcessMemory(start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeProcessMemory(Address start, byte[] data) {
|
||||
public CompletableFuture<Void> writeMemory(Address start, byte[] data) {
|
||||
return processRecorder.writeProcessMemory(start, data);
|
||||
}
|
||||
|
||||
@@ -566,6 +562,7 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
return true;
|
||||
}
|
||||
|
||||
// UNUSED?
|
||||
@Override
|
||||
public CompletableFuture<Void> flushTransactions() {
|
||||
return parTx.flush();
|
||||
|
||||
+16
-2
@@ -24,20 +24,22 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import ghidra.app.plugin.core.debug.utils.DefaultTransactionCoalescer;
|
||||
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer;
|
||||
import ghidra.app.plugin.core.debug.utils.TransactionCoalescer.CoalescedTx;
|
||||
import ghidra.framework.model.DomainObjectException;
|
||||
import ghidra.framework.model.UndoableDomainObject;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.ClosedException;
|
||||
|
||||
public class PermanentTransactionExecutor {
|
||||
|
||||
private final TransactionCoalescer txc;
|
||||
private final Executor[] threads;
|
||||
private final ExecutorService[] threads;
|
||||
private final UndoableDomainObject obj;
|
||||
|
||||
public PermanentTransactionExecutor(UndoableDomainObject obj, String name, int threadCount,
|
||||
int delayMs) {
|
||||
this.obj = obj;
|
||||
txc = new DefaultTransactionCoalescer<>(obj, RecorderPermanentTransaction::start, delayMs);
|
||||
this.threads = new Executor[threadCount];
|
||||
this.threads = new ExecutorService[threadCount];
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
.namingPattern(name + "thread-" + i + "-%d")
|
||||
@@ -46,6 +48,12 @@ public class PermanentTransactionExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdownNow() {
|
||||
for (ExecutorService t : threads) {
|
||||
t.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This hash is borrowed from {@link HashMap}, except for the power-of-two masking, since I
|
||||
* don't want to force the thread count to be a power of two (though it probably is). In the
|
||||
@@ -70,6 +78,12 @@ public class PermanentTransactionExecutor {
|
||||
try (CoalescedTx tx = txc.start(description)) {
|
||||
runnable.run();
|
||||
}
|
||||
catch (DomainObjectException e) {
|
||||
if (e.getCause() instanceof ClosedException) {
|
||||
Msg.info(this, obj + " is closed. Shutting down transaction executor.");
|
||||
shutdownNow();
|
||||
}
|
||||
}
|
||||
}, selectThread(sel)).exceptionally(e -> {
|
||||
Msg.error(this, "Trouble recording " + description, e);
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class RecorderPermanentTransaction implements AutoCloseable {
|
||||
|
||||
static RecorderPermanentTransaction start(UndoableDomainObject obj, String description) {
|
||||
public static RecorderPermanentTransaction start(UndoableDomainObject obj, String description) {
|
||||
UndoableTransaction tid = UndoableTransaction.start(obj, description, true);
|
||||
return new RecorderPermanentTransaction(obj, tid);
|
||||
}
|
||||
|
||||
-7
@@ -79,13 +79,6 @@ public class RecorderSimpleMemory implements AbstractRecorderMemory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accessible memory, as viewed in the trace
|
||||
*
|
||||
* @param pred an additional predicate applied via "AND" with accessibility
|
||||
* @param memMapper target-to-trace mapping utility
|
||||
* @return the computed set
|
||||
*/
|
||||
@Override
|
||||
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
||||
DebuggerMemoryMapper memMapper) {
|
||||
|
||||
+7
@@ -32,6 +32,13 @@ public interface AbstractRecorderMemory {
|
||||
|
||||
public CompletableFuture<Void> writeMemory(Address address, byte[] data);
|
||||
|
||||
/**
|
||||
* Get accessible memory, as viewed in the trace
|
||||
*
|
||||
* @param pred an additional predicate applied via "AND" with accessibility
|
||||
* @param memMapper target-to-trace mapping utility
|
||||
* @return the computed set
|
||||
*/
|
||||
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
||||
DebuggerMemoryMapper memMapper);
|
||||
|
||||
|
||||
+7
-5
@@ -13,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model;
|
||||
package ghidra.app.plugin.core.debug.service.model.record;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.model.RecorderPermanentTransaction;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.async.AsyncFence;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.target.*;
|
||||
@@ -27,13 +29,13 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DefaultDataTypeRecorder {
|
||||
public class DataTypeRecorder {
|
||||
|
||||
//private DefaultTraceRecorder recorder;
|
||||
private Trace trace;
|
||||
//private TraceRecorder recorder;
|
||||
private final Trace trace;
|
||||
private final TargetDataTypeConverter typeConverter;
|
||||
|
||||
public DefaultDataTypeRecorder(DefaultTraceRecorder recorder) {
|
||||
public DataTypeRecorder(TraceRecorder recorder) {
|
||||
//this.recorder = recorder;
|
||||
this.trace = recorder.getTrace();
|
||||
this.typeConverter = new TargetDataTypeConverter(trace.getDataTypeManager());
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/* ###
|
||||
* 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.model.record;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerRegisterMapper;
|
||||
import ghidra.app.plugin.core.debug.register.RegisterTypeInfo;
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
||||
public class EmptyDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
||||
@Override
|
||||
public TargetRegister getTargetRegister(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register getTraceRegister(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetRegister traceToTarget(Register register) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register targetToTrace(TargetRegister tReg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisterTypeInfo getDefaultTypeInfo(Register lReg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Register> getRegistersOnTarget() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void targetRegisterAdded(TargetRegister register) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void targetRegisterRemoved(TargetRegister register) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/* ###
|
||||
* 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.model.record;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.dbg.error.DebuggerMemoryAccessException;
|
||||
import ghidra.dbg.target.TargetMemory;
|
||||
import ghidra.dbg.target.TargetMemoryRegion;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.util.Msg;
|
||||
import utilities.util.IDKeyed;
|
||||
|
||||
class MemoryRecorder {
|
||||
protected final ObjectBasedTraceRecorder recorder;
|
||||
protected final TraceMemoryManager memoryManager;
|
||||
|
||||
protected final Map<IDKeyed<AddressSpace>, TargetMemory> memoriesByTargetSpace =
|
||||
new HashMap<>();
|
||||
protected final Map<IDKeyed<TargetMemoryRegion>, AddressRange> regions = new HashMap<>();
|
||||
|
||||
protected MemoryRecorder(ObjectBasedTraceRecorder recorder) {
|
||||
this.recorder = recorder;
|
||||
this.memoryManager = recorder.trace.getMemoryManager();
|
||||
}
|
||||
|
||||
private TargetMemory getMemoryForSpace(AddressSpace space) {
|
||||
return memoriesByTargetSpace.get(new IDKeyed<>(space));
|
||||
}
|
||||
|
||||
private void addMemoryForSpace(AddressSpace targetSpace, TargetMemory memory) {
|
||||
TargetMemory exists =
|
||||
memoriesByTargetSpace.put(new IDKeyed<>(targetSpace), memory);
|
||||
if (exists != null && exists != memory) {
|
||||
Msg.warn(this,
|
||||
"Address space duplicated between memories: " + exists + " and " + memory);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addRegionMemory(TargetMemoryRegion region, TargetMemory memory) {
|
||||
addMemoryForSpace(region.getRange().getMinAddress().getAddressSpace(), memory);
|
||||
}
|
||||
|
||||
protected void adjustRegionRange(TargetMemoryRegion region, AddressRange range) {
|
||||
synchronized (regions) {
|
||||
AddressRange tRange = recorder.memoryMapper.targetToTrace(range);
|
||||
if (tRange == null) {
|
||||
regions.remove(new IDKeyed<>(region));
|
||||
}
|
||||
else {
|
||||
regions.put(new IDKeyed<>(region), tRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeMemory(TargetMemory memory) {
|
||||
while (memoriesByTargetSpace.values().remove(memory))
|
||||
;
|
||||
}
|
||||
|
||||
protected void removeRegion(TargetMemoryRegion region) {
|
||||
synchronized (regions) {
|
||||
regions.remove(new IDKeyed<>(region));
|
||||
}
|
||||
}
|
||||
|
||||
protected CompletableFuture<byte[]> read(Address start, int length) {
|
||||
Address tStart = recorder.memoryMapper.traceToTarget(start);
|
||||
if (tStart == null) {
|
||||
return CompletableFuture.completedFuture(new byte[] {});
|
||||
}
|
||||
TargetMemory memory = getMemoryForSpace(tStart.getAddressSpace());
|
||||
if (memory == null) {
|
||||
return CompletableFuture.completedFuture(new byte[] {});
|
||||
}
|
||||
return memory.readMemory(tStart, length);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> write(Address start, byte[] data) {
|
||||
Address tStart = recorder.memoryMapper.traceToTarget(start);
|
||||
if (tStart == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Address space " + start.getAddressSpace() + " not defined on the target");
|
||||
}
|
||||
TargetMemory memory = getMemoryForSpace(tStart.getAddressSpace());
|
||||
if (memory == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Address space " + tStart.getAddressSpace() +
|
||||
" cannot be found in target memory");
|
||||
}
|
||||
return memory.writeMemory(tStart, data);
|
||||
}
|
||||
|
||||
protected void invalidate(TargetMemory memory, long snap) {
|
||||
Set<AddressSpace> targetSpaces = memoriesByTargetSpace.entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getValue() == memory)
|
||||
.map(e -> e.getKey().obj)
|
||||
.collect(Collectors.toSet());
|
||||
for (AddressSpace targetSpace : targetSpaces) {
|
||||
Address traceMin = recorder.memoryMapper.targetToTrace(targetSpace.getMinAddress());
|
||||
Address traceMax = traceMin.getAddressSpace().getMaxAddress();
|
||||
memoryManager.setState(snap, traceMin, traceMax, TraceMemoryState.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
protected void recordMemory(long snap, Address start, byte[] data) {
|
||||
memoryManager.putBytes(snap, start, ByteBuffer.wrap(data));
|
||||
}
|
||||
|
||||
public void recordError(long snap, Address tMin, DebuggerMemoryAccessException e) {
|
||||
// TODO: Bookmark to describe error?
|
||||
memoryManager.setState(snap, tMin, TraceMemoryState.ERROR);
|
||||
}
|
||||
|
||||
protected boolean isAccessible(TraceMemoryRegion r) {
|
||||
// TODO: Perhaps a bit aggressive, but haven't really been checking anyway.
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Collector<AddressRange, AddressSet, AddressSet> toAddressSet() {
|
||||
return new Collector<>() {
|
||||
@Override
|
||||
public Supplier<AddressSet> supplier() {
|
||||
return AddressSet::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiConsumer<AddressSet, AddressRange> accumulator() {
|
||||
return AddressSet::add;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryOperator<AddressSet> combiner() {
|
||||
return (s1, s2) -> {
|
||||
s1.add(s2);
|
||||
return s1;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<AddressSet, AddressSet> finisher() {
|
||||
return Function.identity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Characteristics> characteristics() {
|
||||
return Set.of();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public AddressSetView getAccessible() {
|
||||
synchronized (regions) {
|
||||
return regions.values()
|
||||
.stream()
|
||||
.collect(toAddressSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user