mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 12:11:55 +00:00
GP-1529: Interrupt emulator when decoding uninitialized memory
This commit is contained in:
parent
738e662e82
commit
4aa54dd1f9
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.service.emulation;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import generic.ULongSpan.ULongSpanSet;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
||||
@ -52,28 +53,17 @@ public abstract class AbstractRWTargetPcodeExecutorStatePiece
|
||||
super(language, space, backing, bytes, written);
|
||||
}
|
||||
|
||||
protected abstract void fillUninitialized(AddressSet uninitialized);
|
||||
protected abstract ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized);
|
||||
|
||||
@Override
|
||||
public byte[] read(long offset, int size, Reason reason) {
|
||||
if (backing != null) {
|
||||
AddressSet uninitialized =
|
||||
addrSet(bytes.getUninitialized(offset, offset + size - 1));
|
||||
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||
uninitialized = readUninitializedFromTarget(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 uninitialized;
|
||||
}
|
||||
|
||||
return super.readUninitializedFromBacking(uninitialized);
|
||||
// TODO: What to flush when bytes in the trace change?
|
||||
return super.read(offset, size, reason);
|
||||
}
|
||||
|
||||
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.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.PcodeDebuggerMemoryAccess;
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
@ -81,27 +83,27 @@ public class RWTargetMemoryPcodeExecutorStatePiece
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillUninitialized(AddressSet uninitialized) {
|
||||
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
|
||||
if (space.isUniqueSpace()) {
|
||||
return;
|
||||
return uninitialized;
|
||||
}
|
||||
AddressSetView unknown;
|
||||
unknown = backing.intersectUnknown(uninitialized);
|
||||
AddressSet addrsUninit = addrSet(uninitialized);
|
||||
unknown = backing.intersectUnknown(addrsUninit);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
return uninitialized;
|
||||
}
|
||||
if (waitTimeout(backing.readFromTargetMemory(unknown))) {
|
||||
unknown = backing.intersectUnknown(uninitialized);
|
||||
if (backing.isLive() && waitTimeout(backing.readFromTargetMemory(unknown))) {
|
||||
unknown = backing.intersectUnknown(addrsUninit);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
return uninitialized;
|
||||
}
|
||||
}
|
||||
if (backing.readFromStaticImages(bytes, unknown)) {
|
||||
unknown = backing.intersectUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ULongSpan bound = uninitialized.bound();
|
||||
return bytes.getUninitialized(bound.min(), bound.max());
|
||||
}
|
||||
return uninitialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.service.emulation;
|
||||
import java.util.Map;
|
||||
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.PcodeDebuggerRegistersAccess;
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
@ -78,15 +79,14 @@ public class RWTargetRegistersPcodeExecutorStatePiece
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillUninitialized(AddressSet uninitialized) {
|
||||
if (space.isUniqueSpace()) {
|
||||
return;
|
||||
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
|
||||
if (space.isUniqueSpace() || !backing.isLive()) {
|
||||
return uninitialized;
|
||||
}
|
||||
if (!backing.isLive()) {
|
||||
return;
|
||||
}
|
||||
AddressSetView unknown = backing.intersectUnknown(uninitialized);
|
||||
AddressSet addrsUninit = addrSet(uninitialized);
|
||||
AddressSetView unknown = backing.intersectUnknown(addrsUninit);
|
||||
waitTimeout(backing.readFromTargetRegisters(unknown));
|
||||
return uninitialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -353,7 +353,7 @@ public class UnwindAnalysis {
|
||||
public SymPcodeExecutorState executeToPc(Deque<BlockEdge> to) throws CancelledException {
|
||||
SymPcodeExecutorState state = new SymPcodeExecutorState(program);
|
||||
SymPcodeExecutor exec =
|
||||
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE, warnings, monitor);
|
||||
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE_READ, warnings, monitor);
|
||||
executePathTo(exec, to);
|
||||
executeBlockTo(exec, pcBlock.block, pc);
|
||||
return state;
|
||||
@ -375,7 +375,7 @@ public class UnwindAnalysis {
|
||||
public SymPcodeExecutorState executeFromPc(SymPcodeExecutorState state,
|
||||
Deque<BlockEdge> from) throws CancelledException {
|
||||
SymPcodeExecutor exec =
|
||||
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE, warnings, monitor);
|
||||
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE_READ, warnings, monitor);
|
||||
executeBlockFrom(exec, pcBlock.block, pc);
|
||||
executePathFrom(exec, from);
|
||||
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.DebuggerStaticMappingService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
||||
import ghidra.pcode.exec.DecodePcodeExecutionException;
|
||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
@ -343,13 +344,53 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||
.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
|
||||
public void testExecutionBreakpoint() throws Exception {
|
||||
createProgram();
|
||||
intoProject(program);
|
||||
Assembler asm = Assemblers.getAssembler(program);
|
||||
Memory memory = program.getMemory();
|
||||
Address addrText = addr(program, 0x000400000);
|
||||
Address addrText = addr(program, 0x00400000);
|
||||
Register regPC = program.getRegister("pc");
|
||||
Register regR0 = program.getRegister("r0");
|
||||
Register regR1 = program.getRegister("r1");
|
||||
@ -411,7 +452,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||
intoProject(program);
|
||||
Assembler asm = Assemblers.getAssembler(program);
|
||||
Memory memory = program.getMemory();
|
||||
Address addrText = addr(program, 0x000400000);
|
||||
Address addrText = addr(program, 0x00400000);
|
||||
Address addrI1;
|
||||
Address addrI2;
|
||||
try (Transaction tx = program.openTransaction("Initialize")) {
|
||||
@ -470,7 +511,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||
intoProject(program);
|
||||
Assembler asm = Assemblers.getAssembler(program);
|
||||
Memory memory = program.getMemory();
|
||||
Address addrText = addr(program, 0x000400000);
|
||||
Address addrText = addr(program, 0x00400000);
|
||||
Register regPC = program.getRegister("pc");
|
||||
Register regR0 = program.getRegister("r0");
|
||||
Register regR1 = program.getRegister("r1");
|
||||
@ -537,7 +578,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||
intoProject(program);
|
||||
Assembler asm = Assemblers.getAssembler(program);
|
||||
Memory memory = program.getMemory();
|
||||
Address addrText = addr(program, 0x000400000);
|
||||
Address addrText = addr(program, 0x00400000);
|
||||
Register regPC = program.getRegister("pc");
|
||||
Register regR0 = program.getRegister("r0");
|
||||
Register regR1 = program.getRegister("r1");
|
||||
|
@ -21,7 +21,8 @@ import java.util.Map;
|
||||
import generic.ULongSpan;
|
||||
import generic.ULongSpan.ULongSpanSet;
|
||||
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.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
@ -76,19 +77,29 @@ public class BytesTracePcodeExecutorStatePiece
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||
if (!uninitialized.isEmpty()) {
|
||||
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||
if (uninitialized.isEmpty()) {
|
||||
return uninitialized;
|
||||
}
|
||||
// TODO: Warn or bail when reading UNKNOWN bytes
|
||||
// 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());
|
||||
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) {
|
||||
|
@ -30,6 +30,11 @@ import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||
extends AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece {
|
||||
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data);
|
||||
}
|
||||
@ -45,11 +50,6 @@ public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||
spaceMap.fork());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
|
||||
return backing.getKnownNow();
|
||||
}
|
||||
|
@ -149,6 +149,23 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace
|
||||
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
|
||||
public AddressSetView intersectUnknown(AddressSetView guestView) {
|
||||
TraceMemoryOperations ops = getMemoryOps(false);
|
||||
|
@ -80,6 +80,11 @@ public class DefaultPcodeTraceThreadAccess
|
||||
return memory.getKnownBefore().union(registers.getKnownBefore());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView intersectViewKnown(AddressSetView view) {
|
||||
return memory.intersectViewKnown(view).union(registers.intersectViewKnown(view));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView intersectUnknown(AddressSetView view) {
|
||||
return memory.intersectUnknown(view).union(registers.intersectUnknown(view));
|
||||
|
@ -92,6 +92,15 @@ public interface PcodeTraceDataAccess {
|
||||
*/
|
||||
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
|
||||
* {@link TraceMemoryState#UNKNOWN} memory
|
||||
|
@ -221,7 +221,7 @@ public class TraceSleighUtilsTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
new PcodeExecutor<>(sp.getLanguage(),
|
||||
BytesPcodeArithmetic.forLanguage(b.language),
|
||||
new DirectBytesTracePcodeExecutorState(b.host, 0, thread, 0),
|
||||
Reason.EXECUTE);
|
||||
Reason.EXECUTE_READ);
|
||||
sp.execute(executor, PcodeUseropLibrary.nil());
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class TestThread implements PcodeThread<Void> {
|
||||
@Override
|
||||
public PcodeExecutor<Void> getExecutor() {
|
||||
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) {
|
||||
machine.record.add("x:" + name);
|
||||
// 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: size should actually be long (size_t)
|
||||
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....
|
||||
return desc.write(buf);
|
||||
}
|
||||
|
@ -869,11 +869,12 @@ public interface Span<N, S extends Span<N, S>> extends Comparable<S> {
|
||||
|
||||
@Override
|
||||
public S bound() {
|
||||
S result = domain.empty();
|
||||
for (Entry<S, V> entry : spanTree.values()) {
|
||||
result = result.bound(entry.getKey());
|
||||
if (spanTree.isEmpty()) {
|
||||
return domain.empty();
|
||||
}
|
||||
return result;
|
||||
S first = spanTree.firstEntry().getValue().getKey();
|
||||
S last = spanTree.lastEntry().getValue().getKey();
|
||||
return first.bound(last);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,16 +165,16 @@ public class AdaptedEmulator implements Emulator {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||
if (uninitialized.isEmpty()) {
|
||||
return;
|
||||
return uninitialized;
|
||||
}
|
||||
if (backing.loadImage == null) {
|
||||
if (space.isUniqueSpace()) {
|
||||
throw new AccessPcodeExecutionException(
|
||||
"Attempted to read from uninitialized unique: " + uninitialized);
|
||||
}
|
||||
return;
|
||||
return uninitialized;
|
||||
}
|
||||
ULongSpan bound = uninitialized.bound();
|
||||
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()),
|
||||
(int) span.length());
|
||||
}
|
||||
return bytes.getUninitialized(bound.min(), bound.max());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,7 +159,8 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
* @param state the composite state assigned to the 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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* written for {@link Emulator}.
|
||||
*/
|
||||
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE) {
|
||||
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE_READ) {
|
||||
@Override
|
||||
public void setMemoryBank(MemoryBank bank) {
|
||||
// Ignore
|
||||
|
@ -17,6 +17,7 @@ package ghidra.pcode.emu;
|
||||
|
||||
import ghidra.app.util.PseudoInstruction;
|
||||
import ghidra.pcode.emulate.InstructionDecodeException;
|
||||
import ghidra.pcode.exec.DecodePcodeExecutionException;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
@ -87,7 +88,7 @@ public class SleighInstructionDecoder implements InstructionDecoder {
|
||||
block = disassembler.pseudoDisassembleBlock(
|
||||
state.getConcreteBuffer(address, Purpose.DECODE), context, 1);
|
||||
if (block == null || block.isEmpty()) {
|
||||
throw new InstructionDecodeException(lastMsg, address);
|
||||
throw new DecodePcodeExecutionException(lastMsg, address);
|
||||
}
|
||||
instruction = (PseudoInstruction) block.getInstructionAt(address);
|
||||
lengthWithDelays = computeLength();
|
||||
|
@ -25,6 +25,7 @@ import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
@ -41,16 +42,21 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||
protected class StateMemBuffer implements MemBufferMixin {
|
||||
protected final Address address;
|
||||
protected final BytesPcodeExecutorStateSpace<?> source;
|
||||
protected final Reason reason;
|
||||
|
||||
/**
|
||||
* Construct a buffer bound to the given space, at the given address
|
||||
*
|
||||
* @param address the address
|
||||
* @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.source = source;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,8 +76,8 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||
|
||||
@Override
|
||||
public int getBytes(ByteBuffer buffer, int addressOffset) {
|
||||
byte[] data = source.read(address.getOffset() + addressOffset, buffer.remaining(),
|
||||
Reason.EXECUTE);
|
||||
byte[] data =
|
||||
source.read(address.getOffset() + addressOffset, buffer.remaining(), reason);
|
||||
buffer.put(data);
|
||||
return data.length;
|
||||
}
|
||||
@ -160,7 +166,8 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -18,7 +18,7 @@ package ghidra.pcode.exec;
|
||||
import java.util.*;
|
||||
|
||||
import generic.ULongSpan;
|
||||
import generic.ULongSpan.ULongSpanSet;
|
||||
import generic.ULongSpan.*;
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
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.
|
||||
*
|
||||
* @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) {
|
||||
Address start = space.getAddress(span.min());
|
||||
Address end = space.getAddress(span.max());
|
||||
return new AddressRangeImpl(start, end);
|
||||
return new AddressRangeImpl(
|
||||
space.getAddress(span.min()),
|
||||
space.getAddress(span.max()));
|
||||
}
|
||||
|
||||
protected ULongSpan spanRng(AddressRange range) {
|
||||
return ULongSpan.span(
|
||||
range.getMinAddress().getOffset(),
|
||||
range.getMaxAddress().getOffset());
|
||||
}
|
||||
|
||||
protected AddressSet addrSet(ULongSpanSet set) {
|
||||
@ -108,6 +116,20 @@ public class BytesPcodeExecutorStateSpace<B> {
|
||||
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) {
|
||||
Set<Register> regs = new TreeSet<>();
|
||||
for (AddressRange rng : set) {
|
||||
@ -151,12 +173,32 @@ public class BytesPcodeExecutorStateSpace<B> {
|
||||
* @return the bytes read
|
||||
*/
|
||||
public byte[] read(long offset, int size, Reason reason) {
|
||||
if (backing != null) {
|
||||
readUninitializedFromBacking(bytes.getUninitialized(offset, offset + size - 1));
|
||||
ULongSpanSet uninitialized = bytes.getUninitialized(offset, offset + size - 1);
|
||||
if (uninitialized.isEmpty()) {
|
||||
return readBytes(offset, size, reason);
|
||||
}
|
||||
ULongSpanSet stillUninit = bytes.getUninitialized(offset, offset + size - 1);
|
||||
if (!stillUninit.isEmpty() && reason == Reason.EXECUTE) {
|
||||
warnUninit(stillUninit);
|
||||
if (backing != null) {
|
||||
uninitialized = readUninitializedFromBacking(uninitialized);
|
||||
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);
|
||||
}
|
||||
|
@ -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 ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
@ -49,21 +50,31 @@ public interface PcodeArithmetic<T> {
|
||||
*/
|
||||
enum Purpose {
|
||||
/** The value is needed to parse an instruction */
|
||||
DECODE,
|
||||
DECODE(Reason.EXECUTE_DECODE),
|
||||
/** The value is needed for disassembly context */
|
||||
CONTEXT,
|
||||
CONTEXT(Reason.EXECUTE_READ),
|
||||
/** 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 */
|
||||
BRANCH,
|
||||
BRANCH(Reason.EXECUTE_READ),
|
||||
/** 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 */
|
||||
STORE,
|
||||
STORE(Reason.EXECUTE_READ),
|
||||
/** Some other reason, perhaps for userop library use */
|
||||
OTHER,
|
||||
OTHER(Reason.EXECUTE_READ),
|
||||
/** 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 {
|
||||
/** The value is needed as the default program counter or disassembly context */
|
||||
RE_INIT,
|
||||
/** The value is needed by the emulator in the course of execution */
|
||||
EXECUTE,
|
||||
/** The value is being inspected */
|
||||
/** The value is being read by the emulator as data in the course of execution */
|
||||
EXECUTE_READ,
|
||||
/** 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
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class AnnotatedPcodeUseropLibraryTest extends AbstractGTest {
|
||||
protected PcodeExecutor<byte[]> createBytesExecutor(SleighLanguage language) throws Exception {
|
||||
PcodeExecutorState<byte[]> state = new BytesPcodeExecutorState(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,
|
||||
|
@ -802,8 +802,10 @@ public class Disassembler implements DisassemblerConflictHandler {
|
||||
disassembleInstructionBlock(block, blockMemBuffer, null, limit, null, false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Pseudo block disassembly failure at " + blockMemBuffer.getAddress() +
|
||||
": " + e.getMessage(), e);
|
||||
String message = "Pseudo block disassembly failure at " + blockMemBuffer.getAddress() +
|
||||
": " + e.getMessage();
|
||||
Msg.error(this, message, e);
|
||||
reportMessage(message);
|
||||
}
|
||||
finally {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user