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 699933a5f6..28f91f7ef4 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 @@ -940,8 +940,44 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest assertEquals(BigInteger.valueOf(0x0010fff8), regs.getValue(x64, 1, tb.reg(x64, "RSP")).getUnsignedValue()); - assertEquals(new BigInteger("efbeed0d00000000",16), // Guest is LE, host is BE + assertEquals(new BigInteger("efbeed0d00000000", 16), // Guest is LE, host is BE TraceSleighUtils.evaluate(changedExpr, tb.trace, 1, thread, 0)); } } + + @Test + public void testITECC_VMOVCCF32() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "ARM:LE:32:v8T")) { + TraceThread thread = initTrace(tb, """ + pc = 0x00400000; + sp = 0x00110000; + s1 = 0x12341234; + 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)); + } + + 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.finishInstruction(); + + try (Transaction tx = tb.startTransaction()) { + emu.writeDown(tb.host, 1, 1); + } + + 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)); + } + } } 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 4557e2e1e8..696dd55791 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 @@ -20,6 +20,8 @@ import java.util.*; import ghidra.app.emulator.Emulator; import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.app.plugin.processors.sleigh.SleighParserContext; +import ghidra.app.util.PseudoInstruction; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeArithmetic.Purpose; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; @@ -27,6 +29,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Instruction; +import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.pcode.PcodeOp; import ghidra.program.util.ProgramContextImpl; import ghidra.util.Msg; @@ -457,6 +460,33 @@ public class DefaultPcodeThread implements PcodeThread { } } + protected RegisterValue getContextAfterCommits() { + PseudoInstruction pins = (PseudoInstruction) instruction; + try { + SleighParserContext parserCtx = (SleighParserContext) pins.getParserContext(); + var procCtx = new DisassemblerContextAdapter() { + RegisterValue ctxVal = new RegisterValue(language.getContextBaseRegister()); + + @Override + public void setFutureRegisterValue(Address address, RegisterValue value) { + if (!value.getRegister().isProcessorContext()) { + return; + } + if (!address.equals(counter)) { + Msg.warn(this, "Context applied somewhere other than the counter."); + return; + } + ctxVal = ctxVal.assign(value.getRegister(), value); + } + }; + parserCtx.applyCommits(procCtx); + return procCtx.ctxVal; + } + catch (MemoryAccessException e) { + throw new AssertionError(e); + } + } + /** * Resolve a finished instruction, advancing the program counter if necessary */ @@ -469,7 +499,10 @@ public class DefaultPcodeThread implements PcodeThread { overrideCounter(counter.addWrap(decoder.getLastLengthWithDelays())); } if (contextreg != Register.NO_CONTEXT) { - overrideContext(defaultContext.getFlowValue(instruction.getRegisterValue(contextreg))); + RegisterValue flowCtx = + defaultContext.getFlowValue(instruction.getRegisterValue(contextreg)); + RegisterValue commitCtx = getContextAfterCommits(); + overrideContext(flowCtx.combineValues(commitCtx)); } postExecuteInstruction(); frame = null; diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DisassemblerContextAdapter.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DisassemblerContextAdapter.java new file mode 100644 index 0000000000..1ff2c7233c --- /dev/null +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/DisassemblerContextAdapter.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.pcode.emu; + +import java.math.BigInteger; +import java.util.List; + +import ghidra.program.model.address.Address; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.ContextChangeException; + +public interface DisassemblerContextAdapter extends DisassemblerContext { + @Override + default Register getBaseContextRegister() { + throw new UnsupportedOperationException(); + } + + @Override + default List getRegisters() { + throw new UnsupportedOperationException(); + } + + @Override + default Register getRegister(String name) { + throw new UnsupportedOperationException(); + } + + @Override + default BigInteger getValue(Register register, boolean signed) { + throw new UnsupportedOperationException(); + } + + @Override + default RegisterValue getRegisterValue(Register register) { + throw new UnsupportedOperationException(); + } + + @Override + default boolean hasValue(Register register) { + throw new UnsupportedOperationException(); + } + + @Override + default void setValue(Register register, BigInteger value) + throws ContextChangeException { + throw new UnsupportedOperationException(); + } + + @Override + default void setRegisterValue(RegisterValue value) throws ContextChangeException { + throw new UnsupportedOperationException(); + } + + @Override + default void clearRegister(Register register) throws ContextChangeException { + throw new UnsupportedOperationException(); + } + + @Override + default void setFutureRegisterValue(Address address, RegisterValue value) { + throw new UnsupportedOperationException(); + } + + @Override + default void setFutureRegisterValue(Address fromAddr, Address toAddr, + RegisterValue value) { + throw new UnsupportedOperationException(); + } +}