mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-1529: Interrupt emulator when decoding uninitialized memory
This commit is contained in:
parent
738e662e82
commit
4aa54dd1f9
25 changed files with 274 additions and 100 deletions
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.service.emulation;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
import generic.ULongSpan.ULongSpanSet;
|
||||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||||
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
||||||
|
@ -52,28 +53,17 @@ public abstract class AbstractRWTargetPcodeExecutorStatePiece
|
||||||
super(language, space, backing, bytes, written);
|
super(language, space, backing, bytes, written);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void fillUninitialized(AddressSet uninitialized);
|
protected abstract ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] read(long offset, int size, Reason reason) {
|
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||||
if (backing != null) {
|
uninitialized = readUninitializedFromTarget(uninitialized);
|
||||||
AddressSet uninitialized =
|
if (uninitialized.isEmpty()) {
|
||||||
addrSet(bytes.getUninitialized(offset, offset + size - 1));
|
return uninitialized;
|
||||||
if (uninitialized.isEmpty()) {
|
|
||||||
return super.read(offset, size, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
fillUninitialized(uninitialized);
|
|
||||||
|
|
||||||
AddressSetView unknown = backing.intersectUnknown(
|
|
||||||
addrSet(bytes.getUninitialized(offset, offset + size - 1)));
|
|
||||||
if (!unknown.isEmpty() && reason == Reason.EXECUTE) {
|
|
||||||
warnUnknown(unknown);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return super.readUninitializedFromBacking(uninitialized);
|
||||||
// TODO: What to flush when bytes in the trace change?
|
// TODO: What to flush when bytes in the trace change?
|
||||||
return super.read(offset, size, reason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T waitTimeout(CompletableFuture<T> future) {
|
protected <T> T waitTimeout(CompletableFuture<T> future) {
|
||||||
|
|
|
@ -18,6 +18,8 @@ package ghidra.app.plugin.core.debug.service.emulation;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import generic.ULongSpan;
|
||||||
|
import generic.ULongSpan.ULongSpanSet;
|
||||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerMemoryAccess;
|
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerMemoryAccess;
|
||||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||||
|
@ -81,27 +83,27 @@ public class RWTargetMemoryPcodeExecutorStatePiece
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void fillUninitialized(AddressSet uninitialized) {
|
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
|
||||||
if (space.isUniqueSpace()) {
|
if (space.isUniqueSpace()) {
|
||||||
return;
|
return uninitialized;
|
||||||
}
|
}
|
||||||
AddressSetView unknown;
|
AddressSetView unknown;
|
||||||
unknown = backing.intersectUnknown(uninitialized);
|
AddressSet addrsUninit = addrSet(uninitialized);
|
||||||
|
unknown = backing.intersectUnknown(addrsUninit);
|
||||||
if (unknown.isEmpty()) {
|
if (unknown.isEmpty()) {
|
||||||
return;
|
return uninitialized;
|
||||||
}
|
}
|
||||||
if (waitTimeout(backing.readFromTargetMemory(unknown))) {
|
if (backing.isLive() && waitTimeout(backing.readFromTargetMemory(unknown))) {
|
||||||
unknown = backing.intersectUnknown(uninitialized);
|
unknown = backing.intersectUnknown(addrsUninit);
|
||||||
if (unknown.isEmpty()) {
|
if (unknown.isEmpty()) {
|
||||||
return;
|
return uninitialized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (backing.readFromStaticImages(bytes, unknown)) {
|
if (backing.readFromStaticImages(bytes, unknown)) {
|
||||||
unknown = backing.intersectUnknown(uninitialized);
|
ULongSpan bound = uninitialized.bound();
|
||||||
if (unknown.isEmpty()) {
|
return bytes.getUninitialized(bound.min(), bound.max());
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return uninitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.service.emulation;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import generic.ULongSpan.ULongSpanSet;
|
||||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerRegistersAccess;
|
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerRegistersAccess;
|
||||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||||
|
@ -78,15 +79,14 @@ public class RWTargetRegistersPcodeExecutorStatePiece
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void fillUninitialized(AddressSet uninitialized) {
|
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
|
||||||
if (space.isUniqueSpace()) {
|
if (space.isUniqueSpace() || !backing.isLive()) {
|
||||||
return;
|
return uninitialized;
|
||||||
}
|
}
|
||||||
if (!backing.isLive()) {
|
AddressSet addrsUninit = addrSet(uninitialized);
|
||||||
return;
|
AddressSetView unknown = backing.intersectUnknown(addrsUninit);
|
||||||
}
|
|
||||||
AddressSetView unknown = backing.intersectUnknown(uninitialized);
|
|
||||||
waitTimeout(backing.readFromTargetRegisters(unknown));
|
waitTimeout(backing.readFromTargetRegisters(unknown));
|
||||||
|
return uninitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -353,7 +353,7 @@ public class UnwindAnalysis {
|
||||||
public SymPcodeExecutorState executeToPc(Deque<BlockEdge> to) throws CancelledException {
|
public SymPcodeExecutorState executeToPc(Deque<BlockEdge> to) throws CancelledException {
|
||||||
SymPcodeExecutorState state = new SymPcodeExecutorState(program);
|
SymPcodeExecutorState state = new SymPcodeExecutorState(program);
|
||||||
SymPcodeExecutor exec =
|
SymPcodeExecutor exec =
|
||||||
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE, warnings, monitor);
|
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE_READ, warnings, monitor);
|
||||||
executePathTo(exec, to);
|
executePathTo(exec, to);
|
||||||
executeBlockTo(exec, pcBlock.block, pc);
|
executeBlockTo(exec, pcBlock.block, pc);
|
||||||
return state;
|
return state;
|
||||||
|
@ -375,7 +375,7 @@ public class UnwindAnalysis {
|
||||||
public SymPcodeExecutorState executeFromPc(SymPcodeExecutorState state,
|
public SymPcodeExecutorState executeFromPc(SymPcodeExecutorState state,
|
||||||
Deque<BlockEdge> from) throws CancelledException {
|
Deque<BlockEdge> from) throws CancelledException {
|
||||||
SymPcodeExecutor exec =
|
SymPcodeExecutor exec =
|
||||||
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE, warnings, monitor);
|
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE_READ, warnings, monitor);
|
||||||
executeBlockFrom(exec, pcBlock.block, pc);
|
executeBlockFrom(exec, pcBlock.block, pc);
|
||||||
executePathFrom(exec, from);
|
executePathFrom(exec, from);
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -39,6 +39,7 @@ import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlug
|
||||||
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
||||||
|
import ghidra.pcode.exec.DecodePcodeExecutionException;
|
||||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
||||||
import ghidra.pcode.utils.Utils;
|
import ghidra.pcode.utils.Utils;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -343,13 +344,53 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||||
.toString(16));
|
.toString(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInterruptOnDecodeUninitialized() throws Exception {
|
||||||
|
createProgram();
|
||||||
|
intoProject(program);
|
||||||
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
|
Memory memory = program.getMemory();
|
||||||
|
Address addrText = addr(program, 0x00400000);
|
||||||
|
Register regPC = program.getRegister("pc");
|
||||||
|
|
||||||
|
try (Transaction tx = program.openTransaction("Initialize")) {
|
||||||
|
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
|
||||||
|
(byte) 0, TaskMonitor.DUMMY, false);
|
||||||
|
blockText.setExecute(true);
|
||||||
|
asm.assemble(addrText,
|
||||||
|
"br 0x003ffffe");
|
||||||
|
}
|
||||||
|
|
||||||
|
programManager.openProgram(program);
|
||||||
|
waitForSwing();
|
||||||
|
codeBrowser.goTo(new ProgramLocation(program, addrText));
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(codeBrowser.getProvider(), emulationPlugin.actionEmulateProgram, true);
|
||||||
|
|
||||||
|
Trace trace = traceManager.getCurrentTrace();
|
||||||
|
assertNotNull(trace);
|
||||||
|
|
||||||
|
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
||||||
|
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||||
|
|
||||||
|
EmulationResult result = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
||||||
|
TraceSchedule.snap(0), TaskMonitor.DUMMY, Scheduler.oneThread(thread));
|
||||||
|
|
||||||
|
assertEquals(TraceSchedule.parse("0:t0-1"), result.schedule());
|
||||||
|
assertTrue(result.error() instanceof DecodePcodeExecutionException);
|
||||||
|
|
||||||
|
long scratch = result.snapshot();
|
||||||
|
assertEquals(new BigInteger("003ffffe", 16), regs.getViewValue(scratch, regPC).getUnsignedValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExecutionBreakpoint() throws Exception {
|
public void testExecutionBreakpoint() throws Exception {
|
||||||
createProgram();
|
createProgram();
|
||||||
intoProject(program);
|
intoProject(program);
|
||||||
Assembler asm = Assemblers.getAssembler(program);
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
Address addrText = addr(program, 0x000400000);
|
Address addrText = addr(program, 0x00400000);
|
||||||
Register regPC = program.getRegister("pc");
|
Register regPC = program.getRegister("pc");
|
||||||
Register regR0 = program.getRegister("r0");
|
Register regR0 = program.getRegister("r0");
|
||||||
Register regR1 = program.getRegister("r1");
|
Register regR1 = program.getRegister("r1");
|
||||||
|
@ -411,7 +452,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||||
intoProject(program);
|
intoProject(program);
|
||||||
Assembler asm = Assemblers.getAssembler(program);
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
Address addrText = addr(program, 0x000400000);
|
Address addrText = addr(program, 0x00400000);
|
||||||
Address addrI1;
|
Address addrI1;
|
||||||
Address addrI2;
|
Address addrI2;
|
||||||
try (Transaction tx = program.openTransaction("Initialize")) {
|
try (Transaction tx = program.openTransaction("Initialize")) {
|
||||||
|
@ -470,7 +511,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||||
intoProject(program);
|
intoProject(program);
|
||||||
Assembler asm = Assemblers.getAssembler(program);
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
Address addrText = addr(program, 0x000400000);
|
Address addrText = addr(program, 0x00400000);
|
||||||
Register regPC = program.getRegister("pc");
|
Register regPC = program.getRegister("pc");
|
||||||
Register regR0 = program.getRegister("r0");
|
Register regR0 = program.getRegister("r0");
|
||||||
Register regR1 = program.getRegister("r1");
|
Register regR1 = program.getRegister("r1");
|
||||||
|
@ -537,7 +578,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||||
intoProject(program);
|
intoProject(program);
|
||||||
Assembler asm = Assemblers.getAssembler(program);
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
Address addrText = addr(program, 0x000400000);
|
Address addrText = addr(program, 0x00400000);
|
||||||
Register regPC = program.getRegister("pc");
|
Register regPC = program.getRegister("pc");
|
||||||
Register regR0 = program.getRegister("r0");
|
Register regR0 = program.getRegister("r0");
|
||||||
Register regR1 = program.getRegister("r1");
|
Register regR1 = program.getRegister("r1");
|
||||||
|
|
|
@ -21,7 +21,8 @@ import java.util.Map;
|
||||||
import generic.ULongSpan;
|
import generic.ULongSpan;
|
||||||
import generic.ULongSpan.ULongSpanSet;
|
import generic.ULongSpan.ULongSpanSet;
|
||||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.AbstractBytesPcodeExecutorStatePiece;
|
||||||
|
import ghidra.pcode.exec.BytesPcodeExecutorStateSpace;
|
||||||
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece.CachedSpace;
|
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece.CachedSpace;
|
||||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -76,19 +77,29 @@ public class BytesTracePcodeExecutorStatePiece
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||||
if (!uninitialized.isEmpty()) {
|
if (uninitialized.isEmpty()) {
|
||||||
// TODO: Warn or bail when reading UNKNOWN bytes
|
return uninitialized;
|
||||||
// NOTE: Read without regard to gaps
|
|
||||||
// NOTE: Cannot write those gaps, though!!!
|
|
||||||
ULongSpan bound = uninitialized.bound();
|
|
||||||
ByteBuffer buf = ByteBuffer.allocate((int) bound.length());
|
|
||||||
backing.getBytes(space.getAddress(bound.min()), buf);
|
|
||||||
for (ULongSpan span : uninitialized.spans()) {
|
|
||||||
bytes.putData(span.min(), buf.array(), (int) (span.min() - bound.min()),
|
|
||||||
(int) span.length());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// TODO: Warn or bail when reading UNKNOWN bytes
|
||||||
|
// NOTE: Read without regard to gaps
|
||||||
|
// NOTE: Cannot write those gaps, though!!!
|
||||||
|
AddressSetView knownButUninit = backing.intersectViewKnown(addrSet(uninitialized));
|
||||||
|
if (knownButUninit.isEmpty()) {
|
||||||
|
return uninitialized;
|
||||||
|
}
|
||||||
|
AddressRange knownBound = new AddressRangeImpl(
|
||||||
|
knownButUninit.getMinAddress(),
|
||||||
|
knownButUninit.getMaxAddress());
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate((int) knownBound.getLength());
|
||||||
|
backing.getBytes(knownBound.getMinAddress(), buf);
|
||||||
|
for (AddressRange range : knownButUninit) {
|
||||||
|
bytes.putData(range.getMinAddress().getOffset(), buf.array(),
|
||||||
|
(int) (range.getMinAddress().subtract(knownBound.getMinAddress())),
|
||||||
|
(int) range.getLength());
|
||||||
|
}
|
||||||
|
ULongSpan uninitBound = uninitialized.bound();
|
||||||
|
return bytes.getUninitialized(uninitBound.min(), uninitBound.max());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void warnUnknown(AddressSetView unknown) {
|
protected void warnUnknown(AddressSetView unknown) {
|
||||||
|
|
|
@ -30,6 +30,11 @@ import ghidra.trace.model.memory.TraceMemorySpace;
|
||||||
public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||||
extends AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece {
|
extends AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a piece
|
||||||
|
*
|
||||||
|
* @param data the trace-data access shim
|
||||||
|
*/
|
||||||
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||||
super(data);
|
super(data);
|
||||||
}
|
}
|
||||||
|
@ -45,11 +50,6 @@ public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||||
spaceMap.fork());
|
spaceMap.fork());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a piece
|
|
||||||
*
|
|
||||||
* @param data the trace-data access shim
|
|
||||||
*/
|
|
||||||
protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
|
protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
|
||||||
return backing.getKnownNow();
|
return backing.getKnownNow();
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,23 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace
|
||||||
return doGetKnown(Lifespan.since(snap));
|
return doGetKnown(Lifespan.since(snap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AddressSetView intersectViewKnown(AddressSetView guestView) {
|
||||||
|
TraceMemoryOperations ops = getMemoryOps(false);
|
||||||
|
if (ops == null) {
|
||||||
|
return new AddressSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSetView hostView = toOverlay(platform.mapGuestToHost(guestView));
|
||||||
|
AddressSet hostKnown = new AddressSet();
|
||||||
|
for (long sn : viewport.getOrderedSnaps()) {
|
||||||
|
hostKnown.add(ops.getAddressesWithState(sn, hostView,
|
||||||
|
st -> st != null && st != TraceMemoryState.UNKNOWN));
|
||||||
|
}
|
||||||
|
AddressSetView hostResult = hostView.intersect(hostKnown);
|
||||||
|
return platform.mapHostToGuest(hostResult);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSetView intersectUnknown(AddressSetView guestView) {
|
public AddressSetView intersectUnknown(AddressSetView guestView) {
|
||||||
TraceMemoryOperations ops = getMemoryOps(false);
|
TraceMemoryOperations ops = getMemoryOps(false);
|
||||||
|
|
|
@ -80,6 +80,11 @@ public class DefaultPcodeTraceThreadAccess
|
||||||
return memory.getKnownBefore().union(registers.getKnownBefore());
|
return memory.getKnownBefore().union(registers.getKnownBefore());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AddressSetView intersectViewKnown(AddressSetView view) {
|
||||||
|
return memory.intersectViewKnown(view).union(registers.intersectViewKnown(view));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSetView intersectUnknown(AddressSetView view) {
|
public AddressSetView intersectUnknown(AddressSetView view) {
|
||||||
return memory.intersectUnknown(view).union(registers.intersectUnknown(view));
|
return memory.intersectUnknown(view).union(registers.intersectUnknown(view));
|
||||||
|
|
|
@ -92,6 +92,15 @@ public interface PcodeTraceDataAccess {
|
||||||
*/
|
*/
|
||||||
AddressSetView getKnownBefore();
|
AddressSetView getKnownBefore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the intersection of the given address set and the set of
|
||||||
|
* {@link TraceMemoryState#KNOWN} or (@link {@link TraceMemoryState#ERROR} memory
|
||||||
|
*
|
||||||
|
* @param view the address set
|
||||||
|
* @return the intersection
|
||||||
|
*/
|
||||||
|
AddressSetView intersectViewKnown(AddressSetView view);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the intersection of the given address set and the set of
|
* Compute the intersection of the given address set and the set of
|
||||||
* {@link TraceMemoryState#UNKNOWN} memory
|
* {@link TraceMemoryState#UNKNOWN} memory
|
||||||
|
|
|
@ -221,7 +221,7 @@ public class TraceSleighUtilsTest extends AbstractGhidraHeadlessIntegrationTest
|
||||||
new PcodeExecutor<>(sp.getLanguage(),
|
new PcodeExecutor<>(sp.getLanguage(),
|
||||||
BytesPcodeArithmetic.forLanguage(b.language),
|
BytesPcodeArithmetic.forLanguage(b.language),
|
||||||
new DirectBytesTracePcodeExecutorState(b.host, 0, thread, 0),
|
new DirectBytesTracePcodeExecutorState(b.host, 0, thread, 0),
|
||||||
Reason.EXECUTE);
|
Reason.EXECUTE_READ);
|
||||||
sp.execute(executor, PcodeUseropLibrary.nil());
|
sp.execute(executor, PcodeUseropLibrary.nil());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ class TestThread implements PcodeThread<Void> {
|
||||||
@Override
|
@Override
|
||||||
public PcodeExecutor<Void> getExecutor() {
|
public PcodeExecutor<Void> getExecutor() {
|
||||||
return new PcodeExecutor<>(TraceScheduleTest.TOY_BE_64_LANG, machine.getArithmetic(),
|
return new PcodeExecutor<>(TraceScheduleTest.TOY_BE_64_LANG, machine.getArithmetic(),
|
||||||
getState(), Reason.EXECUTE) {
|
getState(), Reason.EXECUTE_READ) {
|
||||||
public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary<Void> library) {
|
public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary<Void> library) {
|
||||||
machine.record.add("x:" + name);
|
machine.record.add("x:" + name);
|
||||||
// TODO: Verify the actual effect
|
// TODO: Verify the actual effect
|
||||||
|
|
|
@ -287,7 +287,7 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||||
// TODO: Not ideal to require concrete size. What are the alternatives, though?
|
// TODO: Not ideal to require concrete size. What are the alternatives, though?
|
||||||
// TODO: size should actually be long (size_t)
|
// TODO: size should actually be long (size_t)
|
||||||
int size = (int) arithmetic.toLong(count, Purpose.OTHER);
|
int size = (int) arithmetic.toLong(count, Purpose.OTHER);
|
||||||
T buf = state.getVar(space, bufPtr, size, true, Reason.EXECUTE);
|
T buf = state.getVar(space, bufPtr, size, true, Reason.EXECUTE_READ);
|
||||||
// TODO: Write back into state? "write" shouldn't touch the buffer....
|
// TODO: Write back into state? "write" shouldn't touch the buffer....
|
||||||
return desc.write(buf);
|
return desc.write(buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -869,11 +869,12 @@ public interface Span<N, S extends Span<N, S>> extends Comparable<S> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public S bound() {
|
public S bound() {
|
||||||
S result = domain.empty();
|
if (spanTree.isEmpty()) {
|
||||||
for (Entry<S, V> entry : spanTree.values()) {
|
return domain.empty();
|
||||||
result = result.bound(entry.getKey());
|
|
||||||
}
|
}
|
||||||
return result;
|
S first = spanTree.firstEntry().getValue().getKey();
|
||||||
|
S last = spanTree.lastEntry().getValue().getKey();
|
||||||
|
return first.bound(last);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -165,16 +165,16 @@ public class AdaptedEmulator implements Emulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||||
if (uninitialized.isEmpty()) {
|
if (uninitialized.isEmpty()) {
|
||||||
return;
|
return uninitialized;
|
||||||
}
|
}
|
||||||
if (backing.loadImage == null) {
|
if (backing.loadImage == null) {
|
||||||
if (space.isUniqueSpace()) {
|
if (space.isUniqueSpace()) {
|
||||||
throw new AccessPcodeExecutionException(
|
throw new AccessPcodeExecutionException(
|
||||||
"Attempted to read from uninitialized unique: " + uninitialized);
|
"Attempted to read from uninitialized unique: " + uninitialized);
|
||||||
}
|
}
|
||||||
return;
|
return uninitialized;
|
||||||
}
|
}
|
||||||
ULongSpan bound = uninitialized.bound();
|
ULongSpan bound = uninitialized.bound();
|
||||||
byte[] data = new byte[(int) bound.length()];
|
byte[] data = new byte[(int) bound.length()];
|
||||||
|
@ -184,6 +184,7 @@ public class AdaptedEmulator implements Emulator {
|
||||||
bytes.putData(span.min(), data, (int) (span.min() - bound.min()),
|
bytes.putData(span.min(), data, (int) (span.min() - bound.min()),
|
||||||
(int) span.length());
|
(int) span.length());
|
||||||
}
|
}
|
||||||
|
return bytes.getUninitialized(bound.min(), bound.max());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -159,7 +159,8 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||||
* @param state the composite state assigned to the thread
|
* @param state the composite state assigned to the thread
|
||||||
*/
|
*/
|
||||||
public PcodeThreadExecutor(DefaultPcodeThread<T> thread) {
|
public PcodeThreadExecutor(DefaultPcodeThread<T> thread) {
|
||||||
super(thread.language, thread.arithmetic, thread.state, Reason.EXECUTE);
|
// NB. The executor itself is not decoding. So reads are in fact data reads.
|
||||||
|
super(thread.language, thread.arithmetic, thread.state, Reason.EXECUTE_READ);
|
||||||
this.thread = thread;
|
this.thread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||||
* These two exist as a way to integrate the language-specific injects that are already
|
* These two exist as a way to integrate the language-specific injects that are already
|
||||||
* written for {@link Emulator}.
|
* written for {@link Emulator}.
|
||||||
*/
|
*/
|
||||||
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE) {
|
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE_READ) {
|
||||||
@Override
|
@Override
|
||||||
public void setMemoryBank(MemoryBank bank) {
|
public void setMemoryBank(MemoryBank bank) {
|
||||||
// Ignore
|
// Ignore
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.pcode.emu;
|
||||||
|
|
||||||
import ghidra.app.util.PseudoInstruction;
|
import ghidra.app.util.PseudoInstruction;
|
||||||
import ghidra.pcode.emulate.InstructionDecodeException;
|
import ghidra.pcode.emulate.InstructionDecodeException;
|
||||||
|
import ghidra.pcode.exec.DecodePcodeExecutionException;
|
||||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||||
import ghidra.pcode.exec.PcodeExecutorState;
|
import ghidra.pcode.exec.PcodeExecutorState;
|
||||||
import ghidra.program.disassemble.Disassembler;
|
import ghidra.program.disassemble.Disassembler;
|
||||||
|
@ -87,7 +88,7 @@ public class SleighInstructionDecoder implements InstructionDecoder {
|
||||||
block = disassembler.pseudoDisassembleBlock(
|
block = disassembler.pseudoDisassembleBlock(
|
||||||
state.getConcreteBuffer(address, Purpose.DECODE), context, 1);
|
state.getConcreteBuffer(address, Purpose.DECODE), context, 1);
|
||||||
if (block == null || block.isEmpty()) {
|
if (block == null || block.isEmpty()) {
|
||||||
throw new InstructionDecodeException(lastMsg, address);
|
throw new DecodePcodeExecutionException(lastMsg, address);
|
||||||
}
|
}
|
||||||
instruction = (PseudoInstruction) block.getInstructionAt(address);
|
instruction = (PseudoInstruction) block.getInstructionAt(address);
|
||||||
lengthWithDelays = computeLength();
|
lengthWithDelays = computeLength();
|
||||||
|
|
|
@ -25,6 +25,7 @@ import ghidra.program.model.lang.Language;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.mem.*;
|
import ghidra.program.model.mem.*;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,16 +42,21 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||||
protected class StateMemBuffer implements MemBufferMixin {
|
protected class StateMemBuffer implements MemBufferMixin {
|
||||||
protected final Address address;
|
protected final Address address;
|
||||||
protected final BytesPcodeExecutorStateSpace<?> source;
|
protected final BytesPcodeExecutorStateSpace<?> source;
|
||||||
|
protected final Reason reason;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a buffer bound to the given space, at the given address
|
* Construct a buffer bound to the given space, at the given address
|
||||||
*
|
*
|
||||||
* @param address the address
|
* @param address the address
|
||||||
* @param source the space
|
* @param source the space
|
||||||
|
* @param reason the reason this buffer reads from the state, as in
|
||||||
|
* {@link PcodeExecutorStatePiece#getVar(Varnode, Reason)}
|
||||||
*/
|
*/
|
||||||
public StateMemBuffer(Address address, BytesPcodeExecutorStateSpace<?> source) {
|
public StateMemBuffer(Address address, BytesPcodeExecutorStateSpace<?> source,
|
||||||
|
Reason reason) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
|
this.reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,8 +76,8 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBytes(ByteBuffer buffer, int addressOffset) {
|
public int getBytes(ByteBuffer buffer, int addressOffset) {
|
||||||
byte[] data = source.read(address.getOffset() + addressOffset, buffer.remaining(),
|
byte[] data =
|
||||||
Reason.EXECUTE);
|
source.read(address.getOffset() + addressOffset, buffer.remaining(), reason);
|
||||||
buffer.put(data);
|
buffer.put(data);
|
||||||
return data.length;
|
return data.length;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +166,8 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemBuffer getConcreteBuffer(Address address, PcodeArithmetic.Purpose purpose) {
|
public MemBuffer getConcreteBuffer(Address address, PcodeArithmetic.Purpose purpose) {
|
||||||
return new StateMemBuffer(address, getForSpace(address.getAddressSpace(), false));
|
return new StateMemBuffer(address, getForSpace(address.getAddressSpace(), false),
|
||||||
|
purpose.reason());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,7 +18,7 @@ package ghidra.pcode.exec;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import generic.ULongSpan;
|
import generic.ULongSpan;
|
||||||
import generic.ULongSpan.ULongSpanSet;
|
import generic.ULongSpan.*;
|
||||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -77,8 +77,10 @@ public class BytesPcodeExecutorStateSpace<B> {
|
||||||
* Extension point: Read from backing into this space, when acting as a cache.
|
* Extension point: Read from backing into this space, when acting as a cache.
|
||||||
*
|
*
|
||||||
* @param uninitialized the ranges which need to be read.
|
* @param uninitialized the ranges which need to be read.
|
||||||
|
* @return the ranges which remain uninitialized
|
||||||
*/
|
*/
|
||||||
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||||
|
return uninitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,9 +97,15 @@ public class BytesPcodeExecutorStateSpace<B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRange addrRng(ULongSpan span) {
|
protected AddressRange addrRng(ULongSpan span) {
|
||||||
Address start = space.getAddress(span.min());
|
return new AddressRangeImpl(
|
||||||
Address end = space.getAddress(span.max());
|
space.getAddress(span.min()),
|
||||||
return new AddressRangeImpl(start, end);
|
space.getAddress(span.max()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ULongSpan spanRng(AddressRange range) {
|
||||||
|
return ULongSpan.span(
|
||||||
|
range.getMinAddress().getOffset(),
|
||||||
|
range.getMaxAddress().getOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddressSet addrSet(ULongSpanSet set) {
|
protected AddressSet addrSet(ULongSpanSet set) {
|
||||||
|
@ -108,6 +116,20 @@ public class BytesPcodeExecutorStateSpace<B> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This assumes without assertion that the set is contained in this space
|
||||||
|
*
|
||||||
|
* @param set the address set
|
||||||
|
* @return the unsigned long span set
|
||||||
|
*/
|
||||||
|
protected ULongSpanSet spanSet(AddressSetView set) {
|
||||||
|
MutableULongSpanSet result = new DefaultULongSpanSet();
|
||||||
|
for (AddressRange range : set) {
|
||||||
|
result.add(spanRng(range));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
protected Set<Register> getRegs(AddressSetView set) {
|
protected Set<Register> getRegs(AddressSetView set) {
|
||||||
Set<Register> regs = new TreeSet<>();
|
Set<Register> regs = new TreeSet<>();
|
||||||
for (AddressRange rng : set) {
|
for (AddressRange rng : set) {
|
||||||
|
@ -151,12 +173,32 @@ public class BytesPcodeExecutorStateSpace<B> {
|
||||||
* @return the bytes read
|
* @return the bytes read
|
||||||
*/
|
*/
|
||||||
public byte[] read(long offset, int size, Reason reason) {
|
public byte[] read(long offset, int size, Reason reason) {
|
||||||
if (backing != null) {
|
ULongSpanSet uninitialized = bytes.getUninitialized(offset, offset + size - 1);
|
||||||
readUninitializedFromBacking(bytes.getUninitialized(offset, offset + size - 1));
|
if (uninitialized.isEmpty()) {
|
||||||
|
return readBytes(offset, size, reason);
|
||||||
}
|
}
|
||||||
ULongSpanSet stillUninit = bytes.getUninitialized(offset, offset + size - 1);
|
if (backing != null) {
|
||||||
if (!stillUninit.isEmpty() && reason == Reason.EXECUTE) {
|
uninitialized = readUninitializedFromBacking(uninitialized);
|
||||||
warnUninit(stillUninit);
|
if (uninitialized.isEmpty()) {
|
||||||
|
return readBytes(offset, size, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<ULongSpan> it =
|
||||||
|
uninitialized.complement(ULongSpan.extent(offset, size)).iterator();
|
||||||
|
if (it.hasNext()) {
|
||||||
|
ULongSpan init = it.next();
|
||||||
|
if (init.min().longValue() == offset) {
|
||||||
|
return readBytes(offset, (int) init.length(), reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason == Reason.EXECUTE_READ) {
|
||||||
|
warnUninit(uninitialized);
|
||||||
|
}
|
||||||
|
else if (reason == Reason.EXECUTE_DECODE) {
|
||||||
|
throw new DecodePcodeExecutionException("Cannot decode uninitialized memory",
|
||||||
|
space.getAddress(offset));
|
||||||
}
|
}
|
||||||
return readBytes(offset, size, reason);
|
return readBytes(offset, size, reason);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* ###
|
||||||
|
* 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.pcode.exec;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
|
||||||
|
public class DecodePcodeExecutionException extends PcodeExecutionException {
|
||||||
|
private final Address pc;
|
||||||
|
|
||||||
|
public DecodePcodeExecutionException(String message, Address pc) {
|
||||||
|
super(message + ", PC=" + pc);
|
||||||
|
this.pc = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getProgramCounter() {
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ package ghidra.pcode.exec;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
import ghidra.pcode.opbehavior.*;
|
import ghidra.pcode.opbehavior.*;
|
||||||
import ghidra.pcode.utils.Utils;
|
import ghidra.pcode.utils.Utils;
|
||||||
import ghidra.program.model.lang.Endian;
|
import ghidra.program.model.lang.Endian;
|
||||||
|
@ -49,21 +50,31 @@ public interface PcodeArithmetic<T> {
|
||||||
*/
|
*/
|
||||||
enum Purpose {
|
enum Purpose {
|
||||||
/** The value is needed to parse an instruction */
|
/** The value is needed to parse an instruction */
|
||||||
DECODE,
|
DECODE(Reason.EXECUTE_DECODE),
|
||||||
/** The value is needed for disassembly context */
|
/** The value is needed for disassembly context */
|
||||||
CONTEXT,
|
CONTEXT(Reason.EXECUTE_READ),
|
||||||
/** The value is needed to decide a conditional branch */
|
/** The value is needed to decide a conditional branch */
|
||||||
CONDITION,
|
CONDITION(Reason.EXECUTE_READ),
|
||||||
/** The value will be used as the address of an indirect branch */
|
/** The value will be used as the address of an indirect branch */
|
||||||
BRANCH,
|
BRANCH(Reason.EXECUTE_READ),
|
||||||
/** The value will be used as the address of a value to load */
|
/** The value will be used as the address of a value to load */
|
||||||
LOAD,
|
LOAD(Reason.EXECUTE_READ),
|
||||||
/** The value will be used as the address of a value to store */
|
/** The value will be used as the address of a value to store */
|
||||||
STORE,
|
STORE(Reason.EXECUTE_READ),
|
||||||
/** Some other reason, perhaps for userop library use */
|
/** Some other reason, perhaps for userop library use */
|
||||||
OTHER,
|
OTHER(Reason.EXECUTE_READ),
|
||||||
/** The user or a tool is inspecting the value */
|
/** The user or a tool is inspecting the value */
|
||||||
INSPECT
|
INSPECT(Reason.INSPECT);
|
||||||
|
|
||||||
|
private final Reason reason;
|
||||||
|
|
||||||
|
private Purpose(Reason reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reason reason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,9 +47,11 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||||
enum Reason {
|
enum Reason {
|
||||||
/** The value is needed as the default program counter or disassembly context */
|
/** The value is needed as the default program counter or disassembly context */
|
||||||
RE_INIT,
|
RE_INIT,
|
||||||
/** The value is needed by the emulator in the course of execution */
|
/** The value is being read by the emulator as data in the course of execution */
|
||||||
EXECUTE,
|
EXECUTE_READ,
|
||||||
/** The value is being inspected */
|
/** The value is being decoded by the emulator as an instruction for execution */
|
||||||
|
EXECUTE_DECODE,
|
||||||
|
/** The value is being inspected by something other than an emulator */
|
||||||
INSPECT
|
INSPECT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class AnnotatedPcodeUseropLibraryTest extends AbstractGTest {
|
||||||
protected PcodeExecutor<byte[]> createBytesExecutor(SleighLanguage language) throws Exception {
|
protected PcodeExecutor<byte[]> createBytesExecutor(SleighLanguage language) throws Exception {
|
||||||
PcodeExecutorState<byte[]> state = new BytesPcodeExecutorState(language);
|
PcodeExecutorState<byte[]> state = new BytesPcodeExecutorState(language);
|
||||||
PcodeArithmetic<byte[]> arithmetic = BytesPcodeArithmetic.forLanguage(language);
|
PcodeArithmetic<byte[]> arithmetic = BytesPcodeArithmetic.forLanguage(language);
|
||||||
return new PcodeExecutor<>(language, arithmetic, state, Reason.EXECUTE);
|
return new PcodeExecutor<>(language, arithmetic, state, Reason.EXECUTE_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> void executeSleigh(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
|
protected <T> void executeSleigh(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
|
||||||
|
|
|
@ -802,8 +802,10 @@ public class Disassembler implements DisassemblerConflictHandler {
|
||||||
disassembleInstructionBlock(block, blockMemBuffer, null, limit, null, false);
|
disassembleInstructionBlock(block, blockMemBuffer, null, limit, null, false);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Msg.error(this, "Pseudo block disassembly failure at " + blockMemBuffer.getAddress() +
|
String message = "Pseudo block disassembly failure at " + blockMemBuffer.getAddress() +
|
||||||
": " + e.getMessage(), e);
|
": " + e.getMessage();
|
||||||
|
Msg.error(this, message, e);
|
||||||
|
reportMessage(message);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue