From 7b97d1899c376d26ff25c63a6dca7fbb38f711c0 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:10:48 -0400 Subject: [PATCH] GP-3716: Fix context flow in Emulator's decoder --- .../trace/model/time/schedule/PatchStep.java | 3 +- .../trace/BytesTracePcodeEmulatorTest.java | 73 ++++++++-- .../ghidra_scripts/EmuDeskCheckScript.java | 127 +++++++++++------- .../ghidra/pcode/emu/DefaultPcodeThread.java | 15 ++- .../java/ghidra/pcode/emu/PcodeThread.java | 7 + 5 files changed, 160 insertions(+), 65 deletions(-) diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java index f3a7a593e4..24a6a75de9 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java @@ -309,8 +309,7 @@ public class PatchStep implements Step { @Override public void execute(PcodeThread emuThread, Stepper stepper, TaskMonitor monitor) throws CancelledException { - PcodeProgram prog = emuThread.getMachine().compileSleigh("schedule", sleigh + ";"); - emuThread.getExecutor().execute(prog, emuThread.getUseropLibrary()); + emuThread.stepPatch(sleigh); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java index 28f91f7ef4..5b1cccdf30 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java @@ -946,28 +946,29 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest } @Test - public void testITECC_VMOVCCF32() throws Throwable { + public void testIT_ContextFlow() throws Throwable { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "ARM:LE:32:v8T")) { TraceThread thread = initTrace(tb, """ pc = 0x00400000; sp = 0x00110000; - s1 = 0x12341234; + r0 = 0x12341234; + r1 = 0x43214321; + r7 = 0xbeef; CY = 1; """, List.of( - "ite cc")); - //"vmov.cc.f32 s1,0xbf000000")); - - try (Transaction tx = tb.startTransaction()) { - tb.trace.getMemoryManager() - .putBytes(0, tb.addr(0x00400002), tb.buf(0xfe, 0xee, 0x00, 0x0a)); - } + "it cc", + "mov r0,r7", // Assembler doesn't handle context flow + "mov r1,r7")); BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0); PcodeThread emuThread = emu.newThread(thread.getPath()); emuThread.stepInstruction(); - emuThread.stepPcodeOp(); // decode - assertEquals("vmov.cc.f32 s1,0xbf000000", emuThread.getInstruction().toString()); + emuThread.stepPcodeOp(); // decode second + assertEquals("mov.cc r0,r7", emuThread.getInstruction().toString()); + emuThread.finishInstruction(); + emuThread.stepPcodeOp(); // decode third + assertEquals("mov r1,r7", emuThread.getInstruction().toString()); emuThread.finishInstruction(); try (Transaction tx = tb.startTransaction()) { @@ -976,8 +977,54 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest assertEquals(BigInteger.valueOf(0x00400006), TraceSleighUtils.evaluate("pc", tb.trace, 1, thread, 0)); - assertEquals(BigInteger.valueOf(0x12341234), // Unaffected - TraceSleighUtils.evaluate("s1", tb.trace, 1, thread, 0)); + assertEquals(BigInteger.valueOf(0x12341234), // r0 Unaffected + TraceSleighUtils.evaluate("r0", tb.trace, 1, thread, 0)); + assertEquals(BigInteger.valueOf(0xbeef), // r1 Affected + TraceSleighUtils.evaluate("r1", tb.trace, 1, thread, 0)); + } + } + @Test + public void testITE_ContextFlow() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "ARM:LE:32:v8T")) { + TraceThread thread = initTrace(tb, """ + pc = 0x00400000; + sp = 0x00110000; + r0 = 0x12341234; + r1 = 0x43214321; + r7 = 0xbeef; + CY = 1; + """, + List.of( + "ite cc", + "mov r0,r7", // Assembler doesn't handle context flow + "mov r1,r7", // " + "mov r2,r7")); + + BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0); + PcodeThread emuThread = emu.newThread(thread.getPath()); + emuThread.stepInstruction(); + emuThread.stepPcodeOp(); // decode second + assertEquals("mov.cc r0,r7", emuThread.getInstruction().toString()); + emuThread.finishInstruction(); + emuThread.stepPcodeOp(); // decode third + assertEquals("mov.cs r1,r7", emuThread.getInstruction().toString()); + emuThread.finishInstruction(); + emuThread.stepPcodeOp(); // decode fourth + assertEquals("mov r2,r7", emuThread.getInstruction().toString()); + emuThread.finishInstruction(); + + try (Transaction tx = tb.startTransaction()) { + emu.writeDown(tb.host, 1, 1); + } + + assertEquals(BigInteger.valueOf(0x00400008), + TraceSleighUtils.evaluate("pc", tb.trace, 1, thread, 0)); + assertEquals(BigInteger.valueOf(0x12341234), // r0 Unaffected + TraceSleighUtils.evaluate("r0", tb.trace, 1, thread, 0)); + assertEquals(BigInteger.valueOf(0xbeef), // r1 Affected + TraceSleighUtils.evaluate("r1", tb.trace, 1, thread, 0)); + assertEquals(BigInteger.valueOf(0xbeef), // r2 Affected + TraceSleighUtils.evaluate("r2", tb.trace, 1, thread, 0)); } } } diff --git a/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java b/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java index b590a5bd78..cff92c2e48 100644 --- a/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java +++ b/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java @@ -13,14 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import java.math.BigInteger; import java.util.*; import org.apache.commons.lang3.tuple.Pair; +import ghidra.app.plugin.core.debug.gui.watch.WatchRow; import ghidra.app.plugin.core.debug.service.emulation.BytesDebuggerPcodeEmulator; import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.script.GhidraScript; +import ghidra.app.services.DebuggerWatchesService; import ghidra.app.tablechooser.*; import ghidra.debug.flatapi.FlatDebuggerAPI; import ghidra.docking.settings.*; @@ -28,6 +32,7 @@ import ghidra.pcode.emu.BytesPcodeThread; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.struct.StructuredSleigh; +import ghidra.pcode.utils.Utils; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Function; @@ -40,63 +45,33 @@ import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.util.Msg; +import ghidra.util.NumericUtilities; import ghidra.util.exception.CancelledException; -/** - * NOTE: Testing with bash: set_shellopts - */ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI { public class Injects extends StructuredSleigh { - Var RAX = lang("RAX", type("void *")); - Var RSP = lang("RSP", type("void *")); - protected Injects() { super(currentProgram); } - - public Var POP() { - Var tgt = local("tgt", RSP.cast(type("void **")).deref()); - RSP.set(RSP.addi(8)); - return tgt; - } - - public void RET() { - _goto(POP()); - } - - public void RET(RVal val) { - RAX.set(val); - RET(); - } - - /** - * TODO: A framework for stubbing the functions. This is close, and the system calls stuff - * can get us closer in its handling of calling conventions. We need either to generate - * Sleigh that gets the parameters in place, or if we're going to use the aliasing idea that - * the syscall stuff does, then we need to allow injection of the already-compiled Sleigh - * program. For now, we'll have to declare the parameter-holding register as a language - * variable. - */ - @StructuredUserop - public void strlen(/*@Param(name = "RDI", type = "char *") Var s*/) { - Var s = lang("RDI", type("char *")); - Var t = temp(type("char *")); - _for(t.set(s), t.deref().neq(0), t.inc(), () -> { - }); - RET(t.subi(s)); - } } - public final List watches = List.of( - watch("RAX", type("int")), - watch("RCX", type("int"), - set(FormatSettingsDefinition.DEF, FormatSettingsDefinition.DECIMAL)), - watch("RSP", type("void *"))); - // TODO: Snarf from Watches window? + protected List collectWatches() { + DebuggerWatchesService watchService = + state.getTool().getService(DebuggerWatchesService.class); + List watches = new ArrayList<>(); + for (WatchRow row : watchService.getWatches()) { + DataType type = row.getDataType(); + watches.add(new Watch(row.getExpression(), + type == null ? null : type(type.getName()), row.getSettings())); + } + return watches; + } @Override protected void run() throws Exception { + List watches = collectWatches(); + Trace trace = emulateLaunch(currentProgram, currentAddress); TracePlatform platform = trace.getPlatformManager().getHostPlatform(); long snap = 0; @@ -114,7 +89,12 @@ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI for (Watch w : watches) { PcodeExpression ce = SleighProgramCompiler .compileExpression((SleighLanguage) platform.getLanguage(), w.expression); - tableDialog.addCustomColumn(new CheckRowWatchDisplay(loader, w, compiled.size())); + if (w.type != null) { + tableDialog.addCustomColumn(new CheckRowWatchDisplay(loader, w, compiled.size())); + } + else { + tableDialog.addCustomColumn(new CheckRowRawDisplay(w, compiled.size())); + } compiled.add(ce); } @@ -166,6 +146,13 @@ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI tableDialog.add(createRow()); } + @Override + public void stepPatch(String sleigh) { + super.stepPatch(sleigh); + position = position.patched(thread, language, sleigh); + tableDialog.add(createRow()); + } + public CheckRow createRow() { List> values = new ArrayList<>(); for (PcodeExpression exp : compiled) { @@ -334,6 +321,51 @@ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI } } + public class CheckRowRawDisplay implements TypedDisplay { + private final boolean isBigEndian = currentProgram.getLanguage().isBigEndian(); + private final Watch watch; + private final int index; + + public CheckRowRawDisplay(Watch watch, int index) { + this.watch = watch; + this.index = index; + } + + @Override + public String getColumnName() { + return watch.expression; + } + + @Override + public Class getColumnClass() { + return String.class; + } + + private String getStringValue(CheckRow r) { + Pair p = r.values.get(index); + Address addr = p.getRight() == null ? null : p.getRight().getAddress(); + byte[] bytes = p.getLeft(); + if (addr == null || !addr.getAddressSpace().isMemorySpace()) { + BigInteger asBigInt = + Utils.bytesToBigInteger(bytes, bytes.length, isBigEndian, false); + return "0x" + asBigInt.toString(16); + } + return "{ " + NumericUtilities.convertBytesToString(bytes, " ") + " }"; + } + + @Override + public int compareTyped(CheckRow r1, CheckRow r2) { + String s1 = getStringValue(r1); + String s2 = getStringValue(r2); + return s1.compareTo(s2); + } + + @Override + public String getTypedValue(CheckRow r) { + return getStringValue(r); + } + } + public class CheckRowWatchDisplay implements TypedDisplay { private final boolean isBigEndian = currentProgram.getLanguage().isBigEndian(); private final Watch watch; @@ -349,6 +381,9 @@ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI } private Object getObjectValue(CheckRow r) { + if (type == null) { + return null; + } try { Pair p = r.values.get(index); Address addr = p.getRight() == null ? null : p.getRight().getAddress(); @@ -412,7 +447,7 @@ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI @Override public boolean execute(AddressableRowObject rowObject) { - CheckRow row = (EmuDeskCheckScript.CheckRow) rowObject; + CheckRow row = (CheckRow) rowObject; try { emulate(row.schedule, monitor); } 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 696dd55791..7f4b8dc8f3 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 @@ -444,6 +444,12 @@ public class DefaultPcodeThread implements PcodeThread { } } + @Override + public void stepPatch(String sleigh) { + PcodeProgram prog = getMachine().compileSleigh("patch", sleigh + ";"); + executor.execute(prog, library); + } + /** * Start execution of the instruction or inject at the program counter */ @@ -499,10 +505,11 @@ public class DefaultPcodeThread implements PcodeThread { overrideCounter(counter.addWrap(decoder.getLastLengthWithDelays())); } if (contextreg != Register.NO_CONTEXT) { - RegisterValue flowCtx = - defaultContext.getFlowValue(instruction.getRegisterValue(contextreg)); - RegisterValue commitCtx = getContextAfterCommits(); - overrideContext(flowCtx.combineValues(commitCtx)); + RegisterValue ctx = new RegisterValue(contextreg, BigInteger.ZERO) + .combineValues(defaultContext.getDefaultValue(contextreg, counter)) + .combineValues(defaultContext.getFlowValue(context)) + .combineValues(getContextAfterCommits()); + overrideContext(ctx); } postExecuteInstruction(); frame = null; diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeThread.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeThread.java index 6557c6c1fb..3eda9f3c76 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeThread.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/PcodeThread.java @@ -196,6 +196,13 @@ public interface PcodeThread { */ void skipPcodeOp(); + /** + * Apply a patch to the emulator + * + * @param sleigh a line of sleigh semantic source to execute (excluding the final semicolon) + */ + void stepPatch(String sleigh); + /** * Get the current frame, if present *