_pcodetest*} will be processed. All files contained within a matching
- * subdirectory will be treated as related binaries and imported. Any *.gzf file will be
- * imported but assumed to be pre-analyzed. Binary files to be imported and analyzed must
- * utilize the *.out file extension.
+ * {@literal _pcodetest*} will be processed. All files contained within a
+ * matching subdirectory will be treated as related binaries and imported. Any *.gzf file will be
+ * imported but assumed to be pre-analyzed. Binary files to be imported and analyzed must utilize
+ * the *.out file extension.
*
* JUnit X86EmulatorTest could utilize the following binary file naming strategy:
+ *
*
* pcodetests/X86_PCodeTests
* - binary1.o
@@ -92,25 +93,22 @@ import utility.application.ApplicationLayout;
* - pcodetests/X86_PCodeTest.out
*
*
- * Any *.out binary found will be imported and analyzed. The resulting program will
- * be stored as a gzf in the test-output cache directory. These cached files will be used
- * instead of a test resource binary if that binary's md5 checksum has not changed since its cached
- * gzf was created. This use of cache files will allow the tests to run quickly on subsequent
- * executions. If re-analysis is required, the cache will need to be cleared manually.
+ * Any *.out binary found will be imported and analyzed. The resulting program will be stored as a
+ * gzf in the test-output cache directory. These cached files will be used instead of a test
+ * resource binary if that binary's md5 checksum has not changed since its cached gzf was created.
+ * This use of cache files will allow the tests to run quickly on subsequent executions. If
+ * re-analysis is required, the cache will need to be cleared manually.
*
- * NOTES:
- * 1. Dummy Test Methods must be added for all known test groups. See bottom of this file. This
- * all allows for the single test trace mode execution to work within Eclipse.
- * 2. Trace logging disabled by default when all test groups are run (see buildEmulatorTestSuite method).
- * Specific traceLevel and traceLog file controlled via environment properties
- * EmuTestTraceLevel and EmuTestTraceFile.
- * 3. The TestInfo structure must be properly maintained within the datatype archive EmuTesting.gdt
- * and field naming consistent with use in PCodeTestControlBlock.java
- * 4. The {@link #initializeState(EmulatorTestRunner, Program)} may be overriden to initialize the
- * register values if needed. This should be based upon symbols or other program information
- * if possible since hardcoded constants may not track future builds of a test binaries.
- * An attempt is made to initialize the stack pointer automatically based upon well known
- * stack initialization symbols.
+ * NOTES: 1. Dummy Test Methods must be added for all known test groups. See bottom of this file.
+ * This all allows for the single test trace mode execution to work within Eclipse. 2. Trace logging
+ * disabled by default when all test groups are run (see buildEmulatorTestSuite method). Specific
+ * traceLevel and traceLog file controlled via environment properties EmuTestTraceLevel and
+ * EmuTestTraceFile. 3. The TestInfo structure must be properly maintained within the datatype
+ * archive EmuTesting.gdt and field naming consistent with use in PCodeTestControlBlock.java 4. The
+ * {@link #initializeState(EmulatorTestRunner, Program)} may be overriden to initialize the register
+ * values if needed. This should be based upon symbols or other program information if possible
+ * since hardcoded constants may not track future builds of a test binaries. An attempt is made to
+ * initialize the stack pointer automatically based upon well known stack initialization symbols.
*/
public abstract class ProcessorEmulatorTestAdapter extends TestCase implements ExecutionListener {
@@ -495,10 +493,11 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Create TestSuite based upon available test groups contained within binary
- * test files associated with target processor.
- * @param emulatorTestClass test which extends ProcessorEmulatorTestAdapter
- * and whose name ends with "EmulatorTest".
+ * Create TestSuite based upon available test groups contained within binary test files
+ * associated with target processor.
+ *
+ * @param emulatorTestClass test which extends ProcessorEmulatorTestAdapter and
+ * whose name ends with "EmulatorTest".
* @return test suite
*/
public static final Test buildEmulatorTestSuite(Class> emulatorTestClass) {
@@ -825,7 +824,8 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
int byteCount = dumpSize * elementSize;
- byte[] bytes = emulatorTestRunner.getEmulatorHelper().readMemory(dumpAddr, byteCount);
+ byte[] bytes =
+ emulatorTestRunner.getEmulatorThread().getState().inspectConcrete(dumpAddr, byteCount);
int index = 0;
log(null, "MEMORY DUMP (" + elementFormat + "): " + comment);
@@ -902,11 +902,6 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
log(testRunner.getTestGroup(), " Write " + formatAssignmentString(address, size, values));
}
- @Override
- public void stepCompleted(EmulatorTestRunner testRunner) {
- logState(testRunner);
- }
-
/**
* Converts the stack trace into a string
*/
@@ -1088,8 +1083,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Single unit test which handles named test group specified during test
- * instantiation.
+ * Single unit test which handles named test group specified during test instantiation.
*/
@Override
public final void runTest() {
@@ -1187,7 +1181,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
boolean done;
if (traceDisabled) {
- done = testRunner.execute(EXECUTION_TIMEOUT_MS, TaskMonitor.DUMMY);
+ done = testRunner.execute(EXECUTION_TIMEOUT_MS);
}
else {
done = testRunner.executeSingleStep(MAX_EXECUTION_STEPS);
@@ -1327,6 +1321,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
/**
* Get symbol name which defines initial stack pointer offset
+ *
* @return stack symbol or null
*/
protected String getPreferredStackSymbolName() {
@@ -1350,8 +1345,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
Address currentAddr = testRunner.getCurrentAddress();
// dump context register state if decode failure
- RegisterValue contextRegValue =
- testRunner.getEmulatorHelper().getEmulator().getContextRegisterValue();
+ RegisterValue contextRegValue = testRunner.getEmulatorThread().getContext();
if (contextRegValue == null) {
return;
}
@@ -1383,7 +1377,8 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
// check for memory modification
- byte[] emuBytes = testRunner.getEmulatorHelper().readMemory(currentAddr, len);
+ byte[] emuBytes =
+ testRunner.getEmulatorThread().getState().inspectConcrete(currentAddr, len);
byte[] programBytes = new byte[emuBytes.length];
program.getMemory().getBytes(currentAddr, programBytes);
if (!Arrays.equals(emuBytes, programBytes)) {
@@ -1425,6 +1420,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
/**
* Get the maximum defined memory address ignoring any overlays which have been defined.
+ *
* @return max defined physical address
*/
protected static final Address getMaxDefinedMemoryAddress(Program program) {
@@ -1441,10 +1437,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Add specified test names to the set of tests which should be ignored due to know issues
- * or limitations. The tests will still be executed, if present, however they will
- * not be included in pass/fail counts. They will appear in log as "(IGNORED)" test
- * result.
+ * Add specified test names to the set of tests which should be ignored due to know issues or
+ * limitations. The tests will still be executed, if present, however they will not be included
+ * in pass/fail counts. They will appear in log as "(IGNORED)" test result.
+ *
* @param testNames one or more test names to be ignored
*/
protected void addIgnoredTests(String... testNames) {
@@ -1456,10 +1452,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
//
/**
- * Get the processor designator used to identify test binary files/folder.
- * The default implementation requires the JUnit test class name to end with
- * "EmulatorTest" where the portion of the name proceeding this suffix will be
- * used as the processor designator
+ * Get the processor designator used to identify test binary files/folder. The default
+ * implementation requires the JUnit test class name to end with "EmulatorTest" where the
+ * portion of the name proceeding this suffix will be used as the processor designator
+ *
* @return processor designator
*/
protected String getProcessorDesignator() {
@@ -1473,6 +1469,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
/**
* Get CUint file designator if use of A, B, C... is not suitable.
+ *
* @param fileIndex file index within sorted list
* @param filePath binary file path
* @return short file designator for use in qualified test name
@@ -1482,12 +1479,12 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Invoked for each program immediately prior to executing a test group.
- * By default this method will initialize the register states based upon the
- * specific register values/context stored at the test group function entry point.
- * Such register values may have been established via the processor specification,
- * loader or analyzers. A specific test may override or extend
- * this behavior for other registers as needed.
+ * Invoked for each program immediately prior to executing a test group. By default this method
+ * will initialize the register states based upon the specific register values/context stored at
+ * the test group function entry point. Such register values may have been established via the
+ * processor specification, loader or analyzers. A specific test may override or extend this
+ * behavior for other registers as needed.
+ *
* @param testRunner emulator group test runner/facilitator
* @param program
* @throws Exception if initialization criteria has not been satisfied
@@ -1513,11 +1510,11 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Invoked immediately following import allow byte processing prior to
- * control structure identification.
- * NOTE: This method will only be invoked once during the first test setup
- * for all test binaries found. This method will not be invoked
- * during subsequent tests since the analyzed program will be cached.
+ * Invoked immediately following import allow byte processing prior to control structure
+ * identification. NOTE: This method will only be invoked once during the first test setup for
+ * all test binaries found. This method will not be invoked during subsequent tests since the
+ * analyzed program will be cached.
+ *
* @param program
* @throws Exception
*/
@@ -1527,10 +1524,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
/**
* Invoked prior to analysis to allow analysis options or other pre-analysis
- * inspection/modification to be performed.
- * NOTE: This method will only be invoked once during the first test setup
- * for all test binaries found. This method will not be invoked
- * during subsequent tests since the analyzed program will be cached.
+ * inspection/modification to be performed. NOTE: This method will only be invoked once during
+ * the first test setup for all test binaries found. This method will not be invoked during
+ * subsequent tests since the analyzed program will be cached.
+ *
* @param program
* @throws Exception
*/
@@ -1539,11 +1536,11 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Invoked for non-gzf files immediately after the analyze method to
- * perform any follow-up changes of inspection of the program.
- * NOTE: This method will only be invoked once during the first test setup
- * for all test binaries found. This method will not be invoked
- * during subsequent tests since the analyzed program will be cached.
+ * Invoked for non-gzf files immediately after the analyze method to perform any follow-up
+ * changes of inspection of the program. NOTE: This method will only be invoked once during the
+ * first test setup for all test binaries found. This method will not be invoked during
+ * subsequent tests since the analyzed program will be cached.
+ *
* @param program
* @throws Exception
*/
@@ -1552,10 +1549,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Invoked for non-gzf files to perform auto-analysis.
- * NOTE: This method will only be invoked once during the first test setup
- * for all test binaries found. This method will not be invoked
+ * Invoked for non-gzf files to perform auto-analysis. NOTE: This method will only be invoked
+ * once during the first test setup for all test binaries found. This method will not be invoked
* during subsequent tests since the analyzed program will be cached.
+ *
* @param program
* @throws Exception
*/
@@ -1713,8 +1710,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Get the loader class which should be used. A null value should be
- * return to use the preferred loader.
+ * Get the loader class which should be used. A null value should be return to use the preferred
+ * loader.
+ *
* @return loader class or null
*/
protected Class extends Loader> getLoaderClass() {
@@ -2137,7 +2135,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb mode)
+ * Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb
+ * mode)
+ *
* @param offset
* @param alignment
* @return
@@ -2147,7 +2147,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
}
/**
- * Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb mode)
+ * Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb
+ * mode)
+ *
* @param addr
* @param alignment
* @return
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/app/emulator/AdaptedEmulator.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/app/emulator/AdaptedEmulator.java
index 9cebdeac78..4c62ece5e9 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/app/emulator/AdaptedEmulator.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/app/emulator/AdaptedEmulator.java
@@ -159,7 +159,7 @@ public class AdaptedEmulator implements Emulator {
@Override
public AddressSetView readUninitialized(PcodeExecutorStatePiece piece,
- AddressSetView set) {
+ AddressSetView set, Reason reason) {
PcodeExecutorStatePiece bytesPiece =
PcodeStateCallbacks.checkValueDomain(piece, byte[].class);
if (bytesPiece == null) {
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ComposedPcodeEmulationCallbacks.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ComposedPcodeEmulationCallbacks.java
index 7852ceb51e..98c55388bf 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ComposedPcodeEmulationCallbacks.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ComposedPcodeEmulationCallbacks.java
@@ -18,6 +18,7 @@ package ghidra.pcode.emu;
import java.util.List;
import ghidra.pcode.exec.*;
+import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Instruction;
@@ -29,18 +30,18 @@ import ghidra.program.model.pcode.PcodeOp;
*
* The two (currently) methods that do not implement a pure broadcast pattern are
* {@link #handleMissingUserop(PcodeThread, PcodeOp, PcodeFrame, String, PcodeUseropLibrary)} and
- * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)}. For a missing
- * userop, it will terminate after the first delegate returns {@code true}, in which case it also
- * returns {@code true}. If all delegates return {@code false}, then it returns {@code false}. For
- * an uninitialized read, each delegate's returned "still-uninitialized" set is passed to the
+ * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)}. For a
+ * missing userop, it will terminate after the first delegate returns {@code true}, in which case it
+ * also returns {@code true}. If all delegates return {@code false}, then it returns {@code false}.
+ * For an uninitialized read, each delegate's returned "still-uninitialized" set is passed to the
* subsequent delegate. The first delegate gets the set as passed into the composition. The set
* returned by the composition is that returned by the last delegate. This terminates early when any
* delegate returns the empty set.
*
*
* One (currently) other method has a non-void return type:
- * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int)}. The
- * callback is broadcast as expected, and the return value is the max returned by the delegates.
+ * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}.
+ * The callback is broadcast as expected, and the return value is the max returned by the delegates.
*
* @param the emulator's value domain
*/
@@ -207,8 +208,8 @@ public class ComposedPcodeEmulationCallbacks implements PcodeEmulationCallbac
}
@Override
- public int readUninitialized(PcodeThread thread,
- PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length) {
+ public int readUninitialized(PcodeThread thread, PcodeExecutorStatePiece piece,
+ AddressSpace space, A offset, int length, Reason reason) {
/**
* NOTE: This could use some work. It's a bit onerous to specify arbitrary, possibly
* disjoint, sets of offsets of type A. Can only be guaranteed to mean something if A is
@@ -218,7 +219,8 @@ public class ComposedPcodeEmulationCallbacks implements PcodeEmulationCallbac
*/
int maxL = 0;
for (PcodeEmulationCallbacks d : delegates) {
- maxL = Math.max(maxL, d.readUninitialized(thread, piece, space, offset, length));
+ maxL =
+ Math.max(maxL, d.readUninitialized(thread, piece, space, offset, length, reason));
if (maxL == length) {
return maxL;
}
@@ -228,10 +230,10 @@ public class ComposedPcodeEmulationCallbacks implements PcodeEmulationCallbac
@Override
public AddressSetView readUninitialized(PcodeThread thread,
- PcodeExecutorStatePiece piece, AddressSetView set) {
+ PcodeExecutorStatePiece piece, AddressSetView set, Reason reason) {
AddressSetView remains = set;
for (PcodeEmulationCallbacks d : delegates) {
- remains = d.readUninitialized(thread, piece, remains);
+ remains = d.readUninitialized(thread, piece, remains, reason);
if (remains.isEmpty()) {
return remains;
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java
index 76886f2953..88cb52b59d 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java
@@ -374,9 +374,13 @@ public class DefaultPcodeThread implements PcodeThread {
@Override
public void assignContext(RegisterValue context) {
- if (!context.getRegister().isProcessorContext()) {
+ if (context.getRegister().getBaseRegister() != contextreg) {
throw new IllegalArgumentException("context must be the contextreg value");
}
+ if (this.context == null) {
+ assert this.contextreg == Register.NO_CONTEXT;
+ return;
+ }
this.context = this.context.assign(context.getRegister(), context);
}
@@ -386,7 +390,14 @@ public class DefaultPcodeThread implements PcodeThread {
}
protected final void writeContext(RegisterValue context) {
+ if (contextreg == Register.NO_CONTEXT && context == null) {
+ return;
+ }
assignContext(context);
+ if (this.context == null) {
+ assert this.contextreg == Register.NO_CONTEXT;
+ return;
+ }
state.setVar(contextreg, arithmetic.fromConst(
this.context.getUnsignedValueIgnoreMask(),
contextreg.getMinimumByteSize(), true));
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/EmulatorUtilities.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/EmulatorUtilities.java
index 542435202b..51e5adb8e8 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/EmulatorUtilities.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/EmulatorUtilities.java
@@ -16,16 +16,20 @@
package ghidra.pcode.emu;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
import java.util.Arrays;
import java.util.NoSuchElementException;
import ghidra.pcode.exec.PcodeArithmetic;
+import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
-import ghidra.program.model.mem.MemoryAccessException;
-import ghidra.program.model.mem.MemoryBlock;
+import ghidra.program.model.mem.*;
import ghidra.util.DifferenceAddressSetView;
import ghidra.util.Msg;
@@ -232,6 +236,51 @@ public enum EmulatorUtilities {
return chooseStackRange(program, entry, DEFAULT_STACK_SIZE);
}
+ /**
+ * Initialize the given thread's registers with the given program counter and (optionall)
+ * program register context.
+ *
+ * @param the type of values in the emulator
+ * @param thread the thread to initialize
+ * @param program optionally, the program whose context to read
+ * @param pc the program counter, also used for register context
+ */
+ public static void initializeRegisters(PcodeThread thread, Program program, Address pc) {
+ if (program != null) {
+ ThreadPcodeExecutorState state = thread.getState();
+ ProgramProcessorContext ctx =
+ new ProgramProcessorContext(program.getProgramContext(), pc);
+ for (Register reg : ctx.getRegisters()) {
+ if (!reg.isBaseRegister() || reg.isProcessorContext()) {
+ continue;
+ }
+ RegisterValue rv = ctx.getRegisterValue(reg);
+ if (rv == null || !rv.hasAnyValue()) {
+ continue;
+ }
+ if (!rv.hasValue()) {
+ rv = new RegisterValue(reg, BigInteger.ZERO).combineValues(rv);
+ }
+ /**
+ * NOTE: In theory, there's no need to combine masked values, if this is a fresh
+ * emulator. If I had to guess, the client would want their values to take
+ * precedence, so they should overwrite the values after calling this method.
+ * Combining can be problematic, because the emulator could return some abstraction
+ * for the current value.
+ */
+ state.setRegisterValue(rv);
+ }
+ }
+
+ thread.overrideCounter(pc);
+ if (program != null) {
+ thread.overrideContext(program.getProgramContext().getDisassemblyContext(pc));
+ }
+ else {
+ thread.overrideContextWithDefault();
+ }
+ }
+
/**
* Prepare a thread to emulate a given function
*
@@ -250,31 +299,88 @@ public enum EmulatorUtilities {
Register sp = cSpec.getStackPointer();
ThreadPcodeExecutorState state = thread.getState();
- ProgramProcessorContext ctx =
- new ProgramProcessorContext(program.getProgramContext(), entry);
- for (Register reg : ctx.getRegisters()) {
- if (!reg.isBaseRegister()) {
- continue;
- }
- RegisterValue rv = ctx.getRegisterValue(reg);
- if (rv == null || !rv.hasAnyValue()) {
- continue;
- }
- /**
- * NOTE: In theory, there's no need to combine masked values, if this is a fresh
- * emulator. If I had to guess, the client would want their values to take precedence,
- * so they should overwrite the values after calling this method. Combining can be
- * problematic, because the emulator could return some abstraction for the current
- * value.
- */
- state.setRegisterValue(rv);
- }
+ initializeRegisters(thread, program, entry);
- AddressRange stack = chooseStackRange(program, entry);
+ AddressRange stack = chooseStackRange(program, entry, stackSize);
long stackOffset = cSpec.stackGrowsNegative() ? stack.getMaxAddress().getOffset() + 1
: stack.getMinAddress().getOffset();
state.setVar(sp, arithmetic.fromConst(stackOffset, sp.getMinimumByteSize()));
+ }
- thread.overrideCounter(entry);
+ /**
+ * Prepare a thread to emulate a given function, using the default stack size
+ *
+ * @param the type of values in the emulator
+ * @param thread the thread whose state to initialize
+ * @param function the function to prepare to enter
+ */
+ public static void initializeForFunction(PcodeThread thread, Function function) {
+ initializeForFunction(thread, function, DEFAULT_STACK_SIZE);
+ }
+
+ /**
+ * Inspect the value of the stack pointer, and return it as an address
+ *
+ * @param the type of values in the emulator
+ * @param thread the thread whose stack pointer to inspect
+ * @param cSpec the compiler spec defining the stack register and base space
+ * @return the address pointed to by the stack pointer
+ */
+ public static Address inspectStackPointer(PcodeThread thread, CompilerSpec cSpec) {
+ Register sp = cSpec.getStackPointer();
+ long stackOffset =
+ thread.getState().inspectRegisterValue(sp).getUnsignedValue().longValueExact();
+ return cSpec.getStackBaseSpace().getAddress(stackOffset);
+ }
+
+ /**
+ * Decode a null-terminated string of the given charset from memory
+ *
+ * This retrieves bytes one at a time from the given state and feeds them to a decoder until
+ * that decoder emits a null character.
+ *
+ * @param state the memory
+ * @param ptr the starting address
+ * @param cs the character set
+ * @param maxBytes the maximum number of bytes to read
+ * @return the decoded string
+ */
+ public static String decodeNullTerminatedString(PcodeExecutorState> state, Address ptr,
+ Charset cs, int maxBytes) {
+ CharsetDecoder decoder = cs.newDecoder();
+ ByteBuffer in = ByteBuffer.allocate(1);
+ CharBuffer out = CharBuffer.allocate(maxBytes); // should never be more chars than bytes
+ MemBuffer buf = state.getConcreteBuffer(ptr, Purpose.INSPECT);
+ for (int i = 0; i < maxBytes; i++) {
+ in.position(0);
+ int len = buf.getBytes(in.array(), i);
+ in.limit(len);
+ if (len == 0) {
+ // Flush out any partials and be done
+ decoder.decode(in, out, true);
+ return out.flip().toString();
+ }
+ decoder.decode(in, out, false);
+ int nullPos = out.position() - 1;
+ if (nullPos >= 0 && out.get(nullPos) == 0) {
+ return out.position(nullPos).flip().toString();
+ }
+ }
+ in.position(0);
+ in.limit(0);
+ // Flush out any partials and be done
+ decoder.decode(in, out, true);
+ return out.flip().toString();
+ }
+
+ /**
+ * Decode an ASCII null-terminated string of at most 128 bytes from memory
+ *
+ * @param state the memory
+ * @param ptr the starting address
+ * @return the decoded string
+ */
+ public static String decodeNullTerminatedString(PcodeExecutorState> state, Address ptr) {
+ return decodeNullTerminatedString(state, ptr, Charset.forName("ASCII"), 128);
}
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeEmulationCallbacks.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeEmulationCallbacks.java
index 1b6b219143..640c23dac9 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeEmulationCallbacks.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeEmulationCallbacks.java
@@ -17,6 +17,7 @@ package ghidra.pcode.emu;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
+import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Instruction;
@@ -378,18 +379,19 @@ public interface PcodeEmulationCallbacks {
* @param space the address space of the operand
* @param offset the offset of the operand
* @param length the size of the operand
+ * @param reason the reason for reading
* @return the length of the operand just initialized, typically 0 or {@code length}
*/
- default int readUninitialized(PcodeThread thread,
- PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length) {
+ default int readUninitialized(PcodeThread thread, PcodeExecutorStatePiece piece,
+ AddressSpace space, A offset, int length, Reason reason) {
return 0;
}
/**
* Typically used from within
- * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int)}
+ * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}
* to forward to the callback for concrete addressing
- * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)}.
+ * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)}.
*
* @param the piece's address domain
* @param the piece's value domain
@@ -399,13 +401,15 @@ public interface PcodeEmulationCallbacks {
* @param space the address space of the operand
* @param offset the offset of the operand
* @param length the size of the operand
+ * @param reason the reason for reading
* @return the length of the operand just initialized, typically 0 or {@code length}
*/
default int delegateReadUninitialized(PcodeThread thread,
- PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length) {
+ PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length,
+ Reason reason) {
long lOffset = piece.getAddressArithmetic().toLong(offset, Purpose.LOAD);
AddressSet set = PcodeStateCallbacks.rngSet(space, lOffset, length);
- AddressSetView remains = readUninitialized(thread, piece, set);
+ AddressSetView remains = readUninitialized(thread, piece, set, reason);
if (set == remains) {
return 0;
}
@@ -432,18 +436,19 @@ public interface PcodeEmulationCallbacks {
* {@link #dataWritten(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Object)}
* @param piece the state piece
* @param set the uninitialized portion required
+ * @param reason the reason for reading
* @return the addresses in {@code set} that remain uninitialized
*/
default AddressSetView readUninitialized(PcodeThread thread,
- PcodeExecutorStatePiece piece, AddressSetView set) {
+ PcodeExecutorStatePiece piece, AddressSetView set, Reason reason) {
return set;
}
/**
* Typically used from within
- * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)} to forward
- * to the callback for abstract addressing
- * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int)}.
+ * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)} to
+ * forward to the callback for abstract addressing
+ * {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}.
*
* @param the piece's address domain
* @param the piece's value domain
@@ -451,10 +456,11 @@ public interface PcodeEmulationCallbacks {
* {@link #dataWritten(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Object)}
* @param piece the state piece
* @param set the uninitialized portion required
+ * @param reason the reason for reading
* @return the addresses in {@code set} that remain uninitialized
*/
default AddressSetView delegateReadUninitialized(PcodeThread thread,
- PcodeExecutorStatePiece piece, AddressSetView set) {
+ PcodeExecutorStatePiece piece, AddressSetView set, Reason reason) {
if (set.isEmpty()) {
return set;
}
@@ -463,7 +469,7 @@ public interface PcodeEmulationCallbacks {
for (AddressRange range : set) {
int l = readUninitialized(thread, piece, range.getAddressSpace(),
piece.getAddressArithmetic().fromConst(range.getMinAddress()),
- (int) range.getLength());
+ (int) range.getLength(), reason);
if (l == 0) {
continue;
}
@@ -497,14 +503,14 @@ public interface PcodeEmulationCallbacks {
@Override
public int readUninitialized(PcodeExecutorStatePiece piece,
- AddressSpace space, A offset, int length) {
- return cb.readUninitialized(thread, piece, space, offset, length);
+ AddressSpace space, A offset, int length, Reason reason) {
+ return cb.readUninitialized(thread, piece, space, offset, length, reason);
}
@Override
public AddressSetView readUninitialized(PcodeExecutorStatePiece piece,
- AddressSetView set) {
- return cb.readUninitialized(thread, piece, set);
+ AddressSetView set, Reason reason) {
+ return cb.readUninitialized(thread, piece, set, reason);
}
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractBytesPcodeExecutorStatePiece.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractBytesPcodeExecutorStatePiece.java
index 07799ae5fb..5e6f38d23a 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractBytesPcodeExecutorStatePiece.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractBytesPcodeExecutorStatePiece.java
@@ -75,7 +75,7 @@ public abstract class AbstractBytesPcodeExecutorStatePiece
S s = getForSpace(space, false);
if (s == null) {
AddressSet set = PcodeStateCallbacks.rngSet(space, offset, size);
- if (set.equals(cb.readUninitialized(this, set))) {
+ if (set.equals(cb.readUninitialized(this, set, reason))) {
return getFromNullSpace(size, reason, cb);
}
s = getForSpace(space, false);
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java
index 62d78c7d4b..92c0a2785a 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java
@@ -211,7 +211,7 @@ public class BytesPcodeExecutorStateSpace {
if (uninitialized.isEmpty()) {
return readBytes(offset, size, reason);
}
- uninitialized = cb.readUninitialized(piece, uninitialized);
+ uninitialized = cb.readUninitialized(piece, uninitialized, reason);
if (uninitialized.isEmpty()) {
return readBytes(offset, size, reason);
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java
index f6a737c855..a1b65e064e 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java
@@ -17,6 +17,7 @@ package ghidra.pcode.exec;
import ghidra.pcode.emu.*;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
+import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.program.model.address.*;
/**
@@ -29,8 +30,8 @@ import ghidra.program.model.address.*;
* {@link PcodeExecutorState}s and/or {@link PcodeExecutorStatePiece}s just to introduce
* integration-driven behaviors. E.g., to lazily load state from an external machine-state snapshot,
* the client should implement the
- * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView)} or
- * {@link PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)}
+ * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView, Reason)} or
+ * {@link PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)}
* callback rather than extending {@link BytesPcodeExecutorStatePiece}.
*/
public interface PcodeStateCallbacks {
@@ -164,18 +165,19 @@ public interface PcodeStateCallbacks {
* @param space the address space of the operand
* @param offset the offset of the operand
* @param length the size of the operand
+ * @param reason the reason for reading
* @return the length of the operand just initialized, typically 0 or {@code length}
*/
default int readUninitialized(PcodeExecutorStatePiece piece,
- AddressSpace space, A offset, int length) {
+ AddressSpace space, A offset, int length, Reason reason) {
return 0;
}
/**
* Typically used from within
- * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int)} to forward to
- * the callback for concrete addressing
- * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView)}.
+ * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)} to
+ * forward to the callback for concrete addressing
+ * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView, Reason)}.
*
* @param the piece's address domain
* @param the piece's value domain
@@ -183,13 +185,14 @@ public interface PcodeStateCallbacks {
* @param space the address space of the operand
* @param offset the offset of the operand
* @param length the size of the operand
+ * @param reason the reason for reading
* @return the length of the operand just initialized, typically 0 or {@code length}
*/
default int delegateReadUninitialized(PcodeExecutorStatePiece piece,
- AddressSpace space, A offset, int length) {
+ AddressSpace space, A offset, int length, Reason reason) {
long lOffset = piece.getAddressArithmetic().toLong(offset, Purpose.LOAD);
AddressSet set = PcodeStateCallbacks.rngSet(space, lOffset, length);
- AddressSetView remains = readUninitialized(piece, set);
+ AddressSetView remains = readUninitialized(piece, set, reason);
if (set == remains) {
return 0;
}
@@ -214,27 +217,29 @@ public interface PcodeStateCallbacks {
* @param the piece's value domain
* @param piece the state piece
* @param set the uninitialized portion required
+ * @param reason the reason for reading
* @return the addresses in {@code set} that remain uninitialized
*/
default AddressSetView readUninitialized(PcodeExecutorStatePiece piece,
- AddressSetView set) {
+ AddressSetView set, Reason reason) {
return set;
}
/**
* Typically used from within
- * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView)} to forward to the
+ * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView, Reason)} to forward to the
* callback for abstract addressing
- * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int)}.
+ * {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}.
*
* @param the piece's address domain
* @param the piece's value domain
* @param piece the state piece
* @param set the uninitialized portion required
+ * @param reason the reason for reading
* @return the addresses in {@code set} that remain uninitialized
*/
default AddressSetView delegateReadUninitialized(PcodeExecutorStatePiece piece,
- AddressSetView set) {
+ AddressSetView set, Reason reason) {
if (set.isEmpty()) {
return set;
}
@@ -242,7 +247,7 @@ public interface PcodeStateCallbacks {
for (AddressRange range : set) {
int l = readUninitialized(piece, range.getAddressSpace(),
piece.getAddressArithmetic().fromConst(range.getMinAddress()),
- (int) range.getLength());
+ (int) range.getLength(), reason);
if (l == 0) {
continue;
}
diff --git a/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html b/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
index d92af74851..f8919f84b2 100644
--- a/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
+++ b/GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
@@ -287,9 +287,9 @@ the Python source over and add it to your PYTHONPATH.
Double-check that you have installed all the required packages and
their dependencies. A common forgotten or incorrectly-versioned
dependency is protobuf. We developed using
-protobuf==3.20.3, newer versions generally work fine. Its “sdist” package is distributed with
-Ghidra under Debugger-rmi-trace/pypkg/dist for your
-convenience.
+protobuf==3.20.3, newer versions generally work fine. Its
+“sdist” package is distributed with Ghidra under
+Debugger-rmi-trace/pypkg/dist for your convenience.
It is also possible that gdb has embedded a different
version of the interpreter than the one that python3
provides. This can happen if you built GDB or Python yourself, or you
diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
index 07963ade27..240cb37007 100644
--- a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
+++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
@@ -780,13 +780,13 @@ class="sourceCode numberSource java numberLines"><
cb.dataWritten(piece, space.getAddress(offset), size, val);
}
- public Expr get(long offset, int size, PcodeStateCallbacks cb) {
+ public Expr get(long offset, int size, Reason reason, PcodeStateCallbacks cb) {
// TODO: Handle overlaps / offcut gets and sets
Expr expr = map.get(offset);
if (expr == null) {
byte[] aOffset =
piece.getAddressArithmetic().fromConst(offset, space.getPointerSize());
- if (cb.readUninitialized(piece, space, aOffset, size) != 0) {
+ if (cb.readUninitialized(piece, space, aOffset, size, reason) != 0) {
return map.get(offset);
}
}
@@ -846,7 +846,7 @@ class="sourceCode numberSource java numberLines"><
@Override
protected Expr getFromSpace(ExprSpace space, long offset, int size, Reason reason,
PcodeStateCallbacks cb) {
- return space.get(offset, size, cb);
+ return space.get(offset, size, reason, cb);
}
@Override
diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md
index 85dc9de6fe..e842d565fa 100644
--- a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md
+++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md
@@ -555,13 +555,13 @@ public static class ExprSpace {
cb.dataWritten(piece, space.getAddress(offset), size, val);
}
- public Expr get(long offset, int size, PcodeStateCallbacks cb) {
+ public Expr get(long offset, int size, Reason reason, PcodeStateCallbacks cb) {
// TODO: Handle overlaps / offcut gets and sets
Expr expr = map.get(offset);
if (expr == null) {
byte[] aOffset =
piece.getAddressArithmetic().fromConst(offset, space.getPointerSize());
- if (cb.readUninitialized(piece, space, aOffset, size) != 0) {
+ if (cb.readUninitialized(piece, space, aOffset, size, reason) != 0) {
return map.get(offset);
}
}
@@ -621,7 +621,7 @@ public static class ExprPcodeExecutorStatePiece
@Override
protected Expr getFromSpace(ExprSpace space, long offset, int size, Reason reason,
PcodeStateCallbacks cb) {
- return space.get(offset, size, cb);
+ return space.get(offset, size, reason, cb);
}
@Override
diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java
index 7e33a5978c..6b2ddfa289 100644
--- a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java
+++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java
@@ -336,13 +336,13 @@ public class ModelingScript extends GhidraScript {
cb.dataWritten(piece, space.getAddress(offset), size, val);
}
- public Expr get(long offset, int size, PcodeStateCallbacks cb) {
+ public Expr get(long offset, int size, Reason reason, PcodeStateCallbacks cb) {
// TODO: Handle overlaps / offcut gets and sets
Expr expr = map.get(offset);
if (expr == null) {
byte[] aOffset =
piece.getAddressArithmetic().fromConst(offset, space.getPointerSize());
- if (cb.readUninitialized(piece, space, aOffset, size) != 0) {
+ if (cb.readUninitialized(piece, space, aOffset, size, reason) != 0) {
return map.get(offset);
}
}
@@ -402,7 +402,7 @@ public class ModelingScript extends GhidraScript {
@Override
protected Expr getFromSpace(ExprSpace space, long offset, int size, Reason reason,
PcodeStateCallbacks cb) {
- return space.get(offset, size, cb);
+ return space.get(offset, size, reason, cb);
}
@Override
diff --git a/GhidraDocs/GhidraClass/ExerciseFiles/Emulation/Source/README.txt b/GhidraDocs/GhidraClass/ExerciseFiles/Emulation/Source/README.txt
index 0881996c56..7d5579604f 100644
--- a/GhidraDocs/GhidraClass/ExerciseFiles/Emulation/Source/README.txt
+++ b/GhidraDocs/GhidraClass/ExerciseFiles/Emulation/Source/README.txt
@@ -7,4 +7,4 @@ Sample scripts deobExampleX86 and deobHookExampleX86 may be built under Linux.
Once these examples have been compiled they may be imported into a Ghidra project and the
corresponding Ghidra Scripts (EmuX86DeobfuscateExampleScript and EmuX86GccDeobfuscateHookExampleScript)
-used to demonstrate the use of the EmulatorHelper class.
+used to demonstrate the use of the PcodeEmulator class.