GP-1529: Interrupt emulator when decoding uninitialized memory

This commit is contained in:
Dan 2023-03-29 16:36:17 -04:00
parent 738e662e82
commit 4aa54dd1f9
25 changed files with 274 additions and 100 deletions

View File

@ -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));
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);
}
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
uninitialized = readUninitializedFromTarget(uninitialized);
if (uninitialized.isEmpty()) {
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) {

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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()) {
// 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());
}
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!!!
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) {

View File

@ -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();
}

View File

@ -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);

View File

@ -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));

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}
/**

View File

@ -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
}

View File

@ -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,

View File

@ -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 {