diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java index aa0baf6bd4..c89219c632 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java @@ -141,7 +141,7 @@ public class DBTraceCodeManager } catch (Exception e) { Msg.error(this, "Bad Instruction Prototype found in DB! Address: " + address + - "b Bytes: " + NumericUtilities.convertBytesToString(bytes)); + "Bytes: " + NumericUtilities.convertBytesToString(bytes)); return new InvalidPrototype(language); } } @@ -237,6 +237,11 @@ public class DBTraceCodeManager } } + protected byte[] valueBytes(RegisterValue rv) { + byte[] bytes = rv.toBytes(); + return Arrays.copyOfRange(bytes, bytes.length / 2, bytes.length); + } + protected int doRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer, ProcessorContextView context) { DBTraceCodePrototypeEntry protoEnt = protoStore.create(); @@ -250,8 +255,8 @@ public class DBTraceCodeManager ctx = null; } else { - BigInteger value = context.getValue(baseCtxReg, true); - ctx = value == null ? null : value.toByteArray(); + RegisterValue value = context.getRegisterValue(baseCtxReg); + ctx = value == null ? null : valueBytes(value); } protoEnt.set(languageManager.getKeyForLanguage(prototype.getLanguage()), bytes, ctx, memBuffer.getAddress(), prototype.isInDelaySlot()); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java index 1d04e7af83..59b105e728 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java @@ -27,6 +27,8 @@ import ghidra.program.model.lang.InstructionError.InstructionErrorType; import ghidra.program.model.listing.*; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.database.DBTraceUtils; +import ghidra.trace.database.context.DBTraceRegisterContextManager; +import ghidra.trace.database.context.DBTraceRegisterContextSpace; import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.Trace.TraceCodeChangeType; @@ -64,9 +66,34 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView this.conflictCodeUnit = conflictCodeUnit; } + protected void doSetContexts(Range lifespan, Address min, Address max, + ProcessorContextView context) { + Language language = space.baseLanguage; + Register contextReg = language.getContextBaseRegister(); + if (contextReg == null) { + return; + } + RegisterValue newValue = context.getRegisterValue(contextReg); + DBTraceRegisterContextManager ctxMgr = space.trace.getRegisterContextManager(); + if (Objects.equals(ctxMgr.getDefaultValue(language, contextReg, min), newValue)) { + DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, false); + if (ctxSpace == null) { + return; + } + ctxSpace.setValue(language, null, lifespan, new AddressRangeImpl(min, max)); + return; + } + DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, true); + // TODO: Do not save non-flowing context beyond??? + ctxSpace.setValue(language, newValue, lifespan, new AddressRangeImpl(min, max)); + } + protected Instruction doCreateInstruction(Range lifespan, Address address, InstructionPrototype prototype, Instruction protoInstr) { try { + doSetContexts(lifespan, address, address.addNoWrap(prototype.getLength() - 1), + protoInstr); + Instruction created = doCreate(lifespan, address, prototype, protoInstr); // copy override settings to replacement instruction if (protoInstr.isFallThroughOverridden()) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java index 9cbfd618d0..fe0330d4f8 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.*; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.*; import org.junit.Test; @@ -32,8 +33,7 @@ import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.emu.PcodeThread; import ghidra.pcode.exec.*; -import ghidra.program.model.lang.Register; -import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.lang.*; import ghidra.program.model.listing.Instruction; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.trace.database.ToyDBTraceBuilder; @@ -775,6 +775,7 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes PcodeThread emuThread = emu.newThread(thread.getPath()); emuThread.overrideContextWithDefault(); emuThread.stepInstruction(); + // No assertions. It should simply not throw an exception. } } @@ -799,6 +800,7 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes PcodeThread emuThread = emu.newThread(thread.getPath()); emuThread.overrideContextWithDefault(); emuThread.stepInstruction(); + // No assertions. It should throw an exception. } } @@ -823,6 +825,7 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes PcodeThread emuThread = emu.newThread(thread.getPath()); emuThread.overrideContextWithDefault(); emuThread.stepInstruction(); + // No assertions. It should simply not throw an exception. } } @@ -847,6 +850,54 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes emuThread.overrideContextWithDefault(); emuThread.stepInstruction(); emuThread.stepInstruction(); + // No assertions. It should simply not throw an exception. + } + } + + @Test + public void testDEC_MOV_compat32() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { + Language lang = tb.trace.getBaseLanguage(); + Register ctxReg = lang.getContextBaseRegister(); + Register opsizeReg = lang.getRegister("opsize"); + Register addrsizeReg = lang.getRegister("addrsize"); + Register longModeReg = lang.getRegister("longMode"); + RegisterValue ctxVal = new RegisterValue(ctxReg) + .assign(opsizeReg, BigInteger.ONE) + .assign(addrsizeReg, BigInteger.ONE) + .assign(longModeReg, BigInteger.ZERO); + try (UndoableTransaction tid = tb.startTransaction()) { + tb.trace.getRegisterContextManager() + .setValue(lang, ctxVal, Range.atLeast(0L), + tb.range(0x00400000, 0x00400002)); + } + TraceThread thread = initTrace(tb, + List.of( + "RIP = 0x00400000;", + "RSP = 0x00110000;", + "RAX = 0xff12345678;"), + List.of( + "DEC EAX", + "MOV ECX,EAX")); + // Assembly sanity check + ByteBuffer buf = ByteBuffer.allocate(3); + tb.trace.getMemoryManager().getBytes(0, tb.addr(0x00400000), buf); + assertArrayEquals(tb.arr(0x48, 0x89, 0xc1), buf.array()); + + TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0); + PcodeThread emuThread = emu.newThread(thread.getPath()); + emuThread.overrideContext(ctxVal); + emuThread.stepInstruction(); + emuThread.stepInstruction(); + + try (UndoableTransaction tid = tb.startTransaction()) { + emu.writeDown(tb.trace, 1, 1, false); + } + + assertEquals(BigInteger.valueOf(0x00400003), + TraceSleighUtils.evaluate("RIP", tb.trace, 1, thread, 0)); + assertEquals(BigInteger.valueOf(0x12345677), + TraceSleighUtils.evaluate("RCX", tb.trace, 1, thread, 0)); } } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java index bc571eb9f9..2d59f46047 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java @@ -215,6 +215,7 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn } } + @Test @TestLanguage(ProgramBuilder._X64) public void test64BitX86DBTrace() throws Exception { try (UndoableTransaction tid = b.startTransaction()) { @@ -233,6 +234,15 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn CodeUnit cu1 = cuManager.getAt(0, b.addr(0x00400000)); assertEquals("MOV RCX,RAX", cu1.toString()); } + + File saved = b.save(); + + // Check that required context is actually saved and restored + try (ToyDBTraceBuilder b = new ToyDBTraceBuilder(saved)) { + DBTraceCodeUnitsMemoryView cuManager = b.trace.getCodeManager().codeUnits(); + CodeUnit cu1 = cuManager.getAt(0, b.addr(0x00400000)); + assertEquals("MOV RCX,RAX", cu1.toString()); + } } @Test @@ -258,6 +268,17 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn CodeUnit cu2 = cuManager.getAt(0, b.addr(0x00400001)); assertEquals("MOV ECX,EAX", cu2.toString()); } + + File saved = b.save(); + + // Check that required context is actually saved and restored + try (ToyDBTraceBuilder b = new ToyDBTraceBuilder(saved)) { + DBTraceCodeUnitsMemoryView cuManager = b.trace.getCodeManager().codeUnits(); + CodeUnit cu1 = cuManager.getAt(0, b.addr(0x00400000)); + assertEquals("DEC EAX", cu1.toString()); + CodeUnit cu2 = cuManager.getAt(0, b.addr(0x00400001)); + assertEquals("MOV ECX,EAX", cu2.toString()); + } } @Test