diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..7948ba8f4f --- /dev/null +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/stdoplib/TrigPcodeUseropLibraryFactory.java @@ -0,0 +1,47 @@ +/* ### + * 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.stdoplib; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; + +// This treads on GP-4240 a bit. Still, enough to demonstrate the framework can support it. +@UseropLibrary("trig") +public class TrigPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + + @Override + @SuppressWarnings("unchecked") + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return (PcodeUseropLibrary) TrigPcodeUseropLibrary.INSTANCE; + } + + public static class TrigPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + public static final TrigPcodeUseropLibrary INSTANCE = new TrigPcodeUseropLibrary<>(); + + @PcodeUserop(functional = true) + public float sin(float a) { + return (float) Math.sin(a); + } + + // GP-5339: This won't work until we support overloaded userops + @PcodeUserop(functional = true) + public double sin(double a) { + return Math.sin(a); + } + } +} diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java index e1fb1eb52f..e1f09d3b79 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java @@ -20,6 +20,7 @@ import java.util.*; import java.util.stream.Collectors; import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.program.model.lang.GhidraLanguagePropertyKeys; import ghidra.util.Msg; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ExtensionPoint; @@ -33,7 +34,7 @@ import ghidra.util.classfinder.ExtensionPoint; */ public interface PcodeUseropLibraryFactory extends ExtensionPoint { /** The property key for useropLib ids in pspec files */ - public static final String KEY_USEROP_LIBS = "useropLibs"; + public static final String KEY_USEROP_LIBS = GhidraLanguagePropertyKeys.USEROP_LIBS; /** * A required annotation for identifying the library in pspec files diff --git a/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/AbstractEmulationEquivalenceTest.java b/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/AbstractEmulationEquivalenceTest.java new file mode 100644 index 0000000000..62f3b1650b --- /dev/null +++ b/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/AbstractEmulationEquivalenceTest.java @@ -0,0 +1,243 @@ +/* ### + * 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.exec; + +import static org.junit.Assert.assertEquals; + +import java.math.BigInteger; +import java.util.*; + +import org.junit.AssumptionViolatedException; +import org.junit.Before; + +import generic.test.AbstractGenericTest; +import ghidra.app.plugin.assembler.*; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.emu.*; +import ghidra.pcode.emulate.BreakTableCallBack; +import ghidra.pcode.emulate.Emulate; +import ghidra.pcode.exec.PcodeArithmetic.Purpose; +import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.pcode.memstate.*; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.Register; +import ghidra.program.model.pcode.PcodeOp; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; +import ghidra.util.classfinder.ClassSearcher; +import ghidra.util.exception.MultipleCauses; +import ghidra.util.task.ConsoleTaskMonitor; +import ghidra.util.task.TaskMonitor; + +public class AbstractEmulationEquivalenceTest extends AbstractGenericTest { + protected static final TaskMonitor MONITOR = new ConsoleTaskMonitor(); + + protected static class TestMemoryFaultHandler implements MemoryFaultHandler { + @Override + public boolean uninitializedRead(Address address, int size, byte[] buf, int bufOffset) { + return false; + } + + @Override + public boolean unknownAddress(Address address, boolean write) { + return false; + } + } + + protected interface DoAsm { + void accept(AssemblyBuffer buf) throws Exception; + } + + @Before + public void setupSearch() throws Exception { + ClassSearcher.search(MONITOR); + } + + protected long getEntryOffset() { + return 0x00400000; + } + + protected void doTestEquivOld(SleighLanguage language, Map init, + AssemblyBuffer buf, int count, Map expected) throws Exception { + Address entry = buf.getEntry(); + + MemoryState state = new DefaultMemoryState(language); + Emulate emu = new Emulate(language, state, new BreakTableCallBack(language)); + AddressSet regsUnchecked = new AddressSet(); + state.setMemoryBank(new MemoryPageBank(language.getAddressFactory().getRegisterSpace(), + language.isBigEndian(), 0x1000, new TestMemoryFaultHandler()) { + + @Override + public void setChunk(long offset, int size, byte[] val) { + super.setChunk(offset, size, val); + try { + regsUnchecked.add(new AddressRangeImpl(getSpace().getAddress(offset), size)); + } + catch (AddressOverflowException | AddressOutOfBoundsException e) { + throw new AssertionError(e); + } + } + }); + state.setMemoryBank(new MemoryPageBank(language.getDefaultSpace(), + language.isBigEndian(), 0x1000, new TestMemoryFaultHandler()) { + @Override + public void setChunk(long offset, int size, byte[] val) { + /*Msg.info(this, "Old set ram[0x%x]:%d = %s".formatted(offset, size, + NumericUtilities.convertBytesToString(val)));*/ + super.setChunk(offset, size, val); + } + }); + byte[] bytes = buf.getBytes(); + state.setChunk(bytes, language.getDefaultSpace(), entry.getOffset(), bytes.length); + + for (Map.Entry ent : init.entrySet()) { + state.setValue(ent.getKey(), new BigInteger(ent.getValue(), 16)); + } + + emu.setExecuteAddress(entry); + for (int i = 0; i < count; i++) { + emu.executeInstruction(false, MONITOR); + } + + for (Map.Entry ent : expected.entrySet()) { + assertEquals("Old register value mismatch on " + ent.getKey(), ent.getValue(), + state.getBigInteger(ent.getKey()).toString(16)); + Register reg = language.getRegister(ent.getKey()); + if (!reg.getAddressSpace().isRegisterSpace()) { + continue; + } + regsUnchecked.delete(new AddressRangeImpl(reg.getAddress(), reg.getNumBytes())); + } + + if (!regsUnchecked.isEmpty()) { + Set regs = new TreeSet<>(); + nextAddr: while (!regsUnchecked.isEmpty()) { + Address min = regsUnchecked.getMinAddress(); + List found = Arrays.asList(language.getRegisters(min)); + found.sort((r1, r2) -> r1.getBitLength() - r2.getBitLength()); + for (Register r : found) { + AddressRange rng = new AddressRangeImpl(r.getAddress(), r.getNumBytes()); + if (!regsUnchecked.contains(rng.getMinAddress(), rng.getMaxAddress())) { + continue; + } + regs.add(r.getName()); + regsUnchecked.delete(rng); + continue nextAddr; + } + regsUnchecked.delete(min, min); + } + if (!regs.isEmpty()) { + throw new AssertionError("Some written registers were not asserted: " + regs); + } + } + } + + protected void doTestEquivNew(SleighLanguage language, Map init, + AssemblyBuffer buf, int count, Map expected) throws Exception { + Address entry = buf.getEntry(); + + @SuppressWarnings("unused") // uncomment arg to PcodeEmulator to enable + PcodeEmulationCallbacks cb = new PcodeEmulationCallbacks<>() { + public void dataWritten(PcodeThread thread, + PcodeExecutorStatePiece piece, Address address, int length, U value) { + if (!address.isMemoryAddress()) { + return; + } + if (!(value instanceof byte[] v)) { + return; + } + Msg.info(this, "New set ram[0x%x]:%d = %s".formatted(address.getOffset(), length, + NumericUtilities.convertBytesToString(v))); + }; + }; + PcodeEmulator emu = new PcodeEmulator(language/*, cb*/) { + @Override + protected BytesPcodeThread createThread(String name) { + /** + * TODO: There's a branch somewhere that will make this not work. + */ + return new BytesPcodeThread(name, this) { + @Override + protected boolean onMissingUseropDef(PcodeOp op, String opName) { + return false; + } + }; + } + }; + byte[] bytes = buf.getBytes(); + emu.getSharedState().setVar(entry, bytes.length, false, bytes); + + PcodeThread thread = emu.newThread(); + PcodeExecutorState state = thread.getState(); + PcodeArithmetic arithmetic = thread.getArithmetic(); + + for (Map.Entry ent : init.entrySet()) { + Register reg = language.getRegister(ent.getKey()); + state.setVar(reg, + arithmetic.fromConst(new BigInteger(ent.getValue(), 16), reg.getNumBytes())); + } + + thread.setCounter(entry); + thread.overrideContextWithDefault(); + thread.stepInstruction(count); + + for (Map.Entry ent : expected.entrySet()) { + Register reg = language.getRegister(ent.getKey()); + assertEquals("New register value mismatch on " + ent.getKey(), ent.getValue(), + arithmetic.toBigInteger(state.getVar(reg, Reason.INSPECT), Purpose.INSPECT) + .toString(16)); + } + } + + protected void doTestEquiv(SleighLanguage language, Map init, DoAsm doAsm, + int count, Map expected) throws Exception { + //Msg.info(this, "Language: " + language.getLanguageID()); + Assembler asm = Assemblers.getAssembler(language); + Address entry = language.getDefaultSpace().getAddress(getEntryOffset()); + AssemblyBuffer buf = new AssemblyBuffer(asm, entry); + + doAsm.accept(buf); + + AssertionError oldFailure = null; + AssertionError newFailure = null; + try { + doTestEquivOld(language, init, buf, count, expected); + } + catch (AssertionError e) { + oldFailure = e; + Msg.error("Old failed: " + e, e); + } + try { + doTestEquivNew(language, init, buf, count, expected); + } + catch (AssertionError e) { + newFailure = e; + Msg.error("New failed: " + e, e); + } + + if (newFailure != null && oldFailure != null) { + throw new AssertionError("Both old and new failed", + new MultipleCauses(List.of(oldFailure, newFailure))); + } + if (newFailure != null) { + throw newFailure; + } + if (oldFailure != null) { + throw new AssumptionViolatedException( + "Old failed, but new passed: " + oldFailure.getMessage(), oldFailure); + } + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java index e9e4a039de..43fde46050 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.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. @@ -89,6 +89,16 @@ public final class GhidraLanguagePropertyKeys { public static final String EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS = "emulateInstructionStateModifierClass"; + /** + * USEROP_LIBS is a string property that indicates keys for matching one or more + * {@code PcodeUseropLibaryFactory}s, which will be composed to form the library of custom + * userop implementations available to an emulator. The default is empty. NOTE: This is + * not the name of a class, but a comma-separated list of IDs. Each factory is also + * tagged with a list of IDs. If any ID matches, then that factory is given a chance to + * contribute userops. + */ + public static final String USEROP_LIBS = "useropLibs"; + /** * PCODE_INJECT_LIBRARY_CLASS indicates the classname of a PcodeInjectLibrary implementation * that is used to generate p-code injection payloads which can replace either CALLs or CALLOTHERs diff --git a/Ghidra/Processors/68000/data/languages/68000.pspec b/Ghidra/Processors/68000/data/languages/68000.pspec index 14df2cbd64..6bb45ee40d 100644 --- a/Ghidra/Processors/68000/data/languages/68000.pspec +++ b/Ghidra/Processors/68000/data/languages/68000.pspec @@ -3,6 +3,7 @@ + diff --git a/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java b/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..a89fef3963 --- /dev/null +++ b/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/M68kPcodeUseropLibraryFactory.java @@ -0,0 +1,29 @@ +/* ### + * 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.program.emulation; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; + +@UseropLibrary("m68k") +public class M68kPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return PcodeUseropLibrary.nil(); + } +} diff --git a/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/m68kEmulateInstructionStateModifier.java b/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/m68kEmulateInstructionStateModifier.java index 7732b5bb4b..3e6ec2657d 100644 --- a/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/m68kEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/68000/src/main/java/ghidra/program/emulation/m68kEmulateInstructionStateModifier.java @@ -22,6 +22,7 @@ import ghidra.pcode.memstate.MemoryState; import ghidra.pcodeCPort.error.LowlevelError; import ghidra.program.model.pcode.Varnode; +@Deprecated(forRemoval = true, since = "12.1") public class m68kEmulateInstructionStateModifier extends EmulateInstructionStateModifier { /* diff --git a/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64EmulateInstructionStateModifier.java b/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64EmulateInstructionStateModifier.java index 361a4bbf97..258087a550 100644 --- a/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64EmulateInstructionStateModifier.java +++ b/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64EmulateInstructionStateModifier.java @@ -25,6 +25,7 @@ import ghidra.pcode.memstate.MemoryState; import ghidra.program.model.pcode.Varnode; //import ghidra.pcode.emulate.callother.SignalingNaNOpBehavior; +@Deprecated(forRemoval = true, since = "12.1") public class AARCH64EmulateInstructionStateModifier extends EmulateInstructionStateModifier { public AARCH64EmulateInstructionStateModifier(Emulate emu) { diff --git a/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64PcodeUseropLibraryFactory.java b/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java similarity index 84% rename from Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64PcodeUseropLibraryFactory.java rename to Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java index 76d3d35c2d..67876a556a 100644 --- a/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/AARCH64PcodeUseropLibraryFactory.java +++ b/Ghidra/Processors/AARCH64/src/main/java/ghidra/program/emulation/Aarch64PcodeUseropLibraryFactory.java @@ -27,25 +27,25 @@ import ghidra.program.model.pcode.Varnode; * https://developer.arm.com/documentation/ddi0602/2024-12/SIMD-FP-Instructions/TBL--Table-vector-lookup- */ @UseropLibrary("aarch64") -public class AARCH64PcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { +public class Aarch64PcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { @Override public PcodeUseropLibrary create(SleighLanguage language, PcodeArithmetic arithmetic) { - return new AARCH64PcodeUseropLibrary<>(language); + return new Aarch64PcodeUseropLibrary<>(language); } - public static class AARCH64PcodeUseropLibrary extends DefaultPcodeUseropLibrary { - public AARCH64PcodeUseropLibrary(SleighLanguage language) { + public static class Aarch64PcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + public Aarch64PcodeUseropLibrary(SleighLanguage language) { SleighPcodeUseropDefinition.Factory factory = new SleighPcodeUseropDefinition.Factory(language); putOp(factory.define("MP_INT_ABS").params("n").body(args -> """ if (n >= 0) goto ; __op_output = -n; - goto ; - : + goto ; + __op_output = n; - : + """).build()); putOp(factory.define("SIMD_PIECE").params("simdBytes", "offset").body(args -> """ @@ -74,7 +74,7 @@ public class AARCH64PcodeUseropLibraryFactory implements PcodeUseropLibraryFacto return "local table:16 = %s;\n".formatted(regs[0]); } int size = 16; // Table is always made up of 16-byte (128-bit) regs - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append("local table:%d;\n".formatted(size * regs.length)); for (int i = 0; i < regs.length; i++) { buf.append("table[%d,%d] = %s;\n".formatted(8 * size * i, 8 * size, regs[i])); @@ -84,7 +84,7 @@ public class AARCH64PcodeUseropLibraryFactory implements PcodeUseropLibraryFacto protected String genIndex(int size, int regCount) { int tableSize = 16 * regCount; - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append("local indicies:%d = m;\n".formatted(size)); buf.append("local result:%d = init;\n".formatted(size)); for (int i = 0; i < size; i++) { @@ -103,5 +103,19 @@ public class AARCH64PcodeUseropLibraryFactory implements PcodeUseropLibraryFacto buf.append("__op_output = result;"); return buf.toString(); } + + @PcodeUserop(functional = true) + public void CallSecureMonitor(int imm16) { + } + + @PcodeUserop(functional = true) + public int ExclusiveMonitorPass(long addr, int rsize) { + return 0; + } + + @PcodeUserop(functional = true) + public byte ExclusiveMonitorsStatus() { + return 0; + } } } diff --git a/Ghidra/Processors/AARCH64/src/test.slow/java/ghidra/program/emulation/AARCH64PcodeLibraryTest.java b/Ghidra/Processors/AARCH64/src/test.slow/java/ghidra/program/emulation/Aarch64PcodeLibraryTest.java similarity index 99% rename from Ghidra/Processors/AARCH64/src/test.slow/java/ghidra/program/emulation/AARCH64PcodeLibraryTest.java rename to Ghidra/Processors/AARCH64/src/test.slow/java/ghidra/program/emulation/Aarch64PcodeLibraryTest.java index 92472ce2c7..b0fc43adc7 100644 --- a/Ghidra/Processors/AARCH64/src/test.slow/java/ghidra/program/emulation/AARCH64PcodeLibraryTest.java +++ b/Ghidra/Processors/AARCH64/src/test.slow/java/ghidra/program/emulation/Aarch64PcodeLibraryTest.java @@ -45,7 +45,7 @@ import ghidra.util.exception.MultipleCauses; import ghidra.util.task.ConsoleTaskMonitor; import ghidra.util.task.TaskMonitor; -public class AARCH64PcodeLibraryTest extends AbstractGhidraHeadlessIntegrationTest { +public class Aarch64PcodeLibraryTest extends AbstractGhidraHeadlessIntegrationTest { static final LanguageID LANG_ID_AARCH64 = new LanguageID("AARCH64:LE:64:v8A"); private SleighLanguage aarch64; diff --git a/Ghidra/Processors/ARM/data/languages/ARM.cspec b/Ghidra/Processors/ARM/data/languages/ARM.cspec index c116a64d37..4eb46f5817 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM.cspec +++ b/Ghidra/Processors/ARM/data/languages/ARM.cspec @@ -293,19 +293,20 @@ - - - - - - - - - - + + + + + + + + + + + @@ -356,8 +356,7 @@ offset = *:1 (tmpptr + r0); lr = lr + 2 * zext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -374,8 +373,7 @@ offset = *:1 (tmpptr + r0); lr = lr + 2 * sext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -393,8 +391,7 @@ offset = *:2 (tmpptr + index); lr = lr + 2 * sext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -412,8 +409,7 @@ offset = *:2 (tmpptr + index); lr = lr + 2 * zext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -432,8 +428,7 @@ offset = offset * 4; lr = lr + offset; - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> diff --git a/Ghidra/Processors/ARM/data/languages/ARM.sinc b/Ghidra/Processors/ARM/data/languages/ARM.sinc index 9bdd823410..c299b22ac6 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM.sinc +++ b/Ghidra/Processors/ARM/data/languages/ARM.sinc @@ -15,7 +15,6 @@ define register offset=0x0084 size=4 [ r14_svc r13_svc spsr_svc ]; define register offset=0x0090 size=8 [ mult_dat8 ]; # Special internal register for dealing with multiple stores/loads define register offset=0x0090 size=16 [ mult_dat16 ]; # Special internal register for dealing with multiple stores/loads define register offset=0x00A0 size=4 [ fpsr ]; # floating point state register (for FPA10 floating-point accelerator) -define register offset=0x0078 size=1 [ ISAModeSwitch ]; # generic name for TB ThumbBit - set same as TB @define FPSCR_N "fpscr[31,1]" @define FPSCR_Z "fpscr[30,1]" @@ -193,7 +192,7 @@ define pcodeop ReverseBitOrder; define pcodeop SendEvent; define pcodeop setEndianState; -# Copies ISAModeSwitch to TMode +# Copies TB to TMode define pcodeop setISAMode; macro affectflags() { @@ -204,14 +203,9 @@ macro affect_resflags() { ZR = tmpZR; NG = tmpNG; } -macro SetISAModeSwitch(value) { - ISAModeSwitch = value; - TB = ISAModeSwitch; -} - macro SetThumbMode(value) { - SetISAModeSwitch(value); - setISAMode(); + TB = value; + setISAMode(TB); } # diff --git a/Ghidra/Processors/ARM/data/languages/ARMCortex.pspec b/Ghidra/Processors/ARM/data/languages/ARMCortex.pspec index e6bf29b3e2..3c0205a119 100644 --- a/Ghidra/Processors/ARM/data/languages/ARMCortex.pspec +++ b/Ghidra/Processors/ARM/data/languages/ARMCortex.pspec @@ -7,6 +7,7 @@ + @@ -18,7 +19,11 @@ - + + + + + diff --git a/Ghidra/Processors/ARM/data/languages/ARM_apcs.cspec b/Ghidra/Processors/ARM/data/languages/ARM_apcs.cspec index 406734fa88..3f21f3346b 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM_apcs.cspec +++ b/Ghidra/Processors/ARM/data/languages/ARM_apcs.cspec @@ -162,6 +162,7 @@ + @@ -212,8 +212,7 @@ offset = *:1 (tmpptr + r0); lr = lr + 2 * zext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -230,8 +229,7 @@ offset = *:1 (tmpptr + r0); lr = lr + 2 * sext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -249,8 +247,7 @@ offset = *:2 (tmpptr + index); lr = lr + 2 * sext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -268,8 +265,7 @@ offset = *:2 (tmpptr + index); lr = lr + 2 * zext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -288,8 +284,7 @@ offset = offset * 4; lr = lr + offset; - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> diff --git a/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec b/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec index a6b7a3bda9..e53aac6bc5 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec +++ b/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec @@ -82,6 +82,7 @@ + @@ -131,8 +131,7 @@ offset = *:1 (tmpptr + r0); lr = lr + 2 * zext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -149,8 +148,7 @@ offset = *:1 (tmpptr + r0); lr = lr + 2 * sext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -168,8 +166,7 @@ offset = *:2 (tmpptr + index); lr = lr + 2 * sext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -187,8 +184,7 @@ offset = *:2 (tmpptr + index); lr = lr + 2 * zext(offset); - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> @@ -207,8 +203,7 @@ offset = offset * 4; lr = lr + offset; - ISAModeSwitch = (lr & 1) != 0; - TB = ISAModeSwitch; + TB = (lr & 1) != 0; pc = lr & 0xfffffffe; goto [pc]; ]]> diff --git a/Ghidra/Processors/ARM/data/languages/ARM_v45.pspec b/Ghidra/Processors/ARM/data/languages/ARM_v45.pspec index b7cd95249f..d28bdb6aac 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM_v45.pspec +++ b/Ghidra/Processors/ARM/data/languages/ARM_v45.pspec @@ -8,6 +8,7 @@ + @@ -18,7 +19,11 @@ - + + + + + diff --git a/Ghidra/Processors/ARM/data/languages/ARM_win.cspec b/Ghidra/Processors/ARM/data/languages/ARM_win.cspec index c9e0c331df..117cbe2bb2 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM_win.cspec +++ b/Ghidra/Processors/ARM/data/languages/ARM_win.cspec @@ -228,6 +228,7 @@ + + @@ -42,6 +43,8 @@ + + diff --git a/Ghidra/Processors/ARM/data/languages/ARMtTHUMB.pspec b/Ghidra/Processors/ARM/data/languages/ARMtTHUMB.pspec index 58b125c908..c390c826ff 100644 --- a/Ghidra/Processors/ARM/data/languages/ARMtTHUMB.pspec +++ b/Ghidra/Processors/ARM/data/languages/ARMtTHUMB.pspec @@ -8,6 +8,7 @@ + @@ -43,6 +44,8 @@ + + diff --git a/Ghidra/Processors/ARM/data/languages/ARMt_v45.pspec b/Ghidra/Processors/ARM/data/languages/ARMt_v45.pspec index a8fe2ee893..26caefddd6 100644 --- a/Ghidra/Processors/ARM/data/languages/ARMt_v45.pspec +++ b/Ghidra/Processors/ARM/data/languages/ARMt_v45.pspec @@ -8,6 +8,7 @@ + @@ -19,7 +20,11 @@ - + + + + + diff --git a/Ghidra/Processors/ARM/data/languages/ARMt_v6.pspec b/Ghidra/Processors/ARM/data/languages/ARMt_v6.pspec index a10848d14c..bb89c424c0 100644 --- a/Ghidra/Processors/ARM/data/languages/ARMt_v6.pspec +++ b/Ghidra/Processors/ARM/data/languages/ARMt_v6.pspec @@ -7,6 +7,7 @@ + @@ -20,7 +21,11 @@ - + + + + + diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ARMEmulateInstructionStateModifier.java b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ARMEmulateInstructionStateModifier.java index 4ebb1e150a..2e02ea49a2 100644 --- a/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ARMEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ARMEmulateInstructionStateModifier.java @@ -26,6 +26,7 @@ import ghidra.program.model.lang.Register; import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.pcode.Varnode; +@Deprecated(forRemoval = true, since = "12.1") public class ARMEmulateInstructionStateModifier extends EmulateInstructionStateModifier { private Register TModeReg; diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmCpuState.java b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmCpuState.java new file mode 100644 index 0000000000..f09b1ed6a2 --- /dev/null +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmCpuState.java @@ -0,0 +1,83 @@ +/* ### + * 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.program.emulation; + +public class ArmCpuState { + private volatile boolean irqEnabled = true; + private volatile boolean privileged = true; + private volatile long mainStackPointer; + private volatile long processStackPointer; + private volatile boolean threadModePrivileged; + private volatile boolean threadMode; + + private volatile long basePriority; + + public boolean isIrqEnabled() { + return irqEnabled; + } + + public void setIrqEnabled(boolean irqEnabled) { + this.irqEnabled = irqEnabled; + } + + public boolean isPrivileged() { + return privileged; + } + + public void setPrivileged(boolean privileged) { + this.privileged = privileged; + } + + public long getMainStackPointer() { + return mainStackPointer; + } + + public void setMainStackPointer(long mainStackPointer) { + this.mainStackPointer = mainStackPointer; + } + + public long getProcessStackPointer() { + return processStackPointer; + } + + public void setProcessStackPointer(long processStackPointer) { + this.processStackPointer = processStackPointer; + } + + public boolean isThreadModePrivileged() { + return threadModePrivileged; + } + + public void setThreadModePrivileged(boolean threadModePrivileged) { + this.threadModePrivileged = threadModePrivileged; + } + + public boolean isThreadMode() { + return threadMode; + } + + public void setThreadMode(boolean threadMode) { + this.threadMode = threadMode; + } + + public long getBasePriority() { + return basePriority; + } + + public void setBasePriority(long basePriority) { + this.basePriority = basePriority; + } +} diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..6a23cd78f1 --- /dev/null +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/program/emulation/ArmPcodeUseropLibraryFactory.java @@ -0,0 +1,117 @@ +/* ### + * 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.program.emulation; + +import java.math.BigInteger; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; +import ghidra.program.model.lang.*; +import ghidra.util.Msg; + +@UseropLibrary("arm") +public class ArmPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return new ArmPcodeUseropLibrary<>(language); + } + + public static class ArmPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + private final RegisterValue tMode; + private final RegisterValue aMode; + + // LATER: This should probably be injected + private final ArmCpuState cpuState = new ArmCpuState(); + + public ArmPcodeUseropLibrary(Language language) { + Register tModeReg = language.getRegister("TMode"); + if (tModeReg != null) { + tMode = new RegisterValue(tModeReg, BigInteger.ONE); + aMode = new RegisterValue(tModeReg, BigInteger.ZERO); + } + else { + tMode = null; + aMode = null; + } + } + + @PcodeUserop(modifiesContext = true) + public void setISAMode(@OpExecutor PcodeExecutor exec, boolean tb) { + if (!(exec instanceof PcodeThreadExecutor tExec)) { + return; + } + tExec.getThread().overrideContext(tb ? tMode : aMode); + } + + @PcodeUserop(functional = true) + public void disableIRQinterrupts() { + cpuState.setIrqEnabled(false); + } + + @PcodeUserop(functional = true) + public void enableIRQinterrupts() { + cpuState.setIrqEnabled(true); + } + + @PcodeUserop(functional = true, hasSideEffects = false) + public boolean isCurrentModePrivileged() { + return cpuState.isPrivileged(); + } + + @PcodeUserop(functional = true) + public void setMainStackPointer(long mainStackPointer) { + cpuState.setMainStackPointer(mainStackPointer); + } + + @PcodeUserop(functional = true) + public void setProcessStackPointer(long processStackPointer) { + cpuState.setProcessStackPointer(processStackPointer); + } + + @PcodeUserop(functional = true, hasSideEffects = false) + public long getProcessStackPointer() { + return cpuState.getProcessStackPointer(); + } + + @PcodeUserop(functional = true/*Maybe not*/) + public void DataSynchronizationBarrier(int todo) { + Msg.warn(this, "TODO:DataSyncBarrier"); + } + + @PcodeUserop(functional = true/*Maybe not*/) + public void InstructionSynchronizationBarrier(int todo) { + Msg.warn(this, "TODO:InsSyncBarrier"); + } + + @PcodeUserop(functional = true) + public void setThreadModePrivileged(boolean privileged) { + cpuState.setThreadModePrivileged(privileged); + } + + @PcodeUserop(functional = true, hasSideEffects = false) + public boolean isThreadMode() { + return cpuState.isThreadMode(); + } + + @PcodeUserop(functional = true) + public void setBasePriority(long basePriority) { + cpuState.setBasePriority(basePriority); + } + } +} diff --git a/Ghidra/Processors/MIPS/data/languages/mips.sinc b/Ghidra/Processors/MIPS/data/languages/mips.sinc index 20d687da48..18c86524bb 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips.sinc +++ b/Ghidra/Processors/MIPS/data/languages/mips.sinc @@ -919,9 +919,12 @@ DXuPos: pos is lsb [ pos = lsb + 32; ] { tmp:1 DinsXSize: mysize is msbd & lsb [ mysize = msbd - lsb + 1 + 32; ] { tmp:1 = mysize; export tmp; } @endif +define pcodeop setISAMode; + macro JXWritePC(addr) { @ifdef ISA_VARIANT ISAModeSwitch = (addr & 0x1) != 0; + setISAMode(ISAModeSwitch); tmp:$(REGSIZE) = -2; tmp = tmp & addr; pc = tmp; diff --git a/Ghidra/Processors/MIPS/data/languages/mips16.sinc b/Ghidra/Processors/MIPS/data/languages/mips16.sinc index c3673bc6a7..51f4fe6989 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips16.sinc +++ b/Ghidra/Processors/MIPS/data/languages/mips16.sinc @@ -639,9 +639,10 @@ SAVE_TOP: SAVE_ARG^EXT_FRAME^SAVE_RA^SAVE_SREG^SAVE_STAT is EXT_FRAME & SAVE_RA :jalx Abs26_m16 is ISA_MODE=1 & RELP=1 & ext_isjal=1 & ext_tgt_x=1 & Abs26_m16 [ ext_delay=0b10; ISA_MODE = 0; globalset(Abs26_m16, ISA_MODE); globalset(inst_next, ext_delay); ] { - ra = inst_next | 0x1; + ra = inst_next | 0x1; delayslot( 1 ); - ISAModeSwitch = 0; + ISAModeSwitch = 0; + setISAMode(ISAModeSwitch); call Abs26_m16; } diff --git a/Ghidra/Processors/MIPS/data/languages/mips32.pspec b/Ghidra/Processors/MIPS/data/languages/mips32.pspec index f4cb53e220..3bcfe9ea45 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32.pspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32Instructions.sinc b/Ghidra/Processors/MIPS/data/languages/mips32Instructions.sinc index c66bad986d..4e8a72f2b0 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32Instructions.sinc +++ b/Ghidra/Processors/MIPS/data/languages/mips32Instructions.sinc @@ -1018,6 +1018,7 @@ define pcodeop SYNC; ra = inst_next; delayslot( 1 ); ISAModeSwitch = 1; + setISAMode(ISAModeSwitch); call Abs26; } @endif diff --git a/Ghidra/Processors/MIPS/data/languages/mips32R6.pspec b/Ghidra/Processors/MIPS/data/languages/mips32R6.pspec index 6bcce3f470..66177d029b 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32R6.pspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32R6.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32_eabi.cspec b/Ghidra/Processors/MIPS/data/languages/mips32_eabi.cspec index 235b535150..7868b883a2 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32_eabi.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32_eabi.cspec @@ -154,5 +154,15 @@ - + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec b/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec index 2e061b2005..eb73508e8b 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec @@ -84,5 +84,15 @@ - + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32be.cspec b/Ghidra/Processors/MIPS/data/languages/mips32be.cspec index bd621b777a..3327ed707e 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32be.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32be.cspec @@ -183,6 +183,15 @@ - - + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32le.cspec b/Ghidra/Processors/MIPS/data/languages/mips32le.cspec index 13e5c29940..f1e74a50ac 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32le.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32le.cspec @@ -174,5 +174,15 @@ - + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32micro.pspec b/Ghidra/Processors/MIPS/data/languages/mips32micro.pspec index 2db2de7b7d..22ed608ce5 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32micro.pspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32micro.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64.pspec b/Ghidra/Processors/MIPS/data/languages/mips64.pspec index 0dca002904..739c58f776 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64.pspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64R6.pspec b/Ghidra/Processors/MIPS/data/languages/mips64R6.pspec index 53695d33b7..f7b5f5582c 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64R6.pspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64R6.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec b/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec index 255cb362e1..15252a8e93 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec @@ -133,4 +133,15 @@ + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec b/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec index 1b01bf46e2..35e48441fa 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec @@ -125,4 +125,15 @@ + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec b/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec index bbb7a4594f..15c9a4d67b 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec @@ -95,4 +95,15 @@ + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64be.cspec b/Ghidra/Processors/MIPS/data/languages/mips64be.cspec index dbcf456ca9..f0fdd2c383 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64be.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64be.cspec @@ -186,4 +186,15 @@ + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64le.cspec b/Ghidra/Processors/MIPS/data/languages/mips64le.cspec index 6cebc64913..0c5842d79b 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64le.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64le.cspec @@ -170,4 +170,15 @@ + + + + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64micro.pspec b/Ghidra/Processors/MIPS/data/languages/mips64micro.pspec index 7819d36ff4..e03bb43f9d 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64micro.pspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64micro.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/MIPS/data/languages/mipsmicro.sinc b/Ghidra/Processors/MIPS/data/languages/mipsmicro.sinc index 5ccf2c3447..24561f0674 100644 --- a/Ghidra/Processors/MIPS/data/languages/mipsmicro.sinc +++ b/Ghidra/Processors/MIPS/data/languages/mipsmicro.sinc @@ -2369,7 +2369,8 @@ STORE_TOP16: STORE_SREG^ra,EXT_CODE4E(sp) is mic_listr6 & REL6=1 & STORE_SREG & :jalx Abs26_mic2 is ISA_MODE=1 & RELP=0 & mic_op=0b111100 & REL6=0 & mic_code ; Abs26_mic2 [ ext_32_code = mic_code; ISA_MODE = 0; globalset(Abs26_mic2, ISA_MODE);] { ra = inst_next | 0x1; delayslot( 1 ); - ISAModeSwitch = 0; + ISAModeSwitch = 0; + setISAMode(ISAModeSwitch); call Abs26_mic2; } diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MIPSEmulateInstructionStateModifier.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MIPSEmulateInstructionStateModifier.java index 374838074c..c13c127439 100644 --- a/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MIPSEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MIPSEmulateInstructionStateModifier.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. @@ -26,6 +26,7 @@ import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.pcode.PcodeOp; //import ghidra.pcode.emulate.callother.SignalingNaNOpBehavior; +@Deprecated(forRemoval = true, since = "12.1") public class MIPSEmulateInstructionStateModifier extends EmulateInstructionStateModifier { private Register ismReg; diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..b4736866dd --- /dev/null +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/program/emulation/MipsPcodeUseropLibraryFactory.java @@ -0,0 +1,58 @@ +/* ### + * 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.program.emulation; + +import java.math.BigInteger; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; +import ghidra.program.model.lang.*; + +@UseropLibrary("mips") +public class MipsPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return new MipsPcodeUseropLibrary<>(language); + } + + public static class MipsPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + private final RegisterValue mode0; + private final RegisterValue mode1; + + public MipsPcodeUseropLibrary(Language language) { + Register isaModeReg = language.getRegister("ISA_MODE"); + if (isaModeReg != null) { + mode0 = new RegisterValue(isaModeReg, BigInteger.ZERO); + mode1 = new RegisterValue(isaModeReg, BigInteger.ONE); + } + else { + mode0 = null; + mode1 = null; + } + } + + @PcodeUserop(modifiesContext = true) + public void setISAMode(@OpExecutor PcodeExecutor exec, boolean mode) { + if (!(exec instanceof PcodeThreadExecutor tExec)) { + return; + } + tExec.getThread().overrideContext(mode ? mode1 : mode0); + } + } +} diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_32.pspec b/Ghidra/Processors/PowerPC/data/languages/ppc_32.pspec index 260c1622b1..1dabc260d7 100644 --- a/Ghidra/Processors/PowerPC/data/languages/ppc_32.pspec +++ b/Ghidra/Processors/PowerPC/data/languages/ppc_32.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_32_mpc8270.pspec b/Ghidra/Processors/PowerPC/data/languages/ppc_32_mpc8270.pspec index 6f223ab6d6..ade46bdd21 100644 --- a/Ghidra/Processors/PowerPC/data/languages/ppc_32_mpc8270.pspec +++ b/Ghidra/Processors/PowerPC/data/languages/ppc_32_mpc8270.pspec @@ -5,6 +5,7 @@ + diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_64.pspec b/Ghidra/Processors/PowerPC/data/languages/ppc_64.pspec index d61b9f0b2c..5ddb3cdcc1 100644 --- a/Ghidra/Processors/PowerPC/data/languages/ppc_64.pspec +++ b/Ghidra/Processors/PowerPC/data/languages/ppc_64.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PPCEmulateInstructionStateModifier.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PPCEmulateInstructionStateModifier.java index bd5f37d85c..437301dea8 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PPCEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PPCEmulateInstructionStateModifier.java @@ -24,6 +24,7 @@ import ghidra.pcode.memstate.MemoryState; import ghidra.pcodeCPort.error.LowlevelError; import ghidra.program.model.pcode.Varnode; +@Deprecated(forRemoval = true, since = "12.1") public class PPCEmulateInstructionStateModifier extends EmulateInstructionStateModifier { public PPCEmulateInstructionStateModifier(Emulate emu) { diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..23a038860d --- /dev/null +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/program/emulation/PpcPcodeUseropLibraryFactory.java @@ -0,0 +1,55 @@ +/* ### + * 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.program.emulation; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; + +@UseropLibrary("ppc") +public class PpcPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return new PpcPcodeUseropLibrary<>(language); + } + + public static class PpcPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + public PpcPcodeUseropLibrary(SleighLanguage language) { + SleighPcodeUseropDefinition.Factory factory = + new SleighPcodeUseropDefinition.Factory(language); + + putOp(factory.define("vectorPermute").params("s1", "s2", "p").body(args -> """ + local table:32; + local result:16; + table[128,128] = s1; + table[0,128] = s2; + """ + genIndex() + """ + __op_output = result; + """).build()); + } + + protected String genIndex() { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 16; i++) { + buf.append("idx%d:1 = 0x1f - (p(%d) & 0x1f);\n".formatted(i, i)); + buf.append("tmp%d:1 = table(idx%d);\n".formatted(i, i)); + buf.append("result[%d,8] = tmp%d;\n".formatted(8 * i, i)); + } + return buf.toString(); + } + } +} diff --git a/Ghidra/Processors/PowerPC/src/test.slow/java/ghidra/program/emulation/PpcPcodeUseropLibraryTest.java b/Ghidra/Processors/PowerPC/src/test.slow/java/ghidra/program/emulation/PpcPcodeUseropLibraryTest.java new file mode 100644 index 0000000000..5803b6dd74 --- /dev/null +++ b/Ghidra/Processors/PowerPC/src/test.slow/java/ghidra/program/emulation/PpcPcodeUseropLibraryTest.java @@ -0,0 +1,107 @@ +/* ### + * 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.program.emulation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertNotNull; + +import java.util.Map; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.program.emulation.PpcPcodeUseropLibraryFactory.PpcPcodeUseropLibrary; +import ghidra.program.model.lang.LanguageID; +import ghidra.program.util.DefaultLanguageService; + +public class PpcPcodeUseropLibraryTest extends AbstractEmulationEquivalenceTest { + static final LanguageID LANGI_ID_PCC64 = new LanguageID("PowerPC:BE:64:A2ALT-32addr"); + + static SleighLanguage PPC64; + + @Before + public void setupPpc() throws Exception { + if (PPC64 == null) { + PPC64 = (SleighLanguage) DefaultLanguageService.getLanguageService() + .getLanguage(LANGI_ID_PCC64); + } + } + + @Test + public void testFoundById() { + PcodeUseropLibrary lib = PcodeUseropLibraryFactory + .createUseropLibraryFromId("ppc", PPC64, BytesPcodeArithmetic.forLanguage(PPC64)); + assertThat(lib, Matchers.instanceOf(PpcPcodeUseropLibrary.class)); + } + + @Test + public void testFoundByLang() { + PcodeUseropLibrary lib = PcodeUseropLibraryFactory + .createUseropLibraryForLanguage(PPC64, BytesPcodeArithmetic.forLanguage(PPC64)); + assertNotNull(lib.getUserops().get("vectorPermute")); + } + + @Test + public void testVperm1() throws Exception { + doTestEquiv(PPC64, + Map.ofEntries( + Map.entry("vs44", "ffffffffffffffff"), + Map.entry("vs45", "0123456789abcdef"), + Map.entry("vs33", "fedcba9876543210"), + Map.entry("vs43", "0004080c1014181c")), + buf -> buf.assemble("vperm v12,v13,v1,v11"), 1, + Map.ofEntries( + Map.entry("vs44", "1890000fe76"), + Map.entry("vs45", "123456789abcdef"), + Map.entry("vs33", "fedcba9876543210"), + Map.entry("vs43", "4080c1014181c"))); + } + + @Test + public void testVperm2() throws Exception { + doTestEquiv(PPC64, + Map.ofEntries( + Map.entry("vs44", "ffffffffffffffffffffffffffffffff"), + Map.entry("vs45", "ffffffe0000000000000000100000002"), + Map.entry("vs33", "00000003000000040000000500000006"), + Map.entry("vs43", "08090a0b0c0d0e0f1011121314151617")), + buf -> buf.assemble("vperm v12,v13,v1,v11"), 1, + Map.ofEntries( + Map.entry("vs44", "1000000020000000300000004"), + Map.entry("vs45", "ffffffe0000000000000000100000002"), + Map.entry("vs33", "3000000040000000500000006"), + Map.entry("vs43", "8090a0b0c0d0e0f1011121314151617"))); + } + + @Test + public void testVperm3() throws Exception { + doTestEquiv(PPC64, + Map.ofEntries( + Map.entry("vs44", "ffffffffffffffffffffffffffffffff"), + Map.entry("vs45", "00000001000000020000000300000004"), + Map.entry("vs33", "00000001000000020000000300000004"), + Map.entry("vs43", "101112131415161718191a1b1c1d1e1f")), + buf -> buf.assemble("vperm v12,v13,v1,v11"), 1, + Map.ofEntries( + Map.entry("vs44", "1000000020000000300000004"), + Map.entry("vs45", "1000000020000000300000004"), + Map.entry("vs33", "1000000020000000300000004"), + Map.entry("vs43", "101112131415161718191a1b1c1d1e1f"))); + } +} diff --git a/Ghidra/Processors/RISCV/src/main/java/ghidra/program/emulation/RISCVEmulateInstructionStateModifier.java b/Ghidra/Processors/RISCV/src/main/java/ghidra/program/emulation/RISCVEmulateInstructionStateModifier.java index d3d54529c5..9abf0b257b 100644 --- a/Ghidra/Processors/RISCV/src/main/java/ghidra/program/emulation/RISCVEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/RISCV/src/main/java/ghidra/program/emulation/RISCVEmulateInstructionStateModifier.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. @@ -22,6 +22,7 @@ import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.pcode.PcodeOp; import ghidra.pcode.emulate.Emulate; +@Deprecated(forRemoval = true, since = "12.1") public class RISCVEmulateInstructionStateModifier extends EmulateInstructionStateModifier { public RISCVEmulateInstructionStateModifier(Emulate emu) { diff --git a/Ghidra/Processors/Xtensa/data/languages/xtensa.pspec b/Ghidra/Processors/Xtensa/data/languages/xtensa.pspec index 721b2e437a..089d24b5d3 100644 --- a/Ghidra/Processors/Xtensa/data/languages/xtensa.pspec +++ b/Ghidra/Processors/Xtensa/data/languages/xtensa.pspec @@ -5,6 +5,7 @@ + diff --git a/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaEmulateInstructionStateModifier.java b/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaEmulateInstructionStateModifier.java index 9679d0dce3..671a012aa3 100644 --- a/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaEmulateInstructionStateModifier.java @@ -27,6 +27,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.lang.Register; import ghidra.program.model.pcode.Varnode; +@Deprecated(forRemoval = true, since = "12.1") public class XtensaEmulateInstructionStateModifier extends EmulateInstructionStateModifier { private Stack stashStack = new Stack(); diff --git a/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java b/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..11e6a63953 --- /dev/null +++ b/Ghidra/Processors/Xtensa/src/main/java/ghidra/program/emulation/XtensaPcodeUseropLibraryFactory.java @@ -0,0 +1,196 @@ +/* ### + * 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.program.emulation; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.error.LowlevelError; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; + +@UseropLibrary("xtensa") +public class XtensaPcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return new XtensaPcodeUseropLibrary<>(language); + } + + public static class IntStack { + public static final int INIT_SIZE = 16; + private int[] vals = new int[INIT_SIZE]; + private int size = 0; + + public void push(int value) { + if (size == vals.length) { + grow(); + } + vals[size] = value; + size++; + } + + public int pop() { + if (size == 0) { + throw new LowlevelError("stack is empty"); + } + size--; + return vals[size]; + } + + private void grow() { + int[] vals = new int[this.vals.length * 2]; + System.arraycopy(this.vals, 0, vals, 0, this.vals.length); + this.vals = vals; + } + } + + public static class XtensaPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + private final static int TYPE_COUNT = 0; + private final static int TYPE_VALUE = 1; + + private final IntStack stack = new IntStack(); + + public XtensaPcodeUseropLibrary(SleighLanguage language) { + SleighPcodeUseropDefinition.Factory factory = + new SleighPcodeUseropDefinition.Factory(language); + + putOp(factory.define("rotateRegWindow").params("callinc").body(args -> """ + __pushCount(callinc); + if (callinc == 0) goto ; + if (callinc == 1) goto ; + if (callinc == 2) goto ; + if (callinc == 3) goto ; + __lle_invalidCallinc(callinc); + + """ + genPushN(4) + """ + goto ; + + """ + genPushN(8) + """ + goto ; + + """ + genPushN(12) + """ + + """).build()); + + putOp(factory.define("restoreRegWindow").body(args -> """ + callinc:4 = (a0 >> 30) & 0x3; + __popCount(callinc); + if (callinc == 0) goto ; + if (callinc == 1) goto ; + if (callinc == 2) goto ; + if (callinc == 3) goto ; + + """ + genPopN(4) + """ + goto ; + + """ + genPopN(8) + """ + goto ; + + """ + genPopN(12) + """ + + """).build()); + } + + protected String genPushN(int n) { + StringBuilder buf = new StringBuilder(); + int i = 0; + for (; i < n; i++) { + buf.append(" __pushValue(a%d);\n".formatted(i)); + } + for (; i < 16; i++) { + buf.append(" a%d = a%d;\n".formatted(i - n, i)); + } + return buf.toString(); + } + + protected String genPopN(int n) { + StringBuilder buf = new StringBuilder(); + int i = 15; + for (; i >= n; i--) { + buf.append(" a%d = a%d;\n".formatted(i, i - n)); + } + for (; i >= 0; i--) { + buf.append(" a%d = __popValue();\n".formatted(i)); + } + return buf.toString(); + } + + @PcodeUserop(functional = true, hasSideEffects = false) + public void __lle_invalidCallinc(int callinc) { + throw new LowlevelError( + "rotateRegWindow: invalid value for CALLINC (0x%x)".formatted(callinc)); + } + + @PcodeUserop(functional = true) + public void __pushCount(int count) { + stack.push(TYPE_COUNT); + stack.push(count); + } + + @PcodeUserop(functional = true) + public void __popCount(int count) { + int type = stack.pop(); + int value = stack.pop(); + if (type != TYPE_COUNT) { + throw new LowlevelError("Popped count, but got a value"); + } + if (value != count) { + throw new LowlevelError(""" + restoreRegWindow: return address CALLINC (%d) does not match last entry \ + CALLINC value (%d)""".formatted(count, value)); + } + } + + @PcodeUserop(functional = true) + public void __pushValue(int value) { + stack.push(TYPE_VALUE); + stack.push(value); + } + + @PcodeUserop(functional = true) + public int __popValue() { + int type = stack.pop(); + int value = stack.pop(); + if (type != TYPE_VALUE) { + throw new LowlevelError("Popped value, but got a count"); + } + return value; + } + + @PcodeUserop(canInline = true) + public void swap4() { + } + + @PcodeUserop(canInline = true) + public void swap8() { + } + + @PcodeUserop(canInline = true) + public void swap12() { + } + + @PcodeUserop(canInline = true) + public void restore4() { + } + + @PcodeUserop(canInline = true) + public void restore8() { + } + + @PcodeUserop(canInline = true) + public void restore12() { + } + } +} diff --git a/Ghidra/Processors/tricore/data/languages/tc172x.pspec b/Ghidra/Processors/tricore/data/languages/tc172x.pspec index 57dc6aeee9..e3350f0037 100644 --- a/Ghidra/Processors/tricore/data/languages/tc172x.pspec +++ b/Ghidra/Processors/tricore/data/languages/tc172x.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/tricore/data/languages/tc176x.pspec b/Ghidra/Processors/tricore/data/languages/tc176x.pspec index e5d96cdc2b..7af25d210b 100644 --- a/Ghidra/Processors/tricore/data/languages/tc176x.pspec +++ b/Ghidra/Processors/tricore/data/languages/tc176x.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/tricore/data/languages/tc29x.pspec b/Ghidra/Processors/tricore/data/languages/tc29x.pspec index 70d760bce5..e0f7b5acd1 100644 --- a/Ghidra/Processors/tricore/data/languages/tc29x.pspec +++ b/Ghidra/Processors/tricore/data/languages/tc29x.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/tricore/data/languages/tricore.pspec b/Ghidra/Processors/tricore/data/languages/tricore.pspec index 835220220d..853d9d9a4c 100644 --- a/Ghidra/Processors/tricore/data/languages/tricore.pspec +++ b/Ghidra/Processors/tricore/data/languages/tricore.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TRICOREEmulateInstructionStateModifier.java b/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TRICOREEmulateInstructionStateModifier.java index 98e7757afb..c15dee8d46 100644 --- a/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TRICOREEmulateInstructionStateModifier.java +++ b/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TRICOREEmulateInstructionStateModifier.java @@ -27,6 +27,7 @@ import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Register; import ghidra.program.model.pcode.Varnode; +@Deprecated(forRemoval = true, since = "12.1") public class TRICOREEmulateInstructionStateModifier extends EmulateInstructionStateModifier { Register FCX,PCXI,LCX,PSW,a10,a11,d8,a12,d12; diff --git a/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java b/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..bc416798d3 --- /dev/null +++ b/Ghidra/Processors/tricore/src/main/java/ghidra/program/emulation/TricorePcodeUseropLibraryFactory.java @@ -0,0 +1,90 @@ +/* ### + * 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.program.emulation; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; + +@UseropLibrary("tricore") +public class TricorePcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return new TricorePcodeUseropLibrary<>(language); + } + + public static class TricorePcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + public TricorePcodeUseropLibrary(SleighLanguage language) { + SleighPcodeUseropDefinition.Factory factory = + new SleighPcodeUseropDefinition.Factory(language); + + putOp(factory.define("saveCallerState") + .params("_fcx", "_lcx", "_pcxi") + .body(args -> """ + local ea:4 = ((FCX & 0xffff0000) << 12) | ((FCX & 0xffff) << 6); + local new_fcx:4 = * ea; + if (new_fcx != 0) goto ; + new_fcx = FCX + 1; + + * ea = PCXI; + * (ea + 4) = PSW; + * (ea + 8) = a10; + * (ea + 12) = a11; + * (ea + 16) = d8; + * (ea + 20) = d9; + * (ea + 24) = d10; + * (ea + 28) = d11; + * (ea + 32) = a12; + * (ea + 36) = a13; + * (ea + 40) = a14; + * (ea + 44) = a15; + * (ea + 48) = d12; + * (ea + 52) = d13; + * (ea + 56) = d14; + * (ea + 60) = d15; + PCXI = (PCXI & 0xfff00000) | ( FCX & 0x000fffff); + FCX = (FCX & 0xfff00000) | (new_fcx & 0x000fffff); + """) + .build()); + putOp(factory.define("restoreCallerState") + .params("_fcx", "_lcx", "_pcxi") + .body(args -> """ + local ea:4 = ((PCXI & 0xffff0000) << 12) | ((PCXI & 0x0000ffff) << 6); + local savePCXI = PCXI; + PCXI = * ea; + PSW = * (ea + 4); + a10 = * (ea + 8); + a11 = * (ea + 12); + d8 = * (ea + 16); + d9 = * (ea + 20); + d10 = * (ea + 24); + d11 = * (ea + 28); + a12 = * (ea + 32); + a13 = * (ea + 36); + a14 = * (ea + 40); + a15 = * (ea + 44); + d12 = * (ea + 48); + d13 = * (ea + 52); + d14 = * (ea + 56); + d15 = * (ea + 60); + * ea = FCX; + FCX = (FCX & 0xfff00000) | (savePCXI & 0x000fffff); + """) + .build()); + } + } +} diff --git a/Ghidra/Processors/tricore/src/test.slow/java/ghidra/program/emulation/TricorePcodeUseropLibraryTest.java b/Ghidra/Processors/tricore/src/test.slow/java/ghidra/program/emulation/TricorePcodeUseropLibraryTest.java new file mode 100644 index 0000000000..1ce5ab17b8 --- /dev/null +++ b/Ghidra/Processors/tricore/src/test.slow/java/ghidra/program/emulation/TricorePcodeUseropLibraryTest.java @@ -0,0 +1,121 @@ +/* ### + * 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.program.emulation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertNotNull; + +import java.util.Map; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.program.emulation.TricorePcodeUseropLibraryFactory.TricorePcodeUseropLibrary; +import ghidra.program.model.lang.LanguageID; +import ghidra.program.util.DefaultLanguageService; + +public class TricorePcodeUseropLibraryTest extends AbstractEmulationEquivalenceTest { + static final LanguageID LANGI_ID_TRICORE = new LanguageID("tricore:LE:32:default"); + + static SleighLanguage TRICORE; + + @Before + public void setupTricore() throws Exception { + if (TRICORE == null) { + TRICORE = (SleighLanguage) DefaultLanguageService.getLanguageService() + .getLanguage(LANGI_ID_TRICORE); + } + } + + @Test + public void testFoundById() { + PcodeUseropLibrary lib = PcodeUseropLibraryFactory + .createUseropLibraryFromId("tricore", TRICORE, + BytesPcodeArithmetic.forLanguage(TRICORE)); + assertThat(lib, Matchers.instanceOf(TricorePcodeUseropLibrary.class)); + } + + @Test + public void testFoundByLang() { + PcodeUseropLibrary lib = PcodeUseropLibraryFactory + .createUseropLibraryForLanguage(TRICORE, BytesPcodeArithmetic.forLanguage(TRICORE)); + assertNotNull(lib.getUserops().get("saveCallerState")); + assertNotNull(lib.getUserops().get("restoreCallerState")); + } + + @Test + public void testCall() throws Exception { + doTestEquiv(TRICORE, + Map.ofEntries( + Map.entry("FCX", "00020000"), + Map.entry("PCXI", "00000000"), + Map.entry("PSW", "00000000")), + buf -> buf.assemble("call 0x00401234"), 1, + Map.ofEntries( + Map.entry("PCXI", "20000"), + Map.entry("FCX", "20001"), + Map.entry("a11", "400004"), + Map.entry("PC", "401234"))); + } + + @Test + public void testCallRet() throws Exception { + doTestEquiv(TRICORE, + Map.ofEntries( + Map.entry("FCX", "00020001"), + Map.entry("PCXI", "00020000"), + Map.entry("a10", "1110"), + Map.entry("a11", "1111"), + Map.entry("a12", "1112"), + Map.entry("a13", "1113"), + Map.entry("a14", "1114"), + Map.entry("a15", "1115"), + Map.entry("d8", "1208"), + Map.entry("d9", "1209"), + Map.entry("d10", "1210"), + Map.entry("d11", "1211"), + Map.entry("d12", "1212"), + Map.entry("d13", "1213"), + Map.entry("d14", "1214"), + Map.entry("d15", "1215")), + buf -> { + buf.assemble("call 0x00400002"); // length is 2 + buf.assemble("ret"); + }, 2, + Map.ofEntries( + Map.entry("PCXI", "20000"), + Map.entry("PSW", "0"), + Map.entry("FCX", "20001"), + Map.entry("PC", "400002"), + Map.entry("a10", "1110"), + Map.entry("a11", "1111"), + Map.entry("a12", "1112"), + Map.entry("a13", "1113"), + Map.entry("a14", "1114"), + Map.entry("a15", "1115"), + Map.entry("d8", "1208"), + Map.entry("d9", "1209"), + Map.entry("d10", "1210"), + Map.entry("d11", "1211"), + Map.entry("d12", "1212"), + Map.entry("d13", "1213"), + Map.entry("d14", "1214"), + Map.entry("d15", "1215"))); + } +} diff --git a/Ghidra/Processors/x86/data/languages/x86-16-real.pspec b/Ghidra/Processors/x86/data/languages/x86-16-real.pspec index ba7ff91207..c8c6483d28 100644 --- a/Ghidra/Processors/x86/data/languages/x86-16-real.pspec +++ b/Ghidra/Processors/x86/data/languages/x86-16-real.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/x86/data/languages/x86-16.pspec b/Ghidra/Processors/x86/data/languages/x86-16.pspec index ae2d69c72d..9ac87bdc3a 100644 --- a/Ghidra/Processors/x86/data/languages/x86-16.pspec +++ b/Ghidra/Processors/x86/data/languages/x86-16.pspec @@ -5,6 +5,7 @@ + diff --git a/Ghidra/Processors/x86/data/languages/x86-64-compat32.pspec b/Ghidra/Processors/x86/data/languages/x86-64-compat32.pspec index 51ba63c8e2..be2b7738a6 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64-compat32.pspec +++ b/Ghidra/Processors/x86/data/languages/x86-64-compat32.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/x86/data/languages/x86-64.pspec b/Ghidra/Processors/x86/data/languages/x86-64.pspec index 45df40cdb8..13cdbb84ae 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64.pspec +++ b/Ghidra/Processors/x86/data/languages/x86-64.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/x86/data/languages/x86.pspec b/Ghidra/Processors/x86/data/languages/x86.pspec index b473a93bec..a577b197aa 100644 --- a/Ghidra/Processors/x86/data/languages/x86.pspec +++ b/Ghidra/Processors/x86/data/languages/x86.pspec @@ -4,6 +4,7 @@ + diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java b/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java new file mode 100644 index 0000000000..4bee10029d --- /dev/null +++ b/Ghidra/Processors/x86/src/main/java/ghidra/program/emulation/X86PcodeUseropLibraryFactory.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.emulation; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.exec.*; +import ghidra.pcode.exec.PcodeUseropLibraryFactory.UseropLibrary; + +@UseropLibrary("x86") +public class X86PcodeUseropLibraryFactory implements PcodeUseropLibraryFactory { + @Override + public PcodeUseropLibrary create(SleighLanguage language, + PcodeArithmetic arithmetic) { + return new X86PcodeUseropLibrary<>(); + } + + public static class X86PcodeUseropLibrary extends AnnotatedPcodeUseropLibrary { + + /** + * LATER: For petri-dish emulation, this is perfect. However, for multi-threaded and + * systems-level stuff, we'll probably need an actual mutex of some sort. We'll also want to + * make sure the behavior is accurate in the face of errors and interrupts. + */ + @PcodeUserop(functional = true) + public void LOCK() { + } + + @PcodeUserop(functional = true) + public void UNLOCK() { + } + } +}