GP-1161: Refactoring legacy state modification glue to abstract class.

This commit is contained in:
Dan
2021-07-27 11:19:46 -04:00
parent 26c3e96bb1
commit 0c56f638a3
8 changed files with 280 additions and 240 deletions

View File

@@ -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;

View File

@@ -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<T> extends DefaultPcodeThread<T> {
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<T> arithmetic,
PcodeExecutorStatePiece<T, T> state) {
super(language, arithmetic, state);
}
@Override
public void executeCallother(PcodeOp op, PcodeFrame frame,
SleighUseropLibrary<T> 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<T> 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());
}
}
}

View File

@@ -30,6 +30,6 @@ public abstract class AbstractPcodeEmulator extends AbstractPcodeMachine<byte[]>
@Override
protected BytesPcodeThread createThread(String name) {
return new BytesPcodeThread(name, this, library);
return new BytesPcodeThread(name, this);
}
}

View File

@@ -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<T> implements PcodeMachine<T> {
public static class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
protected final PcodeExecutorState<T> memoryState;
protected final PcodeExecutorState<T> registerState;
public ThreadPcodeExecutorState(PcodeExecutorState<T> memoryState,
PcodeExecutorState<T> 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<T> getMemoryState() {
return memoryState;
}
public PcodeExecutorState<T> getRegisterState() {
return registerState;
}
}
protected final SleighLanguage language;
protected final PcodeArithmetic<T> arithmetic;
protected final SleighUseropLibrary<T> library;
@@ -128,7 +70,7 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
* @return the new thread
*/
protected PcodeThread<T> createThread(String name) {
return new DefaultPcodeThread<>(name, this, library);
return new DefaultPcodeThread<>(name, this);
}
protected static PcodeStateInitializer getPluggableInitializer(Language language) {

View File

@@ -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<byte[]> {
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<byte[]> arithmetic,
PcodeExecutorStatePiece<byte[], byte[]> state) {
super(language, arithmetic, state);
}
@Override
public void executeCallother(PcodeOp op, PcodeFrame frame,
SleighUseropLibrary<byte[]> 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<byte[]> machine,
SleighUseropLibrary<byte[]> 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<byte[]> {
public BytesPcodeThread(String name, AbstractPcodeMachine<byte[]> 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);
}
}

View File

@@ -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<T> implements PcodeThread<T> {
protected final ProgramContextImpl defaultContext;
protected final Map<Address, PcodeProgram> injects = new HashMap<>();
public DefaultPcodeThread(String name, AbstractPcodeMachine<T> machine,
SleighUseropLibrary<T> library) {
public DefaultPcodeThread(String name, AbstractPcodeMachine<T> machine) {
this.name = name;
this.machine = machine;
this.language = machine.language;
@@ -119,8 +117,8 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
PcodeExecutorState<T> memoryState = machine.getMemoryState();
PcodeExecutorState<T> 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<T> implements PcodeThread<T> {
this.reInitialize();
}
protected SleighInstructionDecoder createInstructionDecoder(PcodeExecutorState<T> memoryState) {
return new SleighInstructionDecoder(language, memoryState);
}
protected SleighUseropLibrary<T> createUseropLibrary() {
return new SleighEmulationLibrary<>(this).compose(machine.library);
}
protected PcodeThreadExecutor createExecutor() {
return new PcodeThreadExecutor(language, arithmetic, state);
}

View File

@@ -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;

View File

@@ -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<T> implements PcodeExecutorState<T> {
protected final PcodeExecutorState<T> memoryState;
protected final PcodeExecutorState<T> registerState;
public ThreadPcodeExecutorState(PcodeExecutorState<T> memoryState,
PcodeExecutorState<T> 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<T> getMemoryState() {
return memoryState;
}
public PcodeExecutorState<T> getRegisterState() {
return registerState;
}
}