diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/TraceScheduleTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/TraceScheduleTest.java index bd93717fbd..654f00fc11 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/TraceScheduleTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/TraceScheduleTest.java @@ -22,9 +22,7 @@ import java.util.List; import org.junit.Test; -import ghidra.pcode.emu.AbstractPcodeMachine; -import ghidra.pcode.emu.AbstractPcodeMachine.ThreadPcodeExecutorState; -import ghidra.pcode.emu.PcodeThread; +import ghidra.pcode.emu.*; import ghidra.pcode.exec.*; import ghidra.program.model.address.Address; import ghidra.program.model.lang.RegisterValue; diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractModifiedPcodeThread.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractModifiedPcodeThread.java new file mode 100644 index 0000000000..1482f663b1 --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractModifiedPcodeThread.java @@ -0,0 +1,194 @@ +/* ### + * 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.lang.reflect.Constructor; + +import ghidra.app.plugin.processors.sleigh.SleighLanguage; +import ghidra.pcode.emulate.*; +import ghidra.pcode.exec.*; +import ghidra.pcode.memstate.MemoryState; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.*; +import ghidra.program.model.pcode.PcodeOp; +import ghidra.util.Msg; + +/** + * A p-code thread which incorporates per-architecture state modifiers on concrete bytes + */ +public abstract class AbstractModifiedPcodeThread extends DefaultPcodeThread { + + protected class GlueEmulate extends Emulate { + public GlueEmulate(SleighLanguage lang, MemoryState s, BreakTable b) { + super(lang, s, b); + } + + @Override + public Language getLanguage() { + return language; + } + + @Override + public void setExecuteAddress(Address addr) { + overrideCounter(addr); + } + + @Override + public Address getExecuteAddress() { + return getCounter(); + } + + @Override + public void setContextRegisterValue(RegisterValue regValue) { + overrideContext(regValue); + } + + @Override + public RegisterValue getContextRegisterValue() { + return getContext(); + } + } + + protected class GlueMemoryState extends MemoryState { + public GlueMemoryState(Language language) { + super(language); + } + + @Override + public int getChunk(byte[] res, AddressSpace spc, long off, int size, + boolean stopOnUnintialized) { + return getBytesChunk(res, spc, off, size, stopOnUnintialized); + } + + @Override + public void setChunk(byte[] val, AddressSpace spc, long off, int size) { + setBytesChunk(val, spc, off, size); + } + + @Override + public void setInitialized(boolean initialized, AddressSpace spc, long off, int size) { + // Do nothing + } + } + + protected class GluePcodeThreadExecutor extends PcodeThreadExecutor { + public GluePcodeThreadExecutor(Language language, PcodeArithmetic arithmetic, + PcodeExecutorStatePiece state) { + super(language, arithmetic, state); + } + + @Override + public void executeCallother(PcodeOp op, PcodeFrame frame, + SleighUseropLibrary library) { + // Prefer one in the library. Fall-back to state modifier's impl + try { + super.executeCallother(op, frame, library); + } + catch (SleighLinkException e) { + if (modifier == null || !modifier.executeCallOther(op)) { + throw e; + } + } + } + } + + // Part of the glue that makes existing state modifiers work in new emulation framework + protected final EmulateInstructionStateModifier modifier; + protected final Emulate emulate; + + protected Address savedCounter; + + public AbstractModifiedPcodeThread(String name, AbstractPcodeMachine machine) { + super(name, machine); + + /** + * These two exist as a way to integrate the language-specific injects that are already + * written for the established concrete emulator. + */ + emulate = new GlueEmulate(language, new GlueMemoryState(language), + new BreakTableCallBack(language)); + modifier = createModifier(); + } + + /** + * Construct a modifier for the given language + * + * @return the state modifier + */ + protected EmulateInstructionStateModifier createModifier() { + String classname = language + .getProperty(GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS); + if (classname == null) { + return null; + } + try { + Class c = Class.forName(classname); + if (!EmulateInstructionStateModifier.class.isAssignableFrom(c)) { + Msg.error(this, + "Language " + language.getLanguageID() + " does not specify a valid " + + GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS); + throw new RuntimeException(classname + " does not implement interface " + + EmulateInstructionStateModifier.class.getName()); + } + Constructor constructor = c.getConstructor(Emulate.class); + return (EmulateInstructionStateModifier) constructor.newInstance(emulate); + } + catch (Exception e) { + Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " + + GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS); + throw new RuntimeException( + "Failed to instantiate " + classname + " for language " + language.getLanguageID(), + e); + } + } + + @Override + protected PcodeThreadExecutor createExecutor() { + return new GluePcodeThreadExecutor(language, arithmetic, state); + } + + /** + * Called by the legacy state modifier to retrieve concrete bytes from the thread's state + * + * @see {@link MemoryState#getChunk(byte[], AddressSpace, long, int, boolean)} + */ + protected abstract int getBytesChunk(byte[] res, AddressSpace spc, long off, int size, + boolean stopOnUnintialized); + + /** + * Called by the legacy state modifier to set concrete bytes in the thread's state + * + * @see {@link MemoryState#setChunk(byte[], AddressSpace, long, int)} + */ + protected abstract void setBytesChunk(byte[] val, AddressSpace spc, long off, int size); + + @Override + protected void preExecuteInstruction() { + if (modifier != null) { + savedCounter = getCounter(); + modifier.initialExecuteCallback(emulate, savedCounter, getContext()); + } + } + + @Override + protected void postExecuteInstruction() { + if (modifier != null) { + modifier.postExecuteCallback(emulate, savedCounter, frame.copyCode(), + frame.getBranched(), getCounter()); + } + } +} diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeEmulator.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeEmulator.java index 61c485a967..8b19aaf84f 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeEmulator.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeEmulator.java @@ -30,6 +30,6 @@ public abstract class AbstractPcodeEmulator extends AbstractPcodeMachine @Override protected BytesPcodeThread createThread(String name) { - return new BytesPcodeThread(name, this, library); + return new BytesPcodeThread(name, this); } } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java index beb09b53c4..41a2f4424e 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/AbstractPcodeMachine.java @@ -20,71 +20,13 @@ import java.util.*; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.exec.*; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Language; -import ghidra.program.model.mem.MemBuffer; import ghidra.util.classfinder.ClassSearcher; /** * An abstract implementation of {@link PcodeMachine} suitable as a base for most implementations */ public abstract class AbstractPcodeMachine implements PcodeMachine { - public static class ThreadPcodeExecutorState implements PcodeExecutorState { - protected final PcodeExecutorState memoryState; - protected final PcodeExecutorState registerState; - - public ThreadPcodeExecutorState(PcodeExecutorState memoryState, - PcodeExecutorState registerState) { - this.memoryState = memoryState; - this.registerState = registerState; - } - - @Override - public T longToOffset(AddressSpace space, long l) { - if (space.isRegisterSpace()) { - return registerState.longToOffset(space, l); - } - else { - return memoryState.longToOffset(space, l); - } - } - - @Override - public void setVar(AddressSpace space, T offset, int size, boolean truncateAddressableUnit, - T val) { - if (space.isRegisterSpace()) { - registerState.setVar(space, offset, size, truncateAddressableUnit, val); - } - else { - memoryState.setVar(space, offset, size, truncateAddressableUnit, val); - } - } - - @Override - public T getVar(AddressSpace space, T offset, int size, boolean truncateAddressableUnit) { - if (space.isRegisterSpace()) { - return registerState.getVar(space, offset, size, truncateAddressableUnit); - } - else { - return memoryState.getVar(space, offset, size, truncateAddressableUnit); - } - } - - @Override - public MemBuffer getConcreteBuffer(Address address) { - assert !address.getAddressSpace().isRegisterSpace(); - return memoryState.getConcreteBuffer(address); - } - - public PcodeExecutorState getMemoryState() { - return memoryState; - } - - public PcodeExecutorState getRegisterState() { - return registerState; - } - } - protected final SleighLanguage language; protected final PcodeArithmetic arithmetic; protected final SleighUseropLibrary library; @@ -128,7 +70,7 @@ public abstract class AbstractPcodeMachine implements PcodeMachine { * @return the new thread */ protected PcodeThread createThread(String name) { - return new DefaultPcodeThread<>(name, this, library); + return new DefaultPcodeThread<>(name, this); } protected static PcodeStateInitializer getPluggableInitializer(Language language) { diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/BytesPcodeThread.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/BytesPcodeThread.java index 2178970329..2a5c18384c 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/BytesPcodeThread.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/BytesPcodeThread.java @@ -1,183 +1,22 @@ -/* ### - * 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.lang.reflect.Constructor; - -import ghidra.app.plugin.processors.sleigh.SleighLanguage; -import ghidra.pcode.emulate.*; -import ghidra.pcode.exec.*; -import ghidra.pcode.memstate.MemoryState; -import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; -import ghidra.program.model.lang.*; -import ghidra.program.model.pcode.PcodeOp; -import ghidra.util.Msg; -/** - * A p-code thread which executes on concrete bytes and incorporates per-architecture state - * modifiers - */ -public class BytesPcodeThread extends DefaultPcodeThread { - - protected class GlueEmulate extends Emulate { - public GlueEmulate(SleighLanguage lang, MemoryState s, BreakTable b) { - super(lang, s, b); - } - - @Override - public Language getLanguage() { - return language; - } - - @Override - public void setExecuteAddress(Address addr) { - overrideCounter(addr); - } - - @Override - public Address getExecuteAddress() { - return getCounter(); - } - - @Override - public void setContextRegisterValue(RegisterValue regValue) { - overrideContext(regValue); - } - - @Override - public RegisterValue getContextRegisterValue() { - return getContext(); - } - } - - protected class GlueMemoryState extends MemoryState { - public GlueMemoryState(Language language) { - super(language); - } - - @Override - public int getChunk(byte[] res, AddressSpace spc, long off, int size, - boolean stopOnUnintialized) { - byte[] var = state.getVar(spc, off, size, true); - System.arraycopy(var, 0, res, 0, var.length); - return var.length; - } - - @Override - public void setChunk(byte[] val, AddressSpace spc, long off, int size) { - state.setVar(spc, off, size, true, val); - } - - @Override - public void setInitialized(boolean initialized, AddressSpace spc, long off, int size) { - // Do nothing - } - } - - protected class GluePcodeThreadExecutor extends PcodeThreadExecutor { - public GluePcodeThreadExecutor(Language language, PcodeArithmetic arithmetic, - PcodeExecutorStatePiece state) { - super(language, arithmetic, state); - } - - @Override - public void executeCallother(PcodeOp op, PcodeFrame frame, - SleighUseropLibrary library) { - // Prefer one in the library. Fall-back to state modifier's impl - try { - super.executeCallother(op, frame, library); - } - catch (SleighLinkException e) { - if (modifier == null || !modifier.executeCallOther(op)) { - throw e; - } - } - } - } - - // Part of the glue that makes existing state modifiers work in new emulation framework - protected final EmulateInstructionStateModifier modifier; - protected final Emulate emulate; - - protected Address savedCounter; - - public BytesPcodeThread(String name, AbstractPcodeMachine machine, - SleighUseropLibrary library) { - super(name, machine, library); - - /** - * These two exist as a way to integrate the language-specific injects that are already - * written for the established concrete emulator. - */ - emulate = new GlueEmulate(language, new GlueMemoryState(language), - new BreakTableCallBack(language)); - modifier = createModifier(); - } - - /** - * Construct a modifier for the given language - * - * @return the state modifier - */ - protected EmulateInstructionStateModifier createModifier() { - String classname = language - .getProperty(GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS); - if (classname == null) { - return null; - } - try { - Class c = Class.forName(classname); - if (!EmulateInstructionStateModifier.class.isAssignableFrom(c)) { - Msg.error(this, - "Language " + language.getLanguageID() + " does not specify a valid " + - GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS); - throw new RuntimeException(classname + " does not implement interface " + - EmulateInstructionStateModifier.class.getName()); - } - Constructor constructor = c.getConstructor(Emulate.class); - return (EmulateInstructionStateModifier) constructor.newInstance(emulate); - } - catch (Exception e) { - Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " + - GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS); - throw new RuntimeException( - "Failed to instantiate " + classname + " for language " + language.getLanguageID(), - e); - } +public class BytesPcodeThread extends AbstractModifiedPcodeThread { + public BytesPcodeThread(String name, AbstractPcodeMachine machine) { + super(name, machine); } @Override - protected PcodeThreadExecutor createExecutor() { - return new GluePcodeThreadExecutor(language, arithmetic, state); + protected int getBytesChunk(byte[] res, AddressSpace spc, long off, int size, + boolean stopOnUnintialized) { + byte[] var = state.getVar(spc, off, size, true); + System.arraycopy(var, 0, res, 0, var.length); + return var.length; } @Override - protected void preExecuteInstruction() { - if (modifier != null) { - savedCounter = getCounter(); - modifier.initialExecuteCallback(emulate, savedCounter, getContext()); - } - } - - @Override - protected void postExecuteInstruction() { - if (modifier != null) { - modifier.postExecuteCallback(emulate, savedCounter, frame.copyCode(), - frame.getBranched(), getCounter()); - } + protected void setBytesChunk(byte[] val, AddressSpace spc, long off, int size) { + state.setVar(spc, off, size, true, val); } } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java index e95a38d6d7..b45db55abb 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java @@ -19,7 +19,6 @@ import java.math.BigInteger; import java.util.*; import ghidra.app.plugin.processors.sleigh.SleighLanguage; -import ghidra.pcode.emu.AbstractPcodeMachine.ThreadPcodeExecutorState; import ghidra.pcode.exec.*; import ghidra.program.model.address.Address; import ghidra.program.model.lang.*; @@ -110,8 +109,7 @@ public class DefaultPcodeThread implements PcodeThread { protected final ProgramContextImpl defaultContext; protected final Map injects = new HashMap<>(); - public DefaultPcodeThread(String name, AbstractPcodeMachine machine, - SleighUseropLibrary library) { + public DefaultPcodeThread(String name, AbstractPcodeMachine machine) { this.name = name; this.machine = machine; this.language = machine.language; @@ -119,8 +117,8 @@ public class DefaultPcodeThread implements PcodeThread { PcodeExecutorState memoryState = machine.getMemoryState(); PcodeExecutorState registerState = machine.createRegisterState(this); this.state = new ThreadPcodeExecutorState<>(memoryState, registerState); - this.decoder = new SleighInstructionDecoder(language, memoryState); - this.library = new SleighEmulationLibrary<>(this).compose(library); + this.decoder = createInstructionDecoder(memoryState); + this.library = createUseropLibrary(); this.executor = createExecutor(); this.pc = language.getProgramCounter(); @@ -137,6 +135,14 @@ public class DefaultPcodeThread implements PcodeThread { this.reInitialize(); } + protected SleighInstructionDecoder createInstructionDecoder(PcodeExecutorState memoryState) { + return new SleighInstructionDecoder(language, memoryState); + } + + protected SleighUseropLibrary createUseropLibrary() { + return new SleighEmulationLibrary<>(this).compose(machine.library); + } + protected PcodeThreadExecutor createExecutor() { return new PcodeThreadExecutor(language, arithmetic, state); } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java index 64cf785b97..cc11195a9c 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java @@ -17,7 +17,6 @@ package ghidra.pcode.emu; import java.util.List; -import ghidra.pcode.emu.AbstractPcodeMachine.ThreadPcodeExecutorState; import ghidra.pcode.emu.DefaultPcodeThread.SleighEmulationLibrary; import ghidra.pcode.exec.*; import ghidra.program.model.address.Address; diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/ThreadPcodeExecutorState.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/ThreadPcodeExecutorState.java new file mode 100644 index 0000000000..54354cc61c --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/ThreadPcodeExecutorState.java @@ -0,0 +1,62 @@ +package ghidra.pcode.emu; + +import ghidra.pcode.exec.PcodeExecutorState; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.mem.MemBuffer; + +public class ThreadPcodeExecutorState implements PcodeExecutorState { + protected final PcodeExecutorState memoryState; + protected final PcodeExecutorState registerState; + + public ThreadPcodeExecutorState(PcodeExecutorState memoryState, + PcodeExecutorState registerState) { + this.memoryState = memoryState; + this.registerState = registerState; + } + + @Override + public T longToOffset(AddressSpace space, long l) { + if (space.isRegisterSpace()) { + return registerState.longToOffset(space, l); + } + else { + return memoryState.longToOffset(space, l); + } + } + + @Override + public void setVar(AddressSpace space, T offset, int size, boolean truncateAddressableUnit, + T val) { + if (space.isRegisterSpace()) { + registerState.setVar(space, offset, size, truncateAddressableUnit, val); + } + else { + memoryState.setVar(space, offset, size, truncateAddressableUnit, val); + } + } + + @Override + public T getVar(AddressSpace space, T offset, int size, boolean truncateAddressableUnit) { + if (space.isRegisterSpace()) { + return registerState.getVar(space, offset, size, truncateAddressableUnit); + } + else { + return memoryState.getVar(space, offset, size, truncateAddressableUnit); + } + } + + @Override + public MemBuffer getConcreteBuffer(Address address) { + assert !address.getAddressSpace().isRegisterSpace(); + return memoryState.getConcreteBuffer(address); + } + + public PcodeExecutorState getMemoryState() { + return memoryState; + } + + public PcodeExecutorState getRegisterState() { + return registerState; + } +} \ No newline at end of file