diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java index b0c4d1917a..7c9c4dc53a 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java @@ -230,7 +230,7 @@ public enum TraceEmulationIntegration { * @param set the uninitialized portion required * @return the addresses in {@code set} that remain uninitialized * @see PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece, - * AddressSetView) + * AddressSetView, Reason) */ AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSetView set); @@ -246,12 +246,14 @@ public enum TraceEmulationIntegration { * @param space the address space * @param offset the offset at the start of the uninitialized portion * @param length the size in bytes of the uninitialized portion + * @param reason the reason for reading * @return the number of bytes just initialized, typically 0 or {@code length} * @see PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece, - * AddressSpace, Object, int) + * AddressSpace, Object, int, Reason) */ default int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread thread, - PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length) { + PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length, + Reason reason) { return 0; } @@ -542,7 +544,8 @@ public enum TraceEmulationIntegration { */ @Override public int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread thread, - PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length) { + PcodeExecutorStatePiece piece, AddressSpace space, A offset, int length, + Reason reason) { throw new UnsupportedOperationException(); } @@ -783,14 +786,16 @@ public enum TraceEmulationIntegration { @Override public int readUninitialized(PcodeThread thread, - PcodeExecutorStatePiece piece, AddressSpace space, B offset, int length) { + PcodeExecutorStatePiece piece, AddressSpace space, B offset, int length, + Reason reason) { PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess; - return handlerFor(piece).abstractReadUninit(acc, thread, piece, space, offset, length); + return handlerFor(piece).abstractReadUninit(acc, thread, piece, space, offset, length, + reason); } @Override public AddressSetView readUninitialized(PcodeThread thread, - PcodeExecutorStatePiece piece, AddressSetView set) { + PcodeExecutorStatePiece piece, AddressSetView set, Reason reason) { if (set.isEmpty()) { return set; } diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPcodeExecutorStatePiece.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPcodeExecutorStatePiece.java index d85a358e1b..e7806a57b1 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPcodeExecutorStatePiece.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPcodeExecutorStatePiece.java @@ -118,7 +118,7 @@ public class TaintPcodeExecutorStatePiece @Override protected TaintVec getFromSpace(TaintSpace 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/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintSpace.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintSpace.java index 49b6d15f4a..50bc7310cd 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintSpace.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintSpace.java @@ -18,6 +18,7 @@ package ghidra.pcode.emu.taint.state; import java.util.*; import java.util.Map.Entry; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Register; @@ -85,14 +86,15 @@ public class TaintSpace { * * @param offset the offset * @param buf the vector to receive taint sets + * @param reason the reason for reading * @param cb callbacks to receive emulation events */ - public void getInto(long offset, TaintVec buf, PcodeStateCallbacks cb) { + public void getInto(long offset, TaintVec buf, Reason reason, PcodeStateCallbacks cb) { for (int i = 0; i < buf.length; i++) { TaintSet s = taints.get(offset + i); if (s == null) { - if (cb.readUninitialized(piece, PcodeStateCallbacks.rngSet(space, offset + i, 1)) - .isEmpty()) { + if (cb.readUninitialized(piece, PcodeStateCallbacks.rngSet(space, offset + i, 1), + reason).isEmpty()) { s = taints.get(offset + i); } } @@ -107,17 +109,18 @@ public class TaintSpace { * Retrieve the taint sets for the variable at the given offset * *

- * This works the same as {@link #getInto(long, TaintVec, PcodeStateCallbacks)}, but creates a - * new vector of the given size, reads the taint sets, and returns the vector. + * This works the same as {@link #getInto(long, TaintVec, Reason, PcodeStateCallbacks)}, but + * creates a new vector of the given size, reads the taint sets, and returns the vector. * * @param offset the offset * @param size the size of the variable + * @param reason the reason for reading * @param cb callbacks to receive emulation events * @return the taint vector for that variable */ - public TaintVec get(long offset, int size, PcodeStateCallbacks cb) { + public TaintVec get(long offset, int size, Reason reason, PcodeStateCallbacks cb) { TaintVec vec = new TaintVec(size); - getInto(offset, vec, cb); + getInto(offset, vec, reason, cb); return vec; } @@ -153,7 +156,7 @@ public class TaintSpace { } PcodeOp pcodeOp = ops.get(offset); // Needed here to generate the TaintVec TaintVec vec = new TaintVec(MathUtilities.unsignedMin(1024, end - offset), pcodeOp); - getInto(offset, vec, PcodeStateCallbacks.NONE); + getInto(offset, vec, Reason.INSPECT, PcodeStateCallbacks.NONE); return Map.entry(offset, vec); } } diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/AbstractSymZ3OffsetPcodeExecutorStatePiece.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/AbstractSymZ3OffsetPcodeExecutorStatePiece.java index 59d220d9b4..0c4ce5a8ce 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/AbstractSymZ3OffsetPcodeExecutorStatePiece.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/AbstractSymZ3OffsetPcodeExecutorStatePiece.java @@ -109,9 +109,10 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece * @param size the number of bytes to read (the size of the value) * @return the read value */ - protected SymValueZ3 getUnique(SymValueZ3 offset, int size, PcodeStateCallbacks cb) { + protected SymValueZ3 getUnique(SymValueZ3 offset, int size, Reason reason, + PcodeStateCallbacks cb) { S s = getForSpace(uniqueSpace, false); - return getFromSpace(s, offset, size, cb); + return getFromSpace(s, offset, size, reason, cb); } /** @@ -143,10 +144,11 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece * @param space the address space * @param offset the offset within the space * @param size the number of bytes to read (the size of the value) + * @param reason the reason for reading * @param cb callbacks to receive emulation events * @return the read value */ - protected abstract SymValueZ3 getFromSpace(S space, SymValueZ3 offset, int size, + protected abstract SymValueZ3 getFromSpace(S space, SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb); /** @@ -229,7 +231,7 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece } } if (space.isUniqueSpace()) { - return getUnique(offset, size, cb); + return getUnique(offset, size, reason, cb); } S s = getForSpace(space, false); //Msg.info(this, "Now we likely have a space to get from: " + s); @@ -237,7 +239,7 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece return getFromNullSpace(size, cb); } //offset = quantizeOffset(space, offset); - return getFromSpace(s, offset, size, cb); + return getFromSpace(s, offset, size, reason, cb); } @Override diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/SymZ3PcodeExecutorStatePiece.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/SymZ3PcodeExecutorStatePiece.java index 469b899a9c..2316762cba 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/SymZ3PcodeExecutorStatePiece.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/SymZ3PcodeExecutorStatePiece.java @@ -144,9 +144,9 @@ public class SymZ3PcodeExecutorStatePiece * the storage space. */ @Override - protected SymValueZ3 getFromSpace(SymZ3Space space, SymValueZ3 offset, int size, + protected SymValueZ3 getFromSpace(SymZ3Space space, SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) { - return space.get(offset, size, cb); + return space.get(offset, size, reason, cb); } @Override diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3MemorySpace.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3MemorySpace.java index e3f0c6964d..9da1b7f7b0 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3MemorySpace.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3MemorySpace.java @@ -23,6 +23,7 @@ import com.microsoft.z3.Context; import ghidra.pcode.emu.symz3.AbstractSymZ3OffsetPcodeExecutorStatePiece; import ghidra.pcode.emu.symz3.SymZ3MemoryMap; import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Language; @@ -53,9 +54,9 @@ public class SymZ3MemorySpace extends SymZ3Space { } @Override - public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) { + public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) { if (!mmap.hasValueFor(offset, size)) { - cb.readUninitialized(piece, space, offset, size); + cb.readUninitialized(piece, space, offset, size, reason); } return mmap.load(offset, size, true, cb); } diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java index 5125df4dfb..59814327f4 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3PieceHandler.java @@ -84,7 +84,7 @@ public class SymZ3PieceHandler @Override public int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread thread, PcodeExecutorStatePiece piece, AddressSpace space, - SymValueZ3 offset, int length) { + SymValueZ3 offset, int length, Reason reason) { String string = acc.getPropertyAccess(NAME, String.class).get(Address.NO_ADDRESS); if (string == null) { return 0; diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3RegisterSpace.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3RegisterSpace.java index 3428dc3226..ace6c03910 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3RegisterSpace.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3RegisterSpace.java @@ -23,6 +23,7 @@ import com.microsoft.z3.Context; import ghidra.pcode.emu.symz3.AbstractSymZ3OffsetPcodeExecutorStatePiece; import ghidra.pcode.emu.symz3.SymZ3RegisterMap; import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Language; @@ -87,7 +88,7 @@ public class SymZ3RegisterSpace extends SymZ3Space { } @Override - public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) { + public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) { Register r = getRegister(offset, size); if (r == null) { Msg.warn(this, "unable to get register with space: " + space.getSpaceID() + @@ -95,7 +96,7 @@ public class SymZ3RegisterSpace extends SymZ3Space { return null; } if (!rmap.hasValueForRegister(r)) { - cb.readUninitialized(piece, space, offset, size); + cb.readUninitialized(piece, space, offset, size, reason); } SymValueZ3 result = this.rmap.getRegister(r); return result; diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3Space.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3Space.java index 97089c006c..936eb1ed51 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3Space.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3Space.java @@ -22,6 +22,7 @@ import java.util.stream.Stream; import com.microsoft.z3.Context; import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.symz3.model.SymValueZ3; @@ -36,7 +37,8 @@ import ghidra.symz3.model.SymValueZ3; public abstract class SymZ3Space { public abstract void set(SymValueZ3 offset, int size, SymValueZ3 val, PcodeStateCallbacks cb); - public abstract SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb); + public abstract SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, + PcodeStateCallbacks cb); public abstract String printableSummary(); diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3UniqueSpace.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3UniqueSpace.java index 2d14fe05a9..f9ed902833 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3UniqueSpace.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/state/SymZ3UniqueSpace.java @@ -23,6 +23,7 @@ import java.util.stream.Stream; import com.microsoft.z3.*; import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.symz3.model.SymValueZ3; @@ -86,7 +87,7 @@ public class SymZ3UniqueSpace extends SymZ3Space { } @Override - public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) { + public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) { assert offset != null; try (Context ctx = new Context()) { BitVecExpr b = offset.getBitVecExpr(ctx); diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceMemorySpace.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceMemorySpace.java index 8fedf39277..028799b2a0 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceMemorySpace.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceMemorySpace.java @@ -26,6 +26,7 @@ import ghidra.pcode.emu.symz3.SymZ3MemoryMap; import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter; import ghidra.pcode.emu.symz3.state.SymZ3PieceHandler; import ghidra.pcode.emu.symz3.state.SymZ3WriteDownHelper; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess; import ghidra.program.model.address.*; @@ -86,7 +87,7 @@ public class SymZ3TraceMemorySpace extends SymZ3TraceSpace { } @Override - public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) { + public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) { if (mmap.hasValueFor(offset, size)) { return mmap.load(offset, size, true, cb); } diff --git a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceRegisterSpace.java b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceRegisterSpace.java index 693af64f3b..db80d14475 100644 --- a/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceRegisterSpace.java +++ b/Ghidra/Extensions/SymbolicSummaryZ3/src/main/java/ghidra/pcode/emu/symz3/trace/SymZ3TraceRegisterSpace.java @@ -23,6 +23,7 @@ import com.microsoft.z3.Context; import ghidra.pcode.emu.symz3.SymZ3RegisterMap; import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter; import ghidra.pcode.emu.symz3.state.SymZ3WriteDownHelper; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess; import ghidra.program.model.address.AddressSpace; @@ -96,7 +97,7 @@ public class SymZ3TraceRegisterSpace extends SymZ3TraceSpace { } @Override - public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) { + public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) { assert offset != null; Register r = getRegister(offset, size); if (r == null) { diff --git a/Ghidra/Features/Base/ghidra_scripts/EmuX86DeobfuscateExampleScript.java b/Ghidra/Features/Base/ghidra_scripts/EmuX86DeobfuscateExampleScript.java index eef41e2441..eca3356911 100644 --- a/Ghidra/Features/Base/ghidra_scripts/EmuX86DeobfuscateExampleScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/EmuX86DeobfuscateExampleScript.java @@ -25,27 +25,34 @@ // to the function "deobfuscate" so that the various return values can be recorded with a comment // placed just after the call. //@category Examples.Emulation -import ghidra.app.emulator.EmulatorHelper; +import java.util.NoSuchElementException; + import ghidra.app.script.GhidraScript; import ghidra.app.util.opinion.ElfLoader; -import ghidra.pcode.emulate.EmulateExecutionState; +import ghidra.pcode.emu.*; +import ghidra.pcode.exec.InterruptPcodeExecutionException; +import ghidra.pcode.exec.PcodeArithmetic; import ghidra.program.model.address.Address; +import ghidra.program.model.lang.Register; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Instruction; import ghidra.program.model.symbol.*; import ghidra.util.Msg; -import ghidra.util.exception.NotFoundException; public class EmuX86DeobfuscateExampleScript extends GhidraScript { - private static String PROGRAM_NAME = "deobExample"; + private static final String PROGRAM_NAME = "deobExample"; - private EmulatorHelper emuHelper; + private PcodeEmulator emu; + private PcodeThread emuThread; + private PcodeArithmetic arithmetic; // Important breakpoint locations private Address deobfuscateCall; private Address deobfuscateReturn; // Function locations + private Function mainFunction; private Address mainFunctionEntry; // start of emulation address // Address used as final return location @@ -66,18 +73,21 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript { !"x86:LE:64:default".equals(currentProgram.getLanguageID().toString()) || !ElfLoader.ELF_NAME.equals(format)) { - printerr( - "This emulation example script is specifically intended to be executed against the\n" + - PROGRAM_NAME + - " program whose source is contained within the GhidraClass exercise files\n" + - "(see docs/GhidraClass/ExerciseFiles/Emulation/" + PROGRAM_NAME + ".c).\n" + - "This program should be compiled using gcc for x86 64-bit, imported into your project, \n" + - "analyzed and open as the active program before running ths script."); + printerr(""" + This emulation example script is specifically intended to be executed against + the %s program whose source is contained within the GhidraClass exercise files + (see docs/GhidraClass/ExerciseFiles/Emulation/%s.c). This program should be + compiled using gcc for x86 64-bit, imported into your project, analyzed and + open as the active program before running ths script.""" + .formatted(PROGRAM_NAME, PROGRAM_NAME)); return; } // Identify function to be emulated - mainFunctionEntry = getSymbolAddress("main"); + mainFunction = getGlobalFunctions("main").stream() + .findFirst() + .orElseThrow(() -> new NoSuchElementException("main")); + mainFunctionEntry = mainFunction.getEntryPoint(); // Obtain entry instruction in order to establish initial processor context Instruction entryInstr = getInstructionAt(mainFunctionEntry); @@ -100,53 +110,54 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript { // Remove prior pre-comment setPreComment(deobfuscateReturn, null); - // Establish emulation helper - emuHelper = new EmulatorHelper(currentProgram); - try { + // Establish emulator + emu = new PcodeEmulator(currentProgram.getLanguage()); + monitor.addCancelledListener(() -> { + emu.setSuspended(true); + }); + emuThread = emu.newThread(); + arithmetic = emuThread.getArithmetic(); + EmulatorUtilities.loadProgram(emu, currentProgram); - // Initialize stack pointer (not used by this example) - long stackOffset = - (entryInstr.getAddress().getAddressSpace().getMaxAddress().getOffset() >>> 1) - - 0x7fff; - emuHelper.writeRegister(emuHelper.getStackPointerRegister(), stackOffset); + // Initialize program counter, registers from context, and stack pointer + EmulatorUtilities.initializeForFunction(emuThread, mainFunction); + Address stackBase = + EmulatorUtilities.inspectStackPointer(emuThread, currentProgram.getCompilerSpec()); - // Setup breakpoints - emuHelper.setBreakpoint(deobfuscateCall); - emuHelper.setBreakpoint(deobfuscateReturn); + // Setup breakpoints + emu.addBreakpoint(deobfuscateCall, "1:1"); + emu.addBreakpoint(deobfuscateReturn, "1:1"); - // Set controlled return location so we can identify return from emulated function - controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET); - emuHelper.writeStackValue(0, 8, CONTROLLED_RETURN_OFFSET); - emuHelper.setBreakpoint(controlledReturnAddr); + // Set controlled return location so we can identify return from emulated function + controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET); + emuThread.getState() + .setVar(stackBase.add(8), 8, false, arithmetic.fromConst(controlledReturnAddr)); + emu.addBreakpoint(controlledReturnAddr, "1:1"); - Msg.debug(this, "EMU starting at " + mainFunctionEntry); + Msg.debug(this, "EMU starting at " + emuThread.getCounter()); - // Execution loop until return from function or error occurs - while (!monitor.isCancelled()) { - boolean success = - (emuHelper.getEmulateExecutionState() == EmulateExecutionState.BREAKPOINT) - ? emuHelper.run(monitor) - : emuHelper.run(mainFunctionEntry, entryInstr, monitor); - Address executionAddress = emuHelper.getExecutionAddress(); - if (monitor.isCancelled()) { - println("Emulation cancelled"); - return; - } - if (executionAddress.equals(controlledReturnAddr)) { - println("Returned from function"); - return; - } - if (!success) { - String lastError = emuHelper.getLastError(); - printerr("Emulation Error: " + lastError); - return; - } - processBreakpoint(executionAddress); + // Execution loop until return from function or error occurs + while (!monitor.isCancelled()) { + try { + emuThread.run(); } - } - finally { - // cleanup resources and release hold on currentProgram - emuHelper.dispose(); + catch (InterruptPcodeExecutionException e) { + // Hit a breakpoint. Good. + } + catch (Throwable t) { + printerr("Emulation Error: " + t); + return; + } + if (monitor.isCancelled()) { + println("Emulation cancelled"); + return; + } + Address executionAddress = emuThread.getCounter(); + if (executionAddress.equals(controlledReturnAddr)) { + println("Returned from function"); + return; + } + processBreakpoint(executionAddress); } } @@ -156,19 +167,25 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript { /** * Perform processing for the various breakpoints. + * * @param addr current execute address where emulation has been suspended * @throws Exception if an error occurs */ private void processBreakpoint(Address addr) throws Exception { - if (addr.equals(deobfuscateCall)) { - lastDeobfuscateArg0 = emuHelper.readRegister("RDI").longValue(); + Register rdi = currentProgram.getRegister("RDI"); + lastDeobfuscateArg0 = + emuThread.getState().inspectRegisterValue(rdi).getUnsignedValue().longValue(); } - else if (addr.equals(deobfuscateReturn)) { - long deobfuscateReturnValue = emuHelper.readRegister("RAX").longValue(); - String str = "deobfuscate(src=0x" + Long.toHexString(lastDeobfuscateArg0) + ") -> \"" + - emuHelper.readNullTerminatedString(getAddress(deobfuscateReturnValue), 32) + "\""; + Register rax = currentProgram.getRegister("RAX"); + long deobfuscateReturnValue = + emuThread.getState().inspectRegisterValue(rax).getUnsignedValue().longValue(); + String read = EmulatorUtilities.decodeNullTerminatedString(emuThread.getState(), + getAddress(deobfuscateReturnValue)); + String str = """ + deobfuscate(src=0x%x) -> "%s"\ + """.formatted(lastDeobfuscateArg0, read); String comment = getPreComment(deobfuscateReturn); if (comment == null) { comment = ""; @@ -192,14 +209,4 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript { } return null; } - - private Address getSymbolAddress(String symbolName) throws NotFoundException { - Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(currentProgram, symbolName, - err -> Msg.error(this, err)); - if (symbol != null) { - return symbol.getAddress(); - } - throw new NotFoundException("Failed to locate label: " + symbolName); - } - } diff --git a/Ghidra/Features/Base/ghidra_scripts/EmuX86GccDeobfuscateHookExampleScript.java b/Ghidra/Features/Base/ghidra_scripts/EmuX86GccDeobfuscateHookExampleScript.java index dc5339d91a..7c9e2732c2 100644 --- a/Ghidra/Features/Base/ghidra_scripts/EmuX86GccDeobfuscateHookExampleScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/EmuX86GccDeobfuscateHookExampleScript.java @@ -23,23 +23,23 @@ // data. This script hooks the functions "malloc", "free" and "use_string" where the later // simply prints the deobfuscated string passed as an argument. //@category Examples.Emulation -import java.util.HashMap; -import java.util.Map; +import java.util.*; -import ghidra.app.emulator.EmulatorHelper; import ghidra.app.script.GhidraScript; import ghidra.app.util.opinion.ElfLoader; -import ghidra.pcode.emulate.EmulateExecutionState; +import ghidra.pcode.emu.*; +import ghidra.pcode.exec.*; import ghidra.program.model.address.*; import ghidra.program.model.lang.InsufficientBytesException; import ghidra.program.model.listing.Function; +import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.exception.NotFoundException; public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { - private static String PROGRAM_NAME = "deobHookExample"; + private static final String PROGRAM_NAME = "deobHookExample"; // Heap allocation area private static final int MALLOC_REGION_SIZE = 0x1000; @@ -47,7 +47,10 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { // Address used as final return location private static final long CONTROLLED_RETURN_OFFSET = 0; - private EmulatorHelper emuHelper; + private PcodeEmulator emu; + private PcodeThread emuThread; + private PcodeArithmetic arithmetic; + private SimpleMallocMgr mallocMgr; // Important breakpoint locations for hooking behavior not contained with binary (e.g., dynamic library) @@ -57,6 +60,7 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { private Address useStringEntry; // Function locations + private Function mainFunction; private Address mainFunctionEntry; // start of emulation private Address controlledReturnAddr; // end of emulation @@ -69,18 +73,19 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { !"x86:LE:64:default".equals(currentProgram.getLanguageID().toString()) || !ElfLoader.ELF_NAME.equals(format)) { - printerr( - "This emulation example script is specifically intended to be executed against the\n" + - PROGRAM_NAME + - " program whose source is contained within the GhidraClass exercise files\n" + - "(see docs/GhidraClass/ExerciseFiles/Emulation/" + PROGRAM_NAME + ".c).\n" + - "This program should be compiled using gcc for x86 64-bit, imported into your project, \n" + - "analyzed and open as the active program before running ths script."); + printerr(""" + This emulation example script is specifically intended to be executed against + the %s program whose source is contained within the GhidraClass exercise files + (see docs/GhidraClass/ExerciseFiles/Emulation/%s.c). This program should be + compiled using gcc for x86 64-bit, imported into your project, analyzed and + open as the active program before running ths script.""" + .formatted(PROGRAM_NAME, PROGRAM_NAME)); return; } // Identify function be emulated mainFunctionEntry = getSymbolAddress("main"); + mainFunction = currentProgram.getFunctionManager().getFunctionAt(mainFunctionEntry); useStringEntry = getSymbolAddress("use_string"); // Identify important symbol addresses @@ -88,59 +93,71 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { freeEntry = getExternalThunkAddress("free"); strlenEntry = getExternalThunkAddress("strlen"); - // Establish emulation helper - emuHelper = new EmulatorHelper(currentProgram); - try { - // Initialize stack pointer (not used by this example) - long stackOffset = - (mainFunctionEntry.getAddressSpace().getMaxAddress().getOffset() >>> 1) - 0x7fff; - emuHelper.writeRegister(emuHelper.getStackPointerRegister(), stackOffset); - - // Establish simple malloc memory manager with memory region spaced relative to stack pointer - mallocMgr = new SimpleMallocMgr(getAddress(stackOffset - 0x10000), MALLOC_REGION_SIZE); - - // Setup hook breakpoints - emuHelper.setBreakpoint(mallocEntry); - emuHelper.setBreakpoint(freeEntry); - emuHelper.setBreakpoint(strlenEntry); - emuHelper.setBreakpoint(useStringEntry); - - // Set controlled return location so we can identify return from emulated function - controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET); - emuHelper.writeStackValue(0, 8, CONTROLLED_RETURN_OFFSET); - emuHelper.setBreakpoint(controlledReturnAddr); - - // This example directly manipulates the PC register to facilitate hooking - // which must alter the PC during a breakpoint, and optional stepping which does not - // permit an initial address to be specified. - emuHelper.writeRegister(emuHelper.getPCRegister(), mainFunctionEntry.getOffset()); - Msg.debug(this, "EMU starting at " + emuHelper.getExecutionAddress()); - - // Execution loop until return from function or error occurs - while (!monitor.isCancelled()) { - // Use stepping if needed for troubleshooting - although it runs much slower - //boolean success = emuHelper.step(); - boolean success = emuHelper.run(monitor); - Address executionAddress = emuHelper.getExecutionAddress(); - if (monitor.isCancelled()) { - println("Emulation cancelled"); - return; - } - if (executionAddress.equals(controlledReturnAddr)) { - println("Returned from function"); - return; - } - if (!success) { - String lastError = emuHelper.getLastError(); - printerr("Emulation Error: " + lastError); - return; - } - processBreakpoint(executionAddress); + // Establish emulator + emu = new PcodeEmulator(currentProgram.getLanguage()) { + @Override + protected PcodeUseropLibrary createUseropLibrary() { + return super.createUseropLibrary().compose(new DeobfUseropLibrary()); } + }; + monitor.addCancelledListener(() -> { + emu.setSuspended(true); + }); + emuThread = emu.newThread(); + arithmetic = emuThread.getArithmetic(); + EmulatorUtilities.loadProgram(emu, currentProgram); + + // Initialize program counter, registers from context, and stack pointer + // Request more stack space that normal, so we can use the extra for the heap + EmulatorUtilities.initializeForFunction(emuThread, mainFunction, + EmulatorUtilities.DEFAULT_STACK_SIZE + MALLOC_REGION_SIZE); + Address stackBase = + EmulatorUtilities.inspectStackPointer(emuThread, currentProgram.getCompilerSpec()); + + // Establish simple malloc memory manager with memory region spaced relative to stack pointer + mallocMgr = new SimpleMallocMgr( + stackBase.subtract(EmulatorUtilities.DEFAULT_STACK_SIZE + MALLOC_REGION_SIZE), + MALLOC_REGION_SIZE); + + // Setup hook breakpoints + emu.inject(mallocEntry, """ + RAX = __libc_malloc(RDI); + __x86_64_RET(); + """); + emu.inject(freeEntry, """ + __libc_free(RDI); + __x86_64_RET(); + """); + emu.inject(strlenEntry, """ + RAX = __libc_strlen(RDI); + __x86_64_RET(); + """); + emu.inject(useStringEntry, """ + __hook_useString(RDI); + emu_exec_decoded(); + """); + + // Set controlled return location so we can identify return from emulated function + controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET); + emuThread.getState() + .setVar(stackBase.add(8), 8, false, arithmetic.fromConst(controlledReturnAddr)); + emu.addBreakpoint(controlledReturnAddr, "1:1"); + + emuThread.overrideCounter(mainFunctionEntry); + emuThread.overrideContext( + currentProgram.getProgramContext().getDisassemblyContext(mainFunctionEntry)); + Msg.debug(this, "EMU starting at " + emuThread.getCounter()); + + // First call to run should break after final return + try { + emuThread.run(); } - finally { - // cleanup resources and release hold on currentProgram - emuHelper.dispose(); + catch (InterruptPcodeExecutionException e) { + // Hit the breakpoint. Good. + } + catch (Throwable t) { + printerr("Emulation error: " + t); + return; } } @@ -149,61 +166,9 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { } /** - * Perform processing for the various hook points where breakpoints have been set. - * @param addr current execute address where emulation has been suspended - * @throws Exception if an error occurs - */ - private void processBreakpoint(Address addr) throws Exception { - - // malloc hook - if (addr.equals(mallocEntry)) { - int size = emuHelper.readRegister("RDI").intValue(); - Address memAddr = mallocMgr.malloc(size); - emuHelper.writeRegister("RAX", memAddr.getOffset()); - } - - // free hook - else if (addr.equals(freeEntry)) { - Address freeAddr = getAddress(emuHelper.readRegister("RDI").longValue()); - mallocMgr.free(freeAddr); - } - - // strlen hook - else if (addr.equals(strlenEntry)) { - Address ptr = getAddress(emuHelper.readRegister("RDI").longValue()); - int len = 0; - while (emuHelper.readMemoryByte(ptr) != 0) { - ++len; - ptr = ptr.next(); - } - emuHelper.writeRegister("RAX", len); - } - - // use_string hook - print string - else if (addr.equals(useStringEntry)) { - Address stringAddr = getAddress(emuHelper.readRegister("RDI").longValue()); - String str = emuHelper.readNullTerminatedString(stringAddr, 32); - println("use_string: " + str); // output string argument to consoles - } - - // unexpected - else { - if (emuHelper.getEmulateExecutionState() != EmulateExecutionState.BREAKPOINT) { - // assume we are stepping and simply return - return; - } - throw new NotFoundException("Unhandled breakpoint at " + addr); - } - - // force early return - long returnOffset = emuHelper.readStackValue(0, 8, false).longValue(); - - emuHelper.writeRegister(emuHelper.getPCRegister(), returnOffset); - } - - /** - * Get the thunk function corresponding to an external function. Such thunks - * should reside within the EXTERNAL block. (Note: this is specific to the ELF import) + * Get the thunk function corresponding to an external function. Such thunks should reside + * within the EXTERNAL block. (Note: this is specific to the ELF import) + * * @param symbolName external function name * @return address of thunk function which corresponds to an external function * @throws NotFoundException if thunk not found @@ -212,7 +177,7 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { Symbol externalSymbol = currentProgram.getSymbolTable().getExternalSymbol(symbolName); if (externalSymbol != null && externalSymbol.getSymbolType() == SymbolType.FUNCTION) { Function f = (Function) externalSymbol.getObject(); - Address[] thunkAddrs = f.getFunctionThunkAddresses(); + Address[] thunkAddrs = f.getFunctionThunkAddresses(false); if (thunkAddrs.length == 1) { return thunkAddrs[0]; } @@ -222,6 +187,7 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { /** * Get the global namespace symbol address which corresponds to the specified name. + * * @param symbolName global symbol name * @return symbol address * @throws NotFoundException if symbol not found @@ -235,6 +201,66 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { throw new NotFoundException("Failed to locate label: " + symbolName); } + public class DeobfUseropLibrary extends AnnotatedPcodeUseropLibrary { + static final String SRC_X86_RET = """ + RIP = *:8 RSP; + RSP = RSP + 8; + return [RIP]; + """; + PcodeProgram progRet; + + @PcodeUserop(canInline = true) + public void __x86_64_RET(@OpExecutor PcodeExecutor executor, + @OpLibrary PcodeUseropLibrary library) { + if (progRet == null) { + progRet = SleighProgramCompiler.compileUserop(executor.getLanguage(), + "__x86_64_RET", List.of(), SRC_X86_RET, PcodeUseropLibrary.nil(), List.of()); + } + progRet.execute(executor, library); + } + + @PcodeUserop(functional = true, hasSideEffects = true) + public long __libc_malloc(int size) throws InsufficientBytesException { + Address memAddr = mallocMgr.malloc(size); + return memAddr.getOffset(); + } + + @PcodeUserop(functional = true, hasSideEffects = true) + public void __libc_free(long ptr) { + mallocMgr.free(getAddress(ptr)); + } + + static final String SRC_STRLEN = """ + __result = 0; + + if (*:1 (str+__result) == 0 || __result >= maxlen) goto ; + __result = __result + 1; + goto ; + """; + private PcodeProgram progStrlen; + + @PcodeUserop(canInline = true) + public void __libc_strlen(@OpExecutor PcodeExecutor executor, + @OpLibrary PcodeUseropLibrary library, @OpOutput Varnode out, Varnode start) { + Varnode const128 = + new Varnode(executor.getLanguage().getAddressFactory().getConstantAddress(128), 4); + // NOTE: This assumes all calls to __libc_strlen have the same output and input varnodes + if (progStrlen == null) { + progStrlen = SleighProgramCompiler.compileUserop(executor.getLanguage(), + "__libc_strlen", List.of("__result", "str", "maxlen"), + SRC_STRLEN, PcodeUseropLibrary.nil(), List.of(out, start, const128)); + } + progStrlen.execute(executor, library); + } + + @PcodeUserop + public void __hook_useString(@OpState PcodeExecutorState state, long ptr) { + Address addr = state.getLanguage().getDefaultDataSpace().getAddress(ptr); + String str = EmulatorUtilities.decodeNullTerminatedString(state, addr); + println("use_string: " + str); // output string argument to consoles + } + } + /** * SimpleMallocMgr provides a simple malloc memory manager to be used by the * malloc/free hooked implementations. @@ -246,8 +272,9 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript { /** * SimpleMallocMgr constructor. - * @param rangeStart start of the free malloc region (i.e., Heap) which has been - * deemed a safe + * + * @param rangeStart start of the free malloc region (i.e., Heap) which has been deemed a + * safe * @param byteSize * @throws AddressOverflowException */ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/EmulatorTestRunner.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/EmulatorTestRunner.java index ba1abd885a..d7728544f6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/EmulatorTestRunner.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/EmulatorTestRunner.java @@ -4,9 +4,9 @@ * 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. @@ -20,33 +20,34 @@ import java.util.*; import generic.timer.GhidraSwinglessTimer; import generic.timer.TimerCallback; -import ghidra.app.emulator.*; -import ghidra.pcode.emulate.BreakCallBack; -import ghidra.pcode.emulate.EmulateExecutionState; -import ghidra.pcode.error.LowlevelError; -import ghidra.pcode.memstate.MemoryFaultHandler; -import ghidra.pcode.pcoderaw.PcodeOpRaw; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; +import ghidra.pcode.emu.*; +import ghidra.pcode.emu.PcodeMachine.SwiMode; +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.Register; import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; import ghidra.test.processors.support.PCodeTestAbstractControlBlock.FunctionInfo; import ghidra.util.Msg; import ghidra.util.StringUtilities; import ghidra.util.exception.AssertException; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; public class EmulatorTestRunner { private Program program; private PCodeTestGroup testGroup; - private EmulatorHelper emuHelper; - private Emulator emu; + private MyCallbacks emuCallbacks; + private PcodeEmulator emu; + private PcodeThread emuThread; + private PcodeArithmetic emuArithmetic; private ExecutionListener executionListener; private volatile boolean haltedOnTimer = false; @@ -63,36 +64,78 @@ public class EmulatorTestRunner { this.program = program; this.testGroup = testGroup; this.executionListener = executionListener; - emuHelper = new EmulatorHelper(program) { + + this.emuCallbacks = new MyCallbacks(); + this.emu = new PcodeEmulator(program.getLanguage(), emuCallbacks) { + /** + * {@inheritDoc} + *

+ * Overriding this method is not ideal, but the callbacks do not support carte-blanche + * overriding of all state reads and writes, only hooks for uninitialized reads, but all + * writes. Perhaps we should support all reads, too, but only for monitoring/logging + * purposes. Modifying state behavior should probably still require, well, overriding + * the state. + *

+ * We use guilty knowledge of the implementation: We know which method all the calls + * will get funneled through. + */ @Override - protected Emulator newEmulator() { - return new AdaptedEmulator(this); + protected PcodeExecutorState createLocalState(PcodeThread thread) { + PcodeStateCallbacks scb = emuCallbacks.wrapFor(thread); + return new BytesPcodeExecutorState(language, scb) { + @Override + public byte[] getVar(AddressSpace space, long offset, int size, + boolean quantize, Reason reason) { + byte[] result = super.getVar(space, offset, size, quantize, reason); + if (emuCallbacks.logRWEnabled && reason == Reason.EXECUTE_READ) { + Address addr = space.getAddress(offset); + executionListener.logRead(EmulatorTestRunner.this, addr, size, result); + } + return result; + } + + @Override + public void setVar(AddressSpace space, long offset, int size, boolean quantize, + byte[] val) { + if (emuCallbacks.logRWEnabled) { + Address addr = space.getAddress(offset); + executionListener.logWrite(EmulatorTestRunner.this, addr, size, val); + } + super.setVar(space, offset, size, quantize, val); + } + }; } }; - emu = emuHelper.getEmulator(); - emuHelper.setMemoryFaultHandler(new MyMemoryFaultHandler(executionListener)); + loadProgram(); + this.emuThread = emu.newThread(); + this.emuArithmetic = emuThread.getArithmetic(); + } - emuHelper.registerDefaultCallOtherCallback(new BreakCallBack() { - @Override - public boolean pcodeCallback(PcodeOpRaw op) throws LowlevelError { - int userOp = (int) op.getInput(0).getOffset(); - String pcodeOpName = program.getLanguage().getUserDefinedOpName(userOp); - unimplementedSet.add(pcodeOpName); - String outStr = ""; - Varnode output = op.getOutput(); - if (output != null) { - outStr = ", unable to set output " + output.toString(program.getLanguage()); - } - EmulatorTestRunner.this.executionListener.log(testGroup, "Unimplemented pcodeop '" + - pcodeOpName + "' at: " + emu.getExecuteAddress() + outStr); - ++callOtherCount; - return true; + private void loadProgram() { + + byte[] buf = new byte[4096]; + for (MemoryBlock block : program.getMemory().getBlocks()) { + if (!block.isInitialized() || !block.isLoaded() || block.isArtificial() || + block.isOverlay() || block.isMapped()) { + continue; } - }); + for (AddressRange rng : new AddressRangeChunker(block.getAddressRange(), buf.length)) { + try { + int len = block.getBytes(rng.getMinAddress(), buf); + byte[] value = len == buf.length + ? buf + : Arrays.copyOf(buf, len); + emu.getSharedState().setVar(rng.getMinAddress(), len, false, value); + } + catch (MemoryAccessException e) { + Msg.error(this, "Cannot load part of program", e); + } + } + } } public void dispose() { - emuHelper.dispose(); + emuThread = null; emu = null; program = null; executionListener = null; @@ -111,44 +154,33 @@ public class EmulatorTestRunner { return program; } - public EmulatorHelper getEmulatorHelper() { - return emuHelper; + public PcodeThread getEmulatorThread() { + return emuThread; } public void setContextRegister(RegisterValue ctxRegValue) { - emuHelper.setContextRegister(ctxRegValue); + if (ctxRegValue == null) { + emuThread.overrideContextWithDefault(); + } + else { + emuThread.overrideContext(ctxRegValue); + } } public Address getCurrentAddress() { - return emuHelper.getExecutionAddress(); + return emuThread.getCounter(); } public Instruction getCurrentInstruction() { // TODO: Pull instruction from emulator instead of program after // merge with SleighRefactor branch - return program.getListing().getInstructionAt(emu.getExecuteAddress()); - } - - private void flipBytes(byte[] bytes) { - for (int i = 0; i < bytes.length / 2; i++) { - byte b = bytes[i]; - int otherIndex = bytes.length - i - 1; - bytes[i] = bytes[otherIndex]; - bytes[otherIndex] = b; - } + return emuThread.getInstruction(); + //return program.getListing().getInstructionAt(emuThread.getCounter()); } public RegisterValue getRegisterValue(Register reg) { - Register baseReg = reg.getBaseRegister(); - byte[] bytes = emuHelper.readMemory(baseReg.getAddress(), baseReg.getMinimumByteSize()); - if (!reg.isBigEndian()) { - flipBytes(bytes); - } - byte[] maskValue = new byte[2 * bytes.length]; - Arrays.fill(maskValue, (byte) 0xff); - System.arraycopy(bytes, 0, maskValue, bytes.length, bytes.length); - RegisterValue baseValue = new RegisterValue(baseReg, maskValue); - return baseValue.getRegisterValue(reg); + byte[] bytes = emuThread.getState().getVar(reg, Reason.INSPECT); + return emuArithmetic.toRegisterValue(reg, bytes, Purpose.INSPECT); } public String getRegisterValueString(Register reg) { @@ -161,7 +193,7 @@ public class EmulatorTestRunner { if (reg == null) { throw new IllegalArgumentException("Undefined register: " + regName); } - emuHelper.writeRegister(reg, value); + emuThread.getState().setVar(reg, emuArithmetic.fromConst(value, reg.getNumBytes())); } public void setRegister(String regName, BigInteger value) { @@ -169,7 +201,7 @@ public class EmulatorTestRunner { if (reg == null) { throw new IllegalArgumentException("Undefined register: " + regName); } - emuHelper.writeRegister(reg, value); + emuThread.getState().setVar(reg, emuArithmetic.fromConst(value, reg.getNumBytes())); } /** @@ -248,15 +280,55 @@ public class EmulatorTestRunner { return callOtherErrors; } + /** + * Allows the emulator to run until it's interrupted, returning true if the interrupt is caused + * by a breakpoint. + * + * @return true if interrupted by a breakpoint, false otherwise. This may also run indefinitely. + */ + public boolean runToBreakpoint() { + try { + emuThread.run(); + } + catch (InterruptPcodeExecutionException e) { + return true; + } + catch (Throwable t) { + lastError = t.getLocalizedMessage(); + } + return false; + } + + /** + * Step the emulator one instruction, returning true if it's completed without interruption. + * + * @return true if completed, false if interrupted. Though unlikely, this may also run + * indefinitely. + */ + public boolean stepIgnoreBreakpoints() { + SwiMode restore = emu.getSoftwareInterruptMode(); + try { + emu.setSoftwareInterruptMode(SwiMode.IGNORE_ALL); + emuThread.stepInstruction(); + return true; + } + catch (Throwable t) { + lastError = t.getLocalizedMessage(); + return false; + } + finally { + emu.setSoftwareInterruptMode(restore); + } + } + /** * Execute test group without instruction stepping/tracing * - * @param timeLimitMS - * @param monitor - * @return - * @throws CancelledException + * @param timeLimitMS the maximum number of milliseconds to execute before suspending and + * terminating + * @return true if execution reached the "done" breakpoint */ - public boolean execute(int timeLimitMS, TaskMonitor monitor) throws CancelledException { + public boolean execute(int timeLimitMS) { testGroup.clearFailures(); lastError = null; @@ -279,9 +351,9 @@ public class EmulatorTestRunner { executionListener.log(testGroup, " onPass -> " + breakOnPassAddr); executionListener.log(testGroup, " onError -> " + breakOnErrorAddr); - emuHelper.setBreakpoint(breakOnDoneAddr); - emuHelper.setBreakpoint(breakOnPassAddr); - emuHelper.setBreakpoint(breakOnErrorAddr); + emu.addBreakpoint(breakOnDoneAddr, "1:1"); + emu.addBreakpoint(breakOnPassAddr, "1:1"); + emu.addBreakpoint(breakOnErrorAddr, "1:1"); GhidraSwinglessTimer safetyTimer = null; haltedOnTimer = false; @@ -292,7 +364,7 @@ public class EmulatorTestRunner { @Override public synchronized void timerFired() { haltedOnTimer = true; - emuHelper.getEmulator().setHalt(true); + emu.setSuspended(true); } }); safetyTimer.setRepeats(false); @@ -303,20 +375,20 @@ public class EmulatorTestRunner { boolean success; if (atBreakpoint) { - success = emuHelper.run(monitor); + success = runToBreakpoint(); } else { - success = emuHelper.run(alignAddress(testGroup.functionEntryPtr, alignment), - null, monitor); + Address aligned = alignAddress(testGroup.functionEntryPtr, alignment); + EmulatorUtilities.initializeRegisters(emuThread, program, aligned); + success = runToBreakpoint(); } String lastFuncName = getLastFunctionName(testGroup, false); String errFileName = testGroup.mainTestControlBlock.getLastErrorFile(this); int errLineNum = testGroup.mainTestControlBlock.getLastErrorLine(this); - Address executeAddr = emuHelper.getExecutionAddress(); + Address executeAddr = emuThread.getCounter(); if (!success) { - lastError = emuHelper.getLastError(); testGroup.severeTestFailure(lastFuncName, errFileName, errLineNum, program, executionListener); return false; @@ -386,8 +458,7 @@ public class EmulatorTestRunner { Address executeAddr = alignAddress(testGroup.functionEntryPtr, alignment); - emuHelper.writeRegister(program.getLanguage().getProgramCounter(), - executeAddr.getAddressableWordOffset()); + emuThread.overrideCounter(executeAddr); // Enable sprintf use testGroup.mainTestControlBlock.setSprintfEnabled(this, true); @@ -419,22 +490,17 @@ public class EmulatorTestRunner { } } - executionListener.logState(this); - int stepCount = 0; Address lastAddress = null; Address printfCallAddr = null; boolean assertTriggered = false; FunctionInfo currentFunction = null; - MyMemoryAccessFilter memoryFilter = new MyMemoryAccessFilter(); - emu.addMemoryAccessFilter(memoryFilter); + emuCallbacks.logRWEnabled = true; try { while (true) { - if (!emuHelper.step(TaskMonitor.DUMMY)) { - lastError = emuHelper.getLastError(); - + if (!stepIgnoreBreakpoints()) { String lastFuncName = getLastFunctionName(testGroup, true); String errFileName = testGroup.mainTestControlBlock.getLastErrorFile(this); int errLineNum = testGroup.mainTestControlBlock.getLastErrorLine(this); @@ -445,7 +511,7 @@ public class EmulatorTestRunner { return false; } - executeAddr = emuHelper.getExecutionAddress(); + executeAddr = emuThread.getCounter(); List dumpList = dumpPointMap.get(executeAddr); if (dumpList != null) { @@ -483,15 +549,15 @@ public class EmulatorTestRunner { else if (executeAddr.equals(printfAddr)) { // enter printf function printfCallAddr = lastAddress; - memoryFilter.enabled = false; + emuCallbacks.logRWEnabled = false; executionListener.log(testGroup, "printf invocation (log supressed) ..."); } else if (printfCallAddr != null && isPrintfReturn(executeAddr, printfCallAddr)) { // return from printf function printfCallAddr = null; - memoryFilter.enabled = true; + emuCallbacks.logRWEnabled = true; - String str = testGroup.controlBlock.emuReadString(emuHelper, + String str = testGroup.controlBlock.emuReadString(emuThread, testGroup.mainTestControlBlock.getPrintfBufferAddress()); executionListener.log(testGroup, " " + str); } @@ -525,10 +591,6 @@ public class EmulatorTestRunner { return false; } - if (memoryFilter.enabled) { - executionListener.logState(this); - } - lastAddress = executeAddr; } } @@ -537,8 +599,6 @@ public class EmulatorTestRunner { return false; } finally { - memoryFilter.dispose(); - List list = new ArrayList<>(subFunctionMap.values()); if (!list.isEmpty()) { // Show list of sub-functions which were never executed @@ -579,62 +639,84 @@ public class EmulatorTestRunner { return (offset > printfCallAddr.getOffset() && offset <= maxEnd); } - private class MyMemoryAccessFilter extends MemoryAccessFilter { + private class MyCallbacks implements PcodeEmulationCallbacks { + private static final AddressSetView EMPTY = new AddressSet(); - boolean enabled = true; + boolean logRWEnabled = false; @Override - protected void processWrite(AddressSpace spc, long off, int size, byte[] values) { - if (enabled) { - executionListener.logWrite(EmulatorTestRunner.this, spc.getAddress(off), size, - values); + public void beforeStore(PcodeThread thread, PcodeOp op, AddressSpace space, + byte[] offset, int size, byte[] value) { + if (!logRWEnabled) { + return; } + executionListener.logWrite(EmulatorTestRunner.this, + emuArithmetic.toAddress(offset, space, Purpose.INSPECT), size, value); } @Override - protected void processRead(AddressSpace spc, long off, int size, byte[] values) { - if (enabled && - emu.getEmulateExecutionState() != EmulateExecutionState.INSTRUCTION_DECODE) { - executionListener.logRead(EmulatorTestRunner.this, spc.getAddress(off), size, - values); + public void afterLoad(PcodeThread thread, PcodeOp op, AddressSpace space, + byte[] offset, int size, byte[] value) { + if (!logRWEnabled) { + return; } - } - } - - private class MyMemoryFaultHandler implements MemoryFaultHandler { - - private ExecutionListener executionListener; - - public MyMemoryFaultHandler(ExecutionListener executionListener) { - this.executionListener = executionListener; + executionListener.logRead(EmulatorTestRunner.this, + emuArithmetic.toAddress(offset, space, Purpose.INSPECT), size, value); } @Override - public boolean unknownAddress(Address address, boolean write) { - Address pc = emuHelper.getExecutionAddress(); - String access = write ? "written" : "read"; - executionListener.log(testGroup, - "Unknown address " + access + " at " + pc + ": " + address); - return false; - } - - @Override - public boolean uninitializedRead(Address address, int size, byte[] buf, int bufOffset) { - if (emu.getEmulateExecutionState() == EmulateExecutionState.INSTRUCTION_DECODE) { - return false; + public void beforeExecuteInstruction(PcodeThread thread, Instruction instruction, + PcodeProgram program) { + if (!logRWEnabled) { + return; } - Address pc = emuHelper.getExecutionAddress(); - if (!address.isUniqueAddress()) { - Register reg = program.getRegister(address, size); + executionListener.logState(EmulatorTestRunner.this); + } + + @Override + public boolean handleMissingUserop(PcodeThread thread, PcodeOp op, PcodeFrame frame, + String opName, PcodeUseropLibrary library) { + unimplementedSet.add(opName); + Varnode output = op.getOutput(); + String outStr = output == null ? "" + : ", unable to set output " + output.toString(program.getLanguage()); + executionListener.log(testGroup, """ + Unimplemented pcodeop '%s' at: %s%s""".formatted( + opName, thread.getCounter(), outStr)); + ++callOtherCount; + return true; + } + + @Override + public AddressSetView readUninitialized(PcodeThread thread, + PcodeExecutorStatePiece piece, AddressSetView set, Reason reason) { + if (reason == Reason.EXECUTE_DECODE) { + return set; + } + /** + * HACK: Because of limitations in PcodeExecutorState, we can't know what thread is + * accessing a shared state, i.e., memory. We'd need either to require a thread argument + * in all the set/getVar methods (eww), or we need to use the newer JDK stuff for + * passing context vars down (alternative to the now decried ThreadLocal thing). Thus, + * thread may be null. emuThread is being constructed when it first tries to read + * pc,ctx; so it'll be null at that moment. So, we have to try both. + */ + if (thread == null) { + thread = emuThread; + } + Address pc = thread.getCounter(); + for (AddressRange rng : set) { + Register reg = program.getRegister(rng.getMinAddress(), (int) rng.getLength()); if (reg != null) { executionListener.log(testGroup, - "Uninitialized register read at " + pc + ": " + reg); - return true; + "Uninitialized register read at %s: %s".formatted(pc, reg)); + } + else { + executionListener.log(testGroup, "Uninitialized read at %s: %s:%d".formatted(pc, + rng.getMinAddress(), rng.getLength())); } } - executionListener.log(testGroup, - "Uninitialized read at " + pc + ": " + address.toString(true) + ":" + size); - return true; + return EMPTY; } } @@ -643,7 +725,7 @@ public class EmulatorTestRunner { } private abstract class DumpPoint { - final Address breakAddr; + //final Address breakAddr; final int dumpSize; final int elementSize; final DumpFormat elementFormat; @@ -651,7 +733,7 @@ public class EmulatorTestRunner { DumpPoint(Address breakAddr, int dumpSize, int elementSize, DumpFormat elementFormat, String comment) { - this.breakAddr = breakAddr; + //this.breakAddr = breakAddr; this.dumpSize = dumpSize; this.elementSize = elementSize; this.elementFormat = elementFormat; @@ -704,8 +786,7 @@ public class EmulatorTestRunner { Address getDumpAddress() { RegisterValue regVal = getRegisterValue(dumpAddrReg); return dumpAddrSpace.getAddress(regVal.getUnsignedValue().longValue()) - .add( - relativeOffset); + .add(relativeOffset); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ExecutionListener.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ExecutionListener.java index c1c1c84ddb..4309d3522f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ExecutionListener.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ExecutionListener.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * 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. @@ -20,8 +19,6 @@ import ghidra.program.model.address.Address; public interface ExecutionListener extends TestLogger { - public void stepCompleted(EmulatorTestRunner testRunner); - public void logWrite(EmulatorTestRunner testRunner, Address address, int size, byte[] values); public void logRead(EmulatorTestRunner testRunner, Address address, int size, byte[] values); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestAbstractControlBlock.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestAbstractControlBlock.java index 5b6a47b398..4fd4fcc80e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestAbstractControlBlock.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestAbstractControlBlock.java @@ -4,9 +4,9 @@ * 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. @@ -15,11 +15,14 @@ */ package ghidra.test.processors.support; +import java.nio.charset.Charset; import java.util.*; -import ghidra.app.emulator.EmulatorHelper; import ghidra.app.util.PseudoDisassembler; import ghidra.docking.settings.SettingsImpl; +import ghidra.pcode.emu.PcodeThread; +import ghidra.pcode.exec.PcodeArithmetic.Purpose; +import ghidra.pcode.exec.PcodeExecutorState; import ghidra.pcode.memstate.MemoryState; import ghidra.pcode.utils.Utils; import ghidra.program.disassemble.Disassembler; @@ -36,9 +39,9 @@ import ghidra.util.exception.AssertException; import ghidra.util.task.TaskMonitor; /** - * PCodeTestAbstractControlBlock data is models the general capabilities - * of the TestInfo data structure which is used for different puposes as handled - * by extensions of this class. + * PCodeTestAbstractControlBlock data is models the general capabilities of the + * TestInfo data structure which is used for different puposes as handled by extensions of this + * class. */ public abstract class PCodeTestAbstractControlBlock { @@ -59,17 +62,18 @@ public abstract class PCodeTestAbstractControlBlock { private HashMap functionMap = new HashMap<>(); /** - * Construct test control block instance for the specified program. + * Construct test control block instance for the specified program. + * * @param program program containing control block structure * @param infoStructAddr program address where structure resides - * @param infoStruct appropriate Info structure definition which will have array - * of FunctionInfo immediately following. + * @param infoStruct appropriate Info structure definition which will have array of FunctionInfo + * immediately following. */ PCodeTestAbstractControlBlock(Program program, Address infoStructAddr, Structure infoStruct) { this.program = program; this.pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize(); this.infoStructAddr = infoStructAddr; - this.infoProgramStruct = (Structure) infoStruct.clone(program.getDataTypeManager()); + this.infoProgramStruct = infoStruct.clone(program.getDataTypeManager()); codeSpace = program.getAddressFactory().getDefaultAddressSpace(); dataSpace = program.getLanguage().getDefaultDataSpace(); @@ -95,10 +99,10 @@ public abstract class PCodeTestAbstractControlBlock { } /** - * Force an existing reference to refer to the code space. Pointers - * created in the data space refer to the data space by default, this method - * is used to change these pointers in the data space to refer to - * code. + * Force an existing reference to refer to the code space. Pointers created in the data space + * refer to the data space by default, this method is used to change these pointers in the data + * space to refer to code. + * * @param addr location with data space which contains code reference */ void forceCodePointer(Address addr) { @@ -169,8 +173,8 @@ public abstract class PCodeTestAbstractControlBlock { } /** - * Check for a Data pointer at the specified address and return the referenced - * address. + * Check for a Data pointer at the specified address and return the referenced address. + * * @param addr address of stored pointer * @return pointer referenced address or null if no pointer found */ @@ -182,7 +186,8 @@ public abstract class PCodeTestAbstractControlBlock { return (Address) data.getValue(); } - protected Address readCodePointer(MemBuffer buffer, int bufferOffset, boolean updateReference) throws MemoryAccessException { + protected Address readCodePointer(MemBuffer buffer, int bufferOffset, boolean updateReference) + throws MemoryAccessException { Address codePtr = readPointer(buffer, bufferOffset, codeSpace, updateReference); // treat null pointer as special case - just return it @@ -312,8 +317,9 @@ public abstract class PCodeTestAbstractControlBlock { new TerminatedStringDataType(program.getDataTypeManager()); Structure functionInfoStruct = - (Structure) infoProgramStruct.getDataTypeManager().getDataType(CategoryPath.ROOT, - "FunctionInfo"); + (Structure) infoProgramStruct.getDataTypeManager() + .getDataType(CategoryPath.ROOT, + "FunctionInfo"); if (functionInfoStruct == null) { throw new AssertException("FunctionInfo structure not yet resolved"); } @@ -383,46 +389,62 @@ public abstract class PCodeTestAbstractControlBlock { } - protected String emuReadString(EmulatorHelper emu, Address strPtrAddr) { + /** + * Read an ASCII-encoded string, perhaps with wide characters, from the emulator. + *

+ * This is really a hack, but suffices for these tests. The character size is taken from the + * program's data organization. However, even if wide characters are used, this only reads the + * least-significant byte, effectively truncating each to the ASCII-2 range [0-255]. + * + * @param emu the emulator + * @param strPtrAddr the pointer to the string + * @return the string + */ + protected String emuReadString(PcodeThread emu, Address strPtrAddr) { DataOrganization dataOrganization = - emu.getProgram().getDataTypeManager().getDataOrganization(); + program.getDataTypeManager().getDataOrganization(); int charSize = dataOrganization.getCharSize(); - boolean isBigEndian = emu.getProgram().getMemory().isBigEndian(); + boolean isBigEndian = emu.getLanguage().isBigEndian(); - MemoryState memState = emu.getEmulator().getMemState(); - long offset = strPtrAddr.getOffset(); + PcodeExecutorState state = emu.getState(); if (isBigEndian) { - offset += (charSize - 1); + strPtrAddr = strPtrAddr.add(charSize - 1); } - char[] buffer = new char[128]; + MemBuffer memBuf = state.getConcreteBuffer(strPtrAddr, Purpose.INSPECT); + + int offset = 0; int index = 0; + byte[] buffer = new byte[128]; while (index < buffer.length) { - buffer[index] = - (char) (memState.getValue(strPtrAddr.getAddressSpace(), offset, 1) & 0xff); + try { + buffer[index] = memBuf.getByte(offset); + } + catch (MemoryAccessException e) { + throw new AssertionError(e); + } if (buffer[index] == 0) { break; } offset += charSize; ++index; } - return new String(buffer, 0, index); + return new String(buffer, 0, index, Charset.forName("ASCII")); } - protected long emuRead(EmulatorHelper emu, Address addr, int size) { + protected long emuRead(PcodeThread emu, Address addr, int size) { if (size < 1 || size > 8) { throw new IllegalArgumentException("Unsupported EMU read size: " + size); } - MemoryState memState = emu.getEmulator().getMemState(); - return memState.getValue(addr.getAddressSpace(), addr.getOffset(), size); + return emu.getArithmetic() + .toLong(emu.getState().inspectConcrete(addr, size), Purpose.INSPECT); } - protected void emuWrite(EmulatorHelper emu, Address addr, int size, long value) { + protected void emuWrite(PcodeThread emu, Address addr, int size, long value) { if (size < 1 || size > 8) { throw new IllegalArgumentException("Unsupported EMU read size: " + size); } - MemoryState memState = emu.getEmulator().getMemState(); - memState.setValue(addr.getAddressSpace(), addr.getOffset(), size, value); + emu.getState().setVar(addr, size, false, emu.getArithmetic().fromConst(value, size)); } protected Address getMirroredDataAddress(EmulatorTestRunner emuTestRunner, Address addr) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestControlBlock.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestControlBlock.java index b6893fe804..6eaa8f7c55 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestControlBlock.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/PCodeTestControlBlock.java @@ -4,9 +4,9 @@ * 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. @@ -27,9 +27,9 @@ import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.Msg; /** - * PCodeTestControlBlock data is read from each binary test file and - * identified by the MAIN_CONTROL_BLOCK_MAGIC 64-bit character field value at the start of the - * data structure. Only one instance of this should exist within the binary. + * PCodeTestControlBlock data is read from each binary test file and identified by the + * MAIN_CONTROL_BLOCK_MAGIC 64-bit character field value at the start of the data structure. Only + * one instance of this should exist within the binary. */ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { @@ -72,13 +72,14 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { private final PCodeTestResults testResults; /** - * Construct test control block instance for the specified - * program. Create TestInfo structure data within program if requested. + * Construct test control block instance for the specified program. Create TestInfo structure + * data within program if requested. + * * @param program program containing control block structure - * @param restrictedSet the restricted memory area which should be searched - * for control structures + * @param restrictedSet the restricted memory area which should be searched for control + * structures * @param testInfoStructAddr address of Main TestInfo structure - * @param testFile original binary test file + * @param testFile original binary test file * @param cachedProgramPath program path within program file cache * @param applyStruct create structure Data within program if true * @throws InvalidControlBlockException @@ -109,6 +110,7 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { /** * Find Main TestInfo structure within memory and return instance of PCodeTestControlBlock + * * @param program * @param testFile original binary test file * @param restrictedSet a restricted set to be searched for control structures @@ -294,33 +296,36 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { /** * Enable/Diable sprintf use within P-Code test emulation. + * * @param emuTestRunner emulator test runner * @param enable sprintf enablement */ void setSprintfEnabled(EmulatorTestRunner emuTestRunner, boolean enable) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(sprintfEnableOffset)); - emuWrite(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4, enable ? 1 : 0); + emuWrite(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4, enable ? 1 : 0); } /** * Get 'numpass' field value from emulation memory state + * * @param emuTestRunner emulator test runner * @return 'numpass' field value */ int getNumberPassed(EmulatorTestRunner emuTestRunner) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numPassOffset)); - return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4); + return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4); } /** * Set 'numpass' field value within emulation memory state + * * @param emuTestRunner emulator test runner * @param value field value */ void setNumberPassed(EmulatorTestRunner emuTestRunner, int value) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numPassOffset)); - emuWrite(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4, value); + emuWrite(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4, value); } void resultIgnored(String testGroupName, String testName, boolean passed) { @@ -335,6 +340,7 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { /** * Get the number of passed tests which should be ignored + * * @return number of passed tests which should be ignored */ int getNumberPassedIgnored() { @@ -343,6 +349,7 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { /** * Get the number of failed tests which should be ignored + * * @return number of failed tests which should be ignored */ int getNumberFailedIgnored() { @@ -359,62 +366,68 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { /** * Get 'numfail' field value from emulation memory state + * * @param emuTestRunner emulator test runner * @return 'numfail' field value */ int getNumberFailed(EmulatorTestRunner emuTestRunner) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numFailOffset)); - return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4); + return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4); } /** * Set 'numfail' field value within emulation memory state + * * @param emuTestRunner emulator test runner * @param value field value */ void setNumberFailed(EmulatorTestRunner emuTestRunner, int value) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numFailOffset)); - emuWrite(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4, value); + emuWrite(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4, value); } /** * Get 'lastTestPos' field value from emulation memory state + * * @param emuTestRunner emulator test runner * @return 'lastTestPos' field value */ int getLastTestIndex(EmulatorTestRunner emuTestRunner) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(lastTestPosOffset)); - return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4); + return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4); } /** * Get 'lastErrorLine' field value from emulation memory state + * * @param emuTestRunner emulator test runner * @return 'lastErrorLine' field value */ int getLastErrorLine(EmulatorTestRunner emuTestRunner) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(lastErrorLineOffset)); - return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4); + return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4); } /** - * Get 'lastErrorFile' string value from emulation memory state. Must follow string - * pointer contained within lastErrorFile field. + * Get 'lastErrorFile' string value from emulation memory state. Must follow string pointer + * contained within lastErrorFile field. + * * @param emuTestRunner emulator test runner * @return 'lastErrorLine' field value */ String getLastErrorFile(EmulatorTestRunner emuTestRunner) { Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(lastErrorFileOffset)); - long fileNameOffset = emuRead(emuTestRunner.getEmulatorHelper(), addr, pointerSize); + long fileNameOffset = emuRead(emuTestRunner.getEmulatorThread(), addr, pointerSize); addr = addr.getNewAddress(fileNameOffset, true); addr = getMirroredDataAddress(emuTestRunner, addr); - return emuReadString(emuTestRunner.getEmulatorHelper(), addr); + return emuReadString(emuTestRunner.getEmulatorThread(), addr); } /** * Get the name of the last test function to be run + * * @param emuTestRunner * @return last test function name */ @@ -422,10 +435,10 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock { PCodeTestGroup activeGroup) { Address ptrStorageAddr = infoStructAddr.add(lastFuncOffset); Address ptrAddr = getMirroredDataAddress(emuTestRunner, ptrStorageAddr); - long funcNameOffset = emuRead(emuTestRunner.getEmulatorHelper(), ptrAddr, pointerSize); + long funcNameOffset = emuRead(emuTestRunner.getEmulatorThread(), ptrAddr, pointerSize); Address strAddr = ptrAddr.getNewAddress(funcNameOffset, true); strAddr = getMirroredDataAddress(emuTestRunner, strAddr); - String fnName = emuReadString(emuTestRunner.getEmulatorHelper(), strAddr); + String fnName = emuReadString(emuTestRunner.getEmulatorThread(), strAddr); if ("none".equals(fnName)) { if (logger != null) { logger.log(activeGroup, "ERROR last executed function name pointer stored at " + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java index 0936a9b85d..7d1cc493f8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java @@ -4,9 +4,9 @@ * 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. @@ -65,20 +65,21 @@ import utilities.util.FileUtilities; import utility.application.ApplicationLayout; /** - * ProcessorEmulatorTestAdapter provides an abstract JUnit test implementation - * for processor-specific test cases. All test cases which extend this class must have a - * class name which ends with 'EmulatorTest' and starts with the processor designator which - * will be used to identify associated test binaries within either the processor module's - * data/pcodetests/ directory or the Ghidra/Test/TestResources/data/pcodetests/ directory generally - * contained within the binary repository (e.g., ghidra.bin). + * ProcessorEmulatorTestAdapter provides an abstract JUnit test implementation for + * processor-specific test cases. All test cases which extend this class must have a class name + * which ends with 'EmulatorTest' and starts with the processor designator which will be used to + * identify associated test binaries within either the processor module's data/pcodetests/ directory + * or the Ghidra/Test/TestResources/data/pcodetests/ directory generally contained within the binary + * repository (e.g., ghidra.bin). *

* Within the pcodetests directory all files and folders which start with the prefix - * {@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. + * {@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 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.