mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 21:40:59 +08:00
Merge remote-tracking branch 'origin/GP-5301_Dan_testEmuThumbPlt'
This commit is contained in:
+5
@@ -181,6 +181,11 @@ public interface EmuSyscallLibrary<T> extends PcodeUseropLibrary<T> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean modifiesContext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInlinePcode() {
|
public boolean canInlinePcode() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -171,9 +171,10 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void executeSleigh(String source) {
|
public void executeSleigh(String source) {
|
||||||
|
PcodeUseropLibrary<T> library = thread.getUseropLibrary();
|
||||||
PcodeProgram program =
|
PcodeProgram program =
|
||||||
SleighProgramCompiler.compileProgram(language, "exec", source, thread.library);
|
SleighProgramCompiler.compileProgram(language, "exec", source, library);
|
||||||
execute(program, thread.library);
|
execute(program, library);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -224,7 +225,9 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
protected final PcodeArithmetic<T> arithmetic;
|
protected final PcodeArithmetic<T> arithmetic;
|
||||||
protected final ThreadPcodeExecutorState<T> state;
|
protected final ThreadPcodeExecutorState<T> state;
|
||||||
protected final InstructionDecoder decoder;
|
protected final InstructionDecoder decoder;
|
||||||
protected final PcodeUseropLibrary<T> library;
|
|
||||||
|
// Delay, and compute lazily
|
||||||
|
private PcodeUseropLibrary<T> library;
|
||||||
|
|
||||||
protected final PcodeThreadExecutor<T> executor;
|
protected final PcodeThreadExecutor<T> executor;
|
||||||
protected final Register pc;
|
protected final Register pc;
|
||||||
@@ -255,7 +258,6 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
PcodeExecutorState<T> localState = machine.createLocalState(this);
|
PcodeExecutorState<T> localState = machine.createLocalState(this);
|
||||||
this.state = createThreadState(sharedState, localState);
|
this.state = createThreadState(sharedState, localState);
|
||||||
this.decoder = createInstructionDecoder(sharedState);
|
this.decoder = createInstructionDecoder(sharedState);
|
||||||
this.library = createUseropLibrary();
|
|
||||||
|
|
||||||
this.executor = createExecutor();
|
this.executor = createExecutor();
|
||||||
this.pc =
|
this.pc =
|
||||||
@@ -342,7 +344,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
decoder.branched(counter);
|
decoder.branched(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeCounter(Address counter) {
|
protected final void writeCounter(Address counter) {
|
||||||
setCounter(counter);
|
setCounter(counter);
|
||||||
state.setVar(pc,
|
state.setVar(pc,
|
||||||
arithmetic.fromConst(counter.getAddressableWordOffset(), pc.getMinimumByteSize()));
|
arithmetic.fromConst(counter.getAddressableWordOffset(), pc.getMinimumByteSize()));
|
||||||
@@ -366,14 +368,18 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected final void writeContext(RegisterValue context) {
|
||||||
public void overrideContext(RegisterValue context) {
|
|
||||||
assignContext(context);
|
assignContext(context);
|
||||||
state.setVar(contextreg, arithmetic.fromConst(
|
state.setVar(contextreg, arithmetic.fromConst(
|
||||||
this.context.getUnsignedValueIgnoreMask(),
|
this.context.getUnsignedValueIgnoreMask(),
|
||||||
contextreg.getMinimumByteSize(), true));
|
contextreg.getMinimumByteSize(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void overrideContext(RegisterValue context) {
|
||||||
|
writeContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void overrideContextWithDefault() {
|
public void overrideContextWithDefault() {
|
||||||
if (contextreg != Register.NO_CONTEXT) {
|
if (contextreg != Register.NO_CONTEXT) {
|
||||||
@@ -421,7 +427,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
if (inj != null) {
|
if (inj != null) {
|
||||||
instruction = null;
|
instruction = null;
|
||||||
try {
|
try {
|
||||||
executor.execute(inj, library);
|
executor.execute(inj, getUseropLibrary());
|
||||||
}
|
}
|
||||||
catch (PcodeExecutionException e) {
|
catch (PcodeExecutionException e) {
|
||||||
frame = e.getFrame();
|
frame = e.getFrame();
|
||||||
@@ -439,7 +445,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
beginInstructionOrInject();
|
beginInstructionOrInject();
|
||||||
}
|
}
|
||||||
else if (!frame.isFinished()) {
|
else if (!frame.isFinished()) {
|
||||||
executor.step(frame, library);
|
executor.step(frame, getUseropLibrary());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
advanceAfterFinished();
|
advanceAfterFinished();
|
||||||
@@ -462,7 +468,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void stepPatch(String sleigh) {
|
public void stepPatch(String sleigh) {
|
||||||
PcodeProgram prog = getMachine().compileSleigh("patch", sleigh + ";");
|
PcodeProgram prog = getMachine().compileSleigh("patch", sleigh + ";");
|
||||||
executor.execute(prog, library);
|
executor.execute(prog, getUseropLibrary());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -523,14 +529,14 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (frame.isFallThrough()) {
|
if (frame.isFallThrough()) {
|
||||||
overrideCounter(counter.addWrap(decoder.getLastLengthWithDelays()));
|
writeCounter(counter.addWrap(decoder.getLastLengthWithDelays()));
|
||||||
}
|
}
|
||||||
if (contextreg != Register.NO_CONTEXT) {
|
if (contextreg != Register.NO_CONTEXT) {
|
||||||
RegisterValue ctx = new RegisterValue(contextreg, BigInteger.ZERO)
|
RegisterValue ctx = new RegisterValue(contextreg, BigInteger.ZERO)
|
||||||
.combineValues(defaultContext.getDefaultValue(contextreg, counter))
|
.combineValues(defaultContext.getDefaultValue(contextreg, counter))
|
||||||
.combineValues(defaultContext.getFlowValue(context))
|
.combineValues(defaultContext.getFlowValue(context))
|
||||||
.combineValues(getContextAfterCommits());
|
.combineValues(getContextAfterCommits());
|
||||||
overrideContext(ctx);
|
writeContext(ctx);
|
||||||
}
|
}
|
||||||
postExecuteInstruction();
|
postExecuteInstruction();
|
||||||
frame = null;
|
frame = null;
|
||||||
@@ -603,7 +609,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
PcodeProgram insProg = PcodeProgram.fromInstruction(instruction);
|
PcodeProgram insProg = PcodeProgram.fromInstruction(instruction);
|
||||||
preExecuteInstruction();
|
preExecuteInstruction();
|
||||||
try {
|
try {
|
||||||
frame = executor.execute(insProg, library);
|
frame = executor.execute(insProg, getUseropLibrary());
|
||||||
}
|
}
|
||||||
catch (PcodeExecutionException e) {
|
catch (PcodeExecutionException e) {
|
||||||
frame = e.getFrame();
|
frame = e.getFrame();
|
||||||
@@ -615,7 +621,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void finishInstruction() {
|
public void finishInstruction() {
|
||||||
assertMidInstruction();
|
assertMidInstruction();
|
||||||
executor.finish(frame, library);
|
executor.finish(frame, getUseropLibrary());
|
||||||
advanceAfterFinished();
|
advanceAfterFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,6 +675,9 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PcodeUseropLibrary<T> getUseropLibrary() {
|
public PcodeUseropLibrary<T> getUseropLibrary() {
|
||||||
|
if (library == null) {
|
||||||
|
library = createUseropLibrary();
|
||||||
|
}
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,7 +706,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void inject(Address address, String source) {
|
public void inject(Address address, String source) {
|
||||||
PcodeProgram pcode = SleighProgramCompiler.compileProgram(
|
PcodeProgram pcode = SleighProgramCompiler.compileProgram(
|
||||||
language, "thread_inject:" + address, source, library);
|
language, "thread_inject:" + address, source, getUseropLibrary());
|
||||||
injects.put(address, pcode);
|
injects.put(address, pcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+98
-10
@@ -16,18 +16,22 @@
|
|||||||
package ghidra.pcode.emu;
|
package ghidra.pcode.emu;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import ghidra.app.emulator.AdaptedMemoryState;
|
import ghidra.app.emulator.AdaptedMemoryState;
|
||||||
import ghidra.app.emulator.Emulator;
|
import ghidra.app.emulator.Emulator;
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.pcode.emulate.*;
|
import ghidra.pcode.emulate.*;
|
||||||
import ghidra.pcode.exec.ConcretionError;
|
import ghidra.pcode.emulate.callother.OpBehaviorOther;
|
||||||
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
import ghidra.pcode.memstate.MemoryBank;
|
import ghidra.pcode.memstate.MemoryBank;
|
||||||
import ghidra.pcode.memstate.MemoryState;
|
import ghidra.pcode.memstate.MemoryState;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,6 +92,93 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For incorporating the state modifier's userop behaviors
|
||||||
|
*/
|
||||||
|
protected class ModifierUseropLibrary implements PcodeUseropLibrary<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around {@link OpBehaviorOther}
|
||||||
|
*/
|
||||||
|
protected class ModifierUseropDefinition implements PcodeUseropDefinition<T> {
|
||||||
|
private final String name;
|
||||||
|
private final OpBehaviorOther behavior;
|
||||||
|
|
||||||
|
public ModifierUseropDefinition(String name, OpBehaviorOther behavior) {
|
||||||
|
this.name = name;
|
||||||
|
this.behavior = behavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInputCount() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
|
||||||
|
Varnode outVar, List<Varnode> inVars) {
|
||||||
|
behavior.evaluate(emulate, outVar, inVars.toArray(Varnode[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFunctional() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSideEffects() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean modifiesContext() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canInlinePcode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Method getJavaMethod() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PcodeUseropLibrary<?> getDefiningLibrary() {
|
||||||
|
return ModifierUseropLibrary.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, PcodeUseropDefinition<T>> userops;
|
||||||
|
|
||||||
|
private Map<String, PcodeUseropDefinition<T>> computeUserops() {
|
||||||
|
if (modifier == null) {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
Map<String, PcodeUseropDefinition<T>> userops = new HashMap<>();
|
||||||
|
for (Entry<Integer, OpBehaviorOther> entry : modifier.getPcodeOpMap().entrySet()) {
|
||||||
|
String name = language.getUserDefinedOpName(entry.getKey());
|
||||||
|
userops.put(name, new ModifierUseropDefinition(name, entry.getValue()));
|
||||||
|
}
|
||||||
|
return userops;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, PcodeUseropDefinition<T>> getUserops() {
|
||||||
|
if (userops == null) {
|
||||||
|
userops = computeUserops();
|
||||||
|
}
|
||||||
|
return userops;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the glue that makes existing state modifiers work in new emulation framework
|
* Part of the glue that makes existing state modifiers work in new emulation framework
|
||||||
*
|
*
|
||||||
@@ -155,6 +246,11 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeUseropLibrary<T> createUseropLibrary() {
|
||||||
|
return super.createUseropLibrary().compose(new ModifierUseropLibrary());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void overrideCounter(Address counter) {
|
public void overrideCounter(Address counter) {
|
||||||
super.overrideCounter(counter);
|
super.overrideCounter(counter);
|
||||||
@@ -171,12 +267,4 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
|||||||
frame.getBranched(), getCounter());
|
frame.getBranched(), getCounter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean onMissingUseropDef(PcodeOp op, String opName) {
|
|
||||||
if (modifier != null) {
|
|
||||||
return modifier.executeCallOther(op);
|
|
||||||
}
|
|
||||||
return super.onMissingUseropDef(op, opName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ public class JitCompiler {
|
|||||||
* In production, this should be empty.
|
* In production, this should be empty.
|
||||||
*/
|
*/
|
||||||
public static final EnumSet<Diag> ENABLE_DIAGNOSTICS = EnumSet.noneOf(Diag.class);
|
public static final EnumSet<Diag> ENABLE_DIAGNOSTICS = EnumSet.noneOf(Diag.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exclude a given address offset from ASM's {@link ClassWriter#COMPUTE_MAXS} and
|
* Exclude a given address offset from ASM's {@link ClassWriter#COMPUTE_MAXS} and
|
||||||
* {@link ClassWriter#COMPUTE_FRAMES}.
|
* {@link ClassWriter#COMPUTE_FRAMES}.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
|||||||
import ghidra.pcode.emu.jit.gen.op.OpGen;
|
import ghidra.pcode.emu.jit.gen.op.OpGen;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
|
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.PcodeUserop;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressOverflowException;
|
import ghidra.program.model.address.AddressOverflowException;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
@@ -252,6 +253,122 @@ public class JitPassage extends PcodeProgram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch as analyzed within an instruction step
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* After intra-instruction reachability is determined and this branch is to be added to the
|
||||||
|
* whole passage, it will be "upgraded" to a {@link PBranch}.
|
||||||
|
*/
|
||||||
|
public interface SBranch extends Branch {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch as analyzed within a passage
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Many implement this via {@link RBranch}.
|
||||||
|
*/
|
||||||
|
public interface PBranch extends Branch {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch with known intra-instruction reachability
|
||||||
|
*/
|
||||||
|
public interface RBranch extends PBranch {
|
||||||
|
/**
|
||||||
|
* The intra-instruction reachability
|
||||||
|
*
|
||||||
|
* @return the reachability
|
||||||
|
*/
|
||||||
|
Reachability reach();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the manner in which something is reachable, wrt. dynamic context changes <em>within
|
||||||
|
* an instruction step</em>.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* At the moment, the only way context can be changed dynamically is via a p-code userop. Such
|
||||||
|
* ops must have the {@link PcodeUserop#modifiesContext()} attribute set. If such an op is known
|
||||||
|
* to have been executed when finishing an instruction (either by branch or fall-through), we
|
||||||
|
* must exit the compiled passage.
|
||||||
|
*/
|
||||||
|
public enum Reachability {
|
||||||
|
/**
|
||||||
|
* There is at least one path to reach it. None of them modify the context dynamically.
|
||||||
|
*/
|
||||||
|
WITHOUT_CTXMOD {
|
||||||
|
@Override
|
||||||
|
public Reachability combine(Reachability that) {
|
||||||
|
return switch (that) {
|
||||||
|
case null -> this;
|
||||||
|
case WITHOUT_CTXMOD -> WITHOUT_CTXMOD;
|
||||||
|
case MAYBE_CTXMOD -> MAYBE_CTXMOD;
|
||||||
|
case WITH_CTXMOD -> MAYBE_CTXMOD;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canReachWithoutCtxMod() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* There are at least two paths to reach it. Some modify the context dynamically, and some
|
||||||
|
* do not.
|
||||||
|
*/
|
||||||
|
MAYBE_CTXMOD {
|
||||||
|
@Override
|
||||||
|
public Reachability combine(Reachability that) {
|
||||||
|
return MAYBE_CTXMOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canReachWithoutCtxMod() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* There is at least one path to reach it. All of them modify the context dynamically.
|
||||||
|
*/
|
||||||
|
WITH_CTXMOD {
|
||||||
|
@Override
|
||||||
|
public Reachability combine(Reachability that) {
|
||||||
|
return switch (that) {
|
||||||
|
case null -> this;
|
||||||
|
case WITHOUT_CTXMOD -> MAYBE_CTXMOD;
|
||||||
|
case MAYBE_CTXMOD -> MAYBE_CTXMOD;
|
||||||
|
case WITH_CTXMOD -> WITH_CTXMOD;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canReachWithoutCtxMod() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consider this and another reachability as "or"
|
||||||
|
*
|
||||||
|
* @param that the other reachability
|
||||||
|
* @return the "or" of both
|
||||||
|
*/
|
||||||
|
public abstract Reachability combine(Reachability that);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if it is possible for this block to be reached without a context modification.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is true if there exists <em>any</em> path to this block that doesn't include a
|
||||||
|
* possible context modification.
|
||||||
|
*
|
||||||
|
* @return true if reachable without context modification, false otherwise.
|
||||||
|
*/
|
||||||
|
public abstract boolean canReachWithoutCtxMod();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A branch to another p-code op in the same passage
|
* A branch to another p-code op in the same passage
|
||||||
*
|
*
|
||||||
@@ -260,12 +377,46 @@ public class JitPassage extends PcodeProgram {
|
|||||||
* equivalent branch to the translation of the target p-code op. Thus, we remain executing
|
* equivalent branch to the translation of the target p-code op. Thus, we remain executing
|
||||||
* inside the {@link JitCompiledPassage#run(int) run} method. This branch type incurs the least
|
* inside the {@link JitCompiledPassage#run(int) run} method. This branch type incurs the least
|
||||||
* run-time cost.
|
* run-time cost.
|
||||||
*
|
|
||||||
* @param from see {@link #from()}
|
|
||||||
* @param to the target p-code op
|
|
||||||
* @param isFall see {@link #isFall()}
|
|
||||||
*/
|
*/
|
||||||
public record IntBranch(PcodeOp from, PcodeOp to, boolean isFall) implements Branch {}
|
public interface IntBranch extends Branch {
|
||||||
|
/**
|
||||||
|
* The target pcode op
|
||||||
|
*
|
||||||
|
* @return the op
|
||||||
|
*/
|
||||||
|
PcodeOp to();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link IntBranch} as analyzed during one instruction step
|
||||||
|
*
|
||||||
|
* @param from see {@link IntBranch#from()}
|
||||||
|
* @param to see {@link IntBranch#to()}
|
||||||
|
* @param isFall see {@link IntBranch#isFall()}
|
||||||
|
*/
|
||||||
|
public record SIntBranch(PcodeOp from, PcodeOp to, boolean isFall)
|
||||||
|
implements IntBranch, SBranch {
|
||||||
|
/**
|
||||||
|
* Upgrade this branch to an {@link RIntBranch} for inclusion in the passage.
|
||||||
|
*
|
||||||
|
* @param reach see {@link RBranch#reach()}
|
||||||
|
* @return the branch
|
||||||
|
*/
|
||||||
|
public RIntBranch withReach(Reachability reach) {
|
||||||
|
return new RIntBranch(from, to, isFall, reach);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link IntBranch} as added to the passage
|
||||||
|
*
|
||||||
|
* @param from see {@link IntBranch#from()}
|
||||||
|
* @param to see {@link IntBranch#to()}
|
||||||
|
* @param isFall see {@link IntBranch#isFall()}
|
||||||
|
* @param reach see {@link RBranch#reach()}
|
||||||
|
*/
|
||||||
|
public record RIntBranch(PcodeOp from, PcodeOp to, boolean isFall, Reachability reach)
|
||||||
|
implements IntBranch, RBranch {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A branch to an address (and context value) not in the same passage
|
* A branch to an address (and context value) not in the same passage
|
||||||
@@ -275,13 +426,62 @@ public class JitPassage extends PcodeProgram {
|
|||||||
* sets the emulator's program counter and context to the {@link #to() branch target} and
|
* sets the emulator's program counter and context to the {@link #to() branch target} and
|
||||||
* returns the appropriate entry point for further execution.
|
* returns the appropriate entry point for further execution.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Note that this branch type is used by the decoder to track queued decode seeds as well.
|
* Note that this branch type is used by the decoder to track queued decode seeds as well.
|
||||||
* External branches that get decoded are changed into internal branches.
|
* External branches that get decoded are changed into internal branches.
|
||||||
*
|
|
||||||
* @param from see {@link #from()}
|
|
||||||
* @param to the target address-context pair
|
|
||||||
*/
|
*/
|
||||||
public record ExtBranch(PcodeOp from, AddrCtx to) implements Branch {}
|
public interface ExtBranch extends Branch {
|
||||||
|
/**
|
||||||
|
* The target address-context pair
|
||||||
|
*
|
||||||
|
* @return the target
|
||||||
|
*/
|
||||||
|
AddrCtx to();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link ExtBranch} as analyzed during one instruction step
|
||||||
|
*
|
||||||
|
* @param from see {@link ExtBranch#from()}
|
||||||
|
* @param to see {@link ExtBranch#to()}
|
||||||
|
*/
|
||||||
|
public record SExtBranch(PcodeOp from, AddrCtx to) implements ExtBranch, SBranch {
|
||||||
|
/**
|
||||||
|
* Upgrade this branch to an {@link RExtBranch} for inclusion in the passage.
|
||||||
|
*
|
||||||
|
* @param reach see {@link RBranch#reach()}
|
||||||
|
* @return the branch
|
||||||
|
*/
|
||||||
|
public RExtBranch withReach(Reachability reach) {
|
||||||
|
return new RExtBranch(from, to, reach);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ExtBranch} as added to the passage
|
||||||
|
*
|
||||||
|
* @param from see {@link ExtBranch#from()}
|
||||||
|
* @param to see {@link ExtBranch#to()}
|
||||||
|
* @param reach see {@link RBranch#reach()}
|
||||||
|
*/
|
||||||
|
public record RExtBranch(PcodeOp from, AddrCtx to, Reachability reach)
|
||||||
|
implements ExtBranch, RBranch {
|
||||||
|
/**
|
||||||
|
* Convert this external branch into an internal one
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is called whenever it becomes the case that an external target is decoded an added
|
||||||
|
* to the passage, making it an internal branch. Notably, this happens when selecting a seed
|
||||||
|
* from the queue of externals, when flowing to a target that is already decoded, and when
|
||||||
|
* finishing up a passage where all remaining seeds must be examined.
|
||||||
|
*
|
||||||
|
* @param to the target p-code op
|
||||||
|
* @return the resulting internal branch
|
||||||
|
*/
|
||||||
|
public RIntBranch toIntBranch(PcodeOp to) {
|
||||||
|
return new RIntBranch(from, to, false, reach);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A branch to a dynamic address
|
* A branch to a dynamic address
|
||||||
@@ -295,11 +495,43 @@ public class JitPassage extends PcodeProgram {
|
|||||||
* TODO: Some analysis may be possible to narrow the possible addresses to a known few and then
|
* TODO: Some analysis may be possible to narrow the possible addresses to a known few and then
|
||||||
* treat this as several {@link IntBranch}es; however, I worry this is too expensive for what it
|
* treat this as several {@link IntBranch}es; however, I worry this is too expensive for what it
|
||||||
* gets us. This will be necessary if we are to JIT, e.g., a switch table.
|
* gets us. This will be necessary if we are to JIT, e.g., a switch table.
|
||||||
*
|
|
||||||
* @param from see {@link #from()}
|
|
||||||
* @param flowCtx the decode context after the branch is taken
|
|
||||||
*/
|
*/
|
||||||
public record IndBranch(PcodeOp from, RegisterValue flowCtx) implements Branch {}
|
public interface IndBranch extends Branch {
|
||||||
|
/**
|
||||||
|
* The decode context after the branch is taken
|
||||||
|
*
|
||||||
|
* @return the context
|
||||||
|
*/
|
||||||
|
RegisterValue flowCtx();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link IndBranch} as analyzed during one instruction step
|
||||||
|
*
|
||||||
|
* @param from see {@link IndBranch#from()}
|
||||||
|
* @param flowCtx see {@link IndBranch#flowCtx()}
|
||||||
|
*/
|
||||||
|
public record SIndBranch(PcodeOp from, RegisterValue flowCtx) implements IndBranch, SBranch {
|
||||||
|
/**
|
||||||
|
* Upgrade this branch to an {@link RIndBranch} for inclusion in the passage.
|
||||||
|
*
|
||||||
|
* @param reach see {@link RBranch#reach()}
|
||||||
|
* @return the branch
|
||||||
|
*/
|
||||||
|
public RIndBranch withReach(Reachability reach) {
|
||||||
|
return new RIndBranch(from, flowCtx, reach);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link IndBranch} as added to the passage
|
||||||
|
*
|
||||||
|
* @param from see {@link IndBranch#from()}
|
||||||
|
* @param flowCtx see {@link IndBranch#flowCtx()}
|
||||||
|
* @param reach see {@link RBranch#reach()}
|
||||||
|
*/
|
||||||
|
public record RIndBranch(PcodeOp from, RegisterValue flowCtx, Reachability reach)
|
||||||
|
implements IndBranch, RBranch {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A "branch" representing an error
|
* A "branch" representing an error
|
||||||
@@ -327,7 +559,7 @@ public class JitPassage extends PcodeProgram {
|
|||||||
* @param from see {@link #from()}
|
* @param from see {@link #from()}
|
||||||
* @param message the error message for the exception
|
* @param message the error message for the exception
|
||||||
*/
|
*/
|
||||||
public record ErrBranch(PcodeOp from, String message) implements Branch {}
|
public record ErrBranch(PcodeOp from, String message) implements SBranch, PBranch {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extension of {@link PcodeOp} that carries along with it the address and decode context
|
* An extension of {@link PcodeOp} that carries along with it the address and decode context
|
||||||
@@ -462,10 +694,26 @@ public class JitPassage extends PcodeProgram {
|
|||||||
*
|
*
|
||||||
* @param at the address and context value to set on the emulator when exiting the
|
* @param at the address and context value to set on the emulator when exiting the
|
||||||
* {@link JitCompiledPassage#run(int)} method
|
* {@link JitCompiledPassage#run(int)} method
|
||||||
|
* @return the op
|
||||||
*/
|
*/
|
||||||
public ExitPcodeOp(AddrCtx at) {
|
public static ExitPcodeOp exit(AddrCtx at) {
|
||||||
super(new SequenceNumber(at.address, 0), PcodeOp.BRANCH, new Varnode[] {
|
return new ExitPcodeOp(PcodeOp.BRANCH, at);
|
||||||
new Varnode(at.address, 0) }, null);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a synthetic conditional exit op
|
||||||
|
*
|
||||||
|
* @param at the address and context value to set on the emulator when exiting the
|
||||||
|
* {@link JitCompiledPassage#run(int)} method
|
||||||
|
* @return the op
|
||||||
|
*/
|
||||||
|
public static ExitPcodeOp cond(AddrCtx at) {
|
||||||
|
return new ExitPcodeOp(PcodeOp.CBRANCH, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExitPcodeOp(int opcode, AddrCtx at) {
|
||||||
|
super(new SequenceNumber(at.address, 0), opcode,
|
||||||
|
new Varnode[] { new Varnode(at.address, 0) }, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,7 +951,7 @@ public class JitPassage extends PcodeProgram {
|
|||||||
private final List<Instruction> instructions;
|
private final List<Instruction> instructions;
|
||||||
private final AddrCtx entry;
|
private final AddrCtx entry;
|
||||||
private final PcodeUseropLibrary<Object> decodeLibrary;
|
private final PcodeUseropLibrary<Object> decodeLibrary;
|
||||||
private final Map<PcodeOp, Branch> branches;
|
private final Map<PcodeOp, PBranch> branches;
|
||||||
private final Map<PcodeOp, AddrCtx> entries;
|
private final Map<PcodeOp, AddrCtx> entries;
|
||||||
private final Register contextreg;
|
private final Register contextreg;
|
||||||
private final ProgramContextImpl defaultContext;
|
private final ProgramContextImpl defaultContext;
|
||||||
@@ -725,7 +973,7 @@ public class JitPassage extends PcodeProgram {
|
|||||||
*/
|
*/
|
||||||
public JitPassage(SleighLanguage language, AddrCtx entry, List<PcodeOp> code,
|
public JitPassage(SleighLanguage language, AddrCtx entry, List<PcodeOp> code,
|
||||||
PcodeUseropLibrary<Object> decodeLibrary, List<Instruction> instructions,
|
PcodeUseropLibrary<Object> decodeLibrary, List<Instruction> instructions,
|
||||||
Map<PcodeOp, Branch> branches, Map<PcodeOp, AddrCtx> entries) {
|
Map<PcodeOp, PBranch> branches, Map<PcodeOp, AddrCtx> entries) {
|
||||||
super(language, code, decodeLibrary.getSymbols(language));
|
super(language, code, decodeLibrary.getSymbols(language));
|
||||||
this.entry = entry;
|
this.entry = entry;
|
||||||
this.decodeLibrary = decodeLibrary;
|
this.decodeLibrary = decodeLibrary;
|
||||||
@@ -804,7 +1052,7 @@ public class JitPassage extends PcodeProgram {
|
|||||||
*
|
*
|
||||||
* @return the branches, keyed by {@link Branch#from()}.
|
* @return the branches, keyed by {@link Branch#from()}.
|
||||||
*/
|
*/
|
||||||
public Map<PcodeOp, Branch> getBranches() {
|
public Map<PcodeOp, PBranch> getBranches() {
|
||||||
return branches;
|
return branches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
|||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPointPrototype;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPointPrototype;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
import ghidra.program.model.listing.ProgramContext;
|
import ghidra.program.model.listing.ProgramContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,4 +276,40 @@ public class JitPcodeThread extends BytesPcodeThread {
|
|||||||
throw new SuspendedPcodeExecutionException(null, null);
|
throw new SuspendedPcodeExecutionException(null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the given counter and context to the emulator and its machine state
|
||||||
|
*
|
||||||
|
* @param counter the counter
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
public void writeCounterAndContext(Address counter, RegisterValue context) {
|
||||||
|
// Not overrideCounter/Context. Things can override those.
|
||||||
|
writeCounter(counter);
|
||||||
|
if (context != null) {
|
||||||
|
writeContext(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the emulator's counter and context without affecting its machine state
|
||||||
|
*
|
||||||
|
* @param counter the counter
|
||||||
|
* @param context the context
|
||||||
|
* @implNote the reasons for doing this are a bit nuanced and they deal in the setting of the pc
|
||||||
|
* by p-code ops whilst it also makes hazardous userop invocations. The intended value
|
||||||
|
* of the pc may not survive if it gets clobbered with the current counter before
|
||||||
|
* execution reaches the "goto pc".
|
||||||
|
*/
|
||||||
|
public void setCounterAndContext(Address counter, RegisterValue context) {
|
||||||
|
setCounter(counter);
|
||||||
|
if (context != null) {
|
||||||
|
/**
|
||||||
|
* TODO: Later this might become assignContext, but only after we establish conventions
|
||||||
|
* for accessing context in userops. For now, state modifiers expect the contextreg to
|
||||||
|
* be in the machine state.
|
||||||
|
*/
|
||||||
|
writeContext(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+228
-12
@@ -29,14 +29,14 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
|||||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||||
import ghidra.pcode.emu.jit.JitCompiler;
|
import ghidra.pcode.emu.jit.JitCompiler;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||||
|
import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.var.*;
|
import ghidra.pcode.emu.jit.var.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.lang.Endian;
|
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -615,16 +615,17 @@ public class JitAllocationModel {
|
|||||||
* <p>
|
* <p>
|
||||||
* This is just a logical grouping of a varnode and its assigned p-code type.
|
* This is just a logical grouping of a varnode and its assigned p-code type.
|
||||||
*/
|
*/
|
||||||
private record VarDesc(int spaceId, long offset, int size, JitType type) {
|
private record VarDesc(int spaceId, long offset, int size, JitType type, Language language) {
|
||||||
/**
|
/**
|
||||||
* Create a descriptor from the given varnode and type
|
* Create a descriptor from the given varnode and type
|
||||||
*
|
*
|
||||||
* @param vn the varnode
|
* @param vn the varnode
|
||||||
* @param type the p-code type
|
* @param type the p-code type
|
||||||
|
* @param langauge the language
|
||||||
* @return the descriptor
|
* @return the descriptor
|
||||||
*/
|
*/
|
||||||
static VarDesc fromVarnode(Varnode vn, JitType type) {
|
static VarDesc fromVarnode(Varnode vn, JitType type, Language language) {
|
||||||
return new VarDesc(vn.getSpace(), vn.getOffset(), vn.getSize(), type);
|
return new VarDesc(vn.getSpace(), vn.getOffset(), vn.getSize(), type, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -633,20 +634,232 @@ public class JitAllocationModel {
|
|||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
public String name() {
|
public String name() {
|
||||||
|
AddressFactory factory = language.getAddressFactory();
|
||||||
|
AddressSpace space = factory.getAddressSpace(spaceId);
|
||||||
|
Register reg = language.getRegister(space, offset, size);
|
||||||
|
if (reg != null) {
|
||||||
|
return "%s_%d_%s".formatted(reg.getName(), size, type.nm());
|
||||||
|
}
|
||||||
return "s%d_%x_%d_%s".formatted(spaceId, offset, size, type.nm());
|
return "s%d_%x_%d_%s".formatted(spaceId, offset, size, type.nm());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert this descriptor back to a varnode
|
* Convert this descriptor back to a varnode
|
||||||
*
|
*
|
||||||
* @param factory the address factory for the emulation target language
|
|
||||||
* @return the varnode
|
* @return the varnode
|
||||||
*/
|
*/
|
||||||
public Varnode toVarnode(AddressFactory factory) {
|
public Varnode toVarnode() {
|
||||||
|
AddressFactory factory = language.getAddressFactory();
|
||||||
return new Varnode(factory.getAddressSpace(spaceId).getAddress(offset), size);
|
return new Varnode(factory.getAddressSpace(spaceId).getAddress(offset), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A local that is always allocated in its respective method
|
||||||
|
*/
|
||||||
|
public interface FixedLocal {
|
||||||
|
/**
|
||||||
|
* The JVM index of the local
|
||||||
|
*
|
||||||
|
* @return the index
|
||||||
|
*/
|
||||||
|
int index();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the local
|
||||||
|
*
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
String varName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JVM type descriptor for the local
|
||||||
|
*
|
||||||
|
* @param nameThis the name of this class, in case it's the this pointer.
|
||||||
|
* @return the type descriptor
|
||||||
|
*/
|
||||||
|
String typeDesc(String nameThis);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JVM opcode used to load the variable
|
||||||
|
*
|
||||||
|
* @return the load opcode
|
||||||
|
*/
|
||||||
|
int opcodeLoad();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JVM opcode used to store the variable
|
||||||
|
*
|
||||||
|
* @return the store opcode
|
||||||
|
*/
|
||||||
|
int opcodeStore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the declaration of this variable.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is not required, but is nice to have when debugging generated code.
|
||||||
|
*
|
||||||
|
* @param mv the method visitor
|
||||||
|
* @param nameThis the name of the class defining the containing method
|
||||||
|
* @param startLocals the start label which should be placed at the top of the method
|
||||||
|
* @param endLocals the end label which should be placed at the bottom of the method
|
||||||
|
*/
|
||||||
|
default void generateDeclCode(MethodVisitor mv, String nameThis, Label startLocals,
|
||||||
|
Label endLocals) {
|
||||||
|
mv.visitLocalVariable(varName(), typeDesc(nameThis), null, startLocals, endLocals,
|
||||||
|
index());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a load of this variable onto the JVM stack.
|
||||||
|
*
|
||||||
|
* @param mv the method visitor
|
||||||
|
*/
|
||||||
|
default void generateLoadCode(MethodVisitor mv) {
|
||||||
|
mv.visitVarInsn(opcodeLoad(), index());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a store to this variable from the JVM stack.
|
||||||
|
*
|
||||||
|
* @param mv the method visitor
|
||||||
|
*/
|
||||||
|
default void generateStoreCode(MethodVisitor mv) {
|
||||||
|
mv.visitVarInsn(opcodeStore(), index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locals that exist in every compiled passage's constructor.
|
||||||
|
*/
|
||||||
|
public enum InitFixedLocal implements FixedLocal {
|
||||||
|
/**
|
||||||
|
* Because we're compiling a non-static method, the JVM reserves index 0 for {@code this}.
|
||||||
|
*/
|
||||||
|
THIS("this", ALOAD, ASTORE) {
|
||||||
|
@Override
|
||||||
|
public String typeDesc(String nameThis) {
|
||||||
|
return "L" + nameThis + ";";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* The parameter {@code thread} is reserved by the JVM into index 1.
|
||||||
|
*/
|
||||||
|
THREAD("thread", ALOAD, ASTORE) {
|
||||||
|
@Override
|
||||||
|
public String typeDesc(String nameThis) {
|
||||||
|
return GenConsts.TDESC_JIT_PCODE_THREAD;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String varName;
|
||||||
|
private final int opcodeLoad;
|
||||||
|
private final int opcodeStore;
|
||||||
|
|
||||||
|
private InitFixedLocal(String varName, int opcodeLoad, int opcodeStore) {
|
||||||
|
this.varName = varName;
|
||||||
|
this.opcodeLoad = opcodeLoad;
|
||||||
|
this.opcodeStore = opcodeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int index() {
|
||||||
|
return ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String varName() {
|
||||||
|
return varName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int opcodeLoad() {
|
||||||
|
return opcodeLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int opcodeStore() {
|
||||||
|
return opcodeStore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locals that exist in every compiled passage's {@link JitCompiledPassage#run(int) run} method.
|
||||||
|
*/
|
||||||
|
public enum RunFixedLocal implements FixedLocal {
|
||||||
|
/**
|
||||||
|
* Because we're compiling a non-static method, the JVM reserves index 0 for {@code this}.
|
||||||
|
*/
|
||||||
|
THIS("this", ALOAD, ASTORE) {
|
||||||
|
@Override
|
||||||
|
public String typeDesc(String nameThis) {
|
||||||
|
return "L" + nameThis + ";";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* The parameter {@code blockId} is reserved by the JVM into index 1.
|
||||||
|
*/
|
||||||
|
BLOCK_ID("blockId", ILOAD, ISTORE) {
|
||||||
|
@Override
|
||||||
|
public String typeDesc(String nameThis) {
|
||||||
|
return Type.getDescriptor(int.class);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* We declare a local variable to indicate that a context-modifying userop has been invoked.
|
||||||
|
*/
|
||||||
|
CTXMOD("ctxmod", ILOAD, ISTORE) {
|
||||||
|
@Override
|
||||||
|
public String typeDesc(String nameThis) {
|
||||||
|
return Type.getDescriptor(boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDeclCode(MethodVisitor mv, String nameThis, Label startLocals,
|
||||||
|
Label endLocals) {
|
||||||
|
super.generateDeclCode(mv, nameThis, startLocals, endLocals);
|
||||||
|
mv.visitLdcInsn(0);
|
||||||
|
mv.visitVarInsn(ISTORE, index());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String varName;
|
||||||
|
private final int opcodeLoad;
|
||||||
|
private final int opcodeStore;
|
||||||
|
|
||||||
|
private RunFixedLocal(String varName, int opcodeLoad, int opcodeStore) {
|
||||||
|
this.varName = varName;
|
||||||
|
this.opcodeLoad = opcodeLoad;
|
||||||
|
this.opcodeStore = opcodeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All of the runtime locals
|
||||||
|
*/
|
||||||
|
public static final List<FixedLocal> ALL = List.of(values());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int index() {
|
||||||
|
return ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String varName() {
|
||||||
|
return varName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int opcodeLoad() {
|
||||||
|
return opcodeLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int opcodeStore() {
|
||||||
|
return opcodeStore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final JitDataFlowModel dfm;
|
private final JitDataFlowModel dfm;
|
||||||
private final JitVarScopeModel vsm;
|
private final JitVarScopeModel vsm;
|
||||||
private final JitTypeModel tm;
|
private final JitTypeModel tm;
|
||||||
@@ -654,7 +867,7 @@ public class JitAllocationModel {
|
|||||||
private final SleighLanguage language;
|
private final SleighLanguage language;
|
||||||
private final Endian endian;
|
private final Endian endian;
|
||||||
|
|
||||||
private int nextLocal = 2; // 0:this, 1:blockId in run(int blockId)
|
private int nextLocal = RunFixedLocal.ALL.size();
|
||||||
private final Map<JitVal, VarHandler> handlers = new HashMap<>();
|
private final Map<JitVal, VarHandler> handlers = new HashMap<>();
|
||||||
private final Map<Varnode, VarHandler> handlersPerVarnode = new HashMap<>();
|
private final Map<Varnode, VarHandler> handlersPerVarnode = new HashMap<>();
|
||||||
private final NavigableMap<Address, JvmLocal> locals = new TreeMap<>();
|
private final NavigableMap<Address, JvmLocal> locals = new TreeMap<>();
|
||||||
@@ -695,7 +908,7 @@ public class JitAllocationModel {
|
|||||||
else {
|
else {
|
||||||
nextLocal += 1;
|
nextLocal += 1;
|
||||||
}
|
}
|
||||||
return new JvmLocal(i, name, type, desc.toVarnode(language.getAddressFactory()));
|
return new JvmLocal(i, name, type, desc.toVarnode());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -730,7 +943,7 @@ public class JitAllocationModel {
|
|||||||
long offset = desc.offset;
|
long offset = desc.offset;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (SimpleJitType t : it) {
|
for (SimpleJitType t : it) {
|
||||||
VarDesc d = new VarDesc(desc.spaceId, offset, t.size(), t);
|
VarDesc d = new VarDesc(desc.spaceId, offset, t.size(), t, language);
|
||||||
result[i] = genFreeLocal(name + "_" + i, t, d);
|
result[i] = genFreeLocal(name + "_" + i, t, d);
|
||||||
offset += t.size();
|
offset += t.size();
|
||||||
i++;
|
i++;
|
||||||
@@ -852,6 +1065,9 @@ public class JitAllocationModel {
|
|||||||
if (v instanceof JitConstVal) {
|
if (v instanceof JitConstVal) {
|
||||||
return NoHandler.INSTANCE;
|
return NoHandler.INSTANCE;
|
||||||
}
|
}
|
||||||
|
if (v instanceof JitFailVal) {
|
||||||
|
return NoHandler.INSTANCE;
|
||||||
|
}
|
||||||
if (v instanceof JitMemoryVar) {
|
if (v instanceof JitMemoryVar) {
|
||||||
return NoHandler.INSTANCE;
|
return NoHandler.INSTANCE;
|
||||||
}
|
}
|
||||||
@@ -883,7 +1099,7 @@ public class JitAllocationModel {
|
|||||||
.stream()
|
.stream()
|
||||||
.sorted(Comparator.comparing(e -> e.getKey().getAddress()))
|
.sorted(Comparator.comparing(e -> e.getKey().getAddress()))
|
||||||
.toList()) {
|
.toList()) {
|
||||||
VarDesc desc = VarDesc.fromVarnode(entry.getKey(), entry.getValue().winner());
|
VarDesc desc = VarDesc.fromVarnode(entry.getKey(), entry.getValue().winner(), language);
|
||||||
switch (desc.type()) {
|
switch (desc.type()) {
|
||||||
case SimpleJitType t -> {
|
case SimpleJitType t -> {
|
||||||
locals.put(entry.getKey().getAddress(), genFreeLocal(desc.name(), t, desc));
|
locals.put(entry.getKey().getAddress(), genFreeLocal(desc.name(), t, desc));
|
||||||
|
|||||||
+12
-3
@@ -350,7 +350,7 @@ public class JitControlFlowModel {
|
|||||||
* external) that leave each block. We then compute all the branches (internal) that enter each
|
* external) that leave each block. We then compute all the branches (internal) that enter each
|
||||||
* block and the associated flows in both directions.
|
* block and the associated flows in both directions.
|
||||||
*/
|
*/
|
||||||
public static class BlockSplitter {
|
public abstract static class BlockSplitter {
|
||||||
private final PcodeProgram program;
|
private final PcodeProgram program;
|
||||||
|
|
||||||
private final Map<PcodeOp, Branch> branches = new HashMap<>();
|
private final Map<PcodeOp, Branch> branches = new HashMap<>();
|
||||||
@@ -373,6 +373,8 @@ public class JitControlFlowModel {
|
|||||||
this.program = program;
|
this.program = program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract IntBranch newFallthroughIntBranch(PcodeOp from, PcodeOp to);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the splitter of the given branches before analysis
|
* Notify the splitter of the given branches before analysis
|
||||||
*
|
*
|
||||||
@@ -418,7 +420,8 @@ public class JitControlFlowModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (needsFallthrough(lastBlock)) {
|
if (needsFallthrough(lastBlock)) {
|
||||||
lastBlock.branchesFrom.add(new IntBranch(lastBlock.getCode().getLast(), op, true));
|
lastBlock.branchesFrom
|
||||||
|
.add(newFallthroughIntBranch(lastBlock.getCode().getLast(), op));
|
||||||
}
|
}
|
||||||
lastBlock = null;
|
lastBlock = null;
|
||||||
}
|
}
|
||||||
@@ -536,7 +539,13 @@ public class JitControlFlowModel {
|
|||||||
* @return the resulting blocks, keyed by {@link JitBlock#first()}
|
* @return the resulting blocks, keyed by {@link JitBlock#first()}
|
||||||
*/
|
*/
|
||||||
protected SequencedMap<PcodeOp, JitBlock> analyze() {
|
protected SequencedMap<PcodeOp, JitBlock> analyze() {
|
||||||
BlockSplitter splitter = new BlockSplitter(passage);
|
BlockSplitter splitter = new BlockSplitter(passage) {
|
||||||
|
@Override
|
||||||
|
protected IntBranch newFallthroughIntBranch(PcodeOp from, PcodeOp to) {
|
||||||
|
// Decoder should already have inserted fall-through protectors
|
||||||
|
return new RIntBranch(from, to, true, Reachability.WITHOUT_CTXMOD);
|
||||||
|
}
|
||||||
|
};
|
||||||
splitter.addBranches(passage.getBranches().values());
|
splitter.addBranches(passage.getBranches().values());
|
||||||
return splitter.splitBlocks();
|
return splitter.splitBlocks();
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-8
@@ -18,9 +18,9 @@ package ghidra.pcode.emu.jit.analysis;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.Branch;
|
import ghidra.pcode.emu.jit.JitPassage.*;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.IndBranch;
|
|
||||||
import ghidra.pcode.emu.jit.op.*;
|
import ghidra.pcode.emu.jit.op.*;
|
||||||
|
import ghidra.pcode.emu.jit.var.JitFailVal;
|
||||||
import ghidra.pcode.emu.jit.var.JitVal;
|
import ghidra.pcode.emu.jit.var.JitVal;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
@@ -55,7 +55,7 @@ import ghidra.program.model.pcode.Varnode;
|
|||||||
*/
|
*/
|
||||||
class JitDataFlowExecutor extends PcodeExecutor<JitVal> {
|
class JitDataFlowExecutor extends PcodeExecutor<JitVal> {
|
||||||
private final JitDataFlowModel dfm;
|
private final JitDataFlowModel dfm;
|
||||||
private final Map<PcodeOp, Branch> branches;
|
private final Map<PcodeOp, PBranch> branches;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an executor from the given context
|
* Construct an executor from the given context
|
||||||
@@ -85,7 +85,7 @@ class JitDataFlowExecutor extends PcodeExecutor<JitVal> {
|
|||||||
* @param op the op
|
* @param op the op
|
||||||
*/
|
*/
|
||||||
protected void recordBranch(PcodeOp op) {
|
protected void recordBranch(PcodeOp op) {
|
||||||
Branch branch = Objects.requireNonNull(branches.get(op));
|
RBranch branch = (RBranch) Objects.requireNonNull(branches.get(op));
|
||||||
dfm.notifyOp(new JitBranchOp(op, branch));
|
dfm.notifyOp(new JitBranchOp(op, branch));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +100,17 @@ class JitDataFlowExecutor extends PcodeExecutor<JitVal> {
|
|||||||
* @param op the op
|
* @param op the op
|
||||||
*/
|
*/
|
||||||
protected void recordConditionalBranch(PcodeOp op) {
|
protected void recordConditionalBranch(PcodeOp op) {
|
||||||
Branch branch = Objects.requireNonNull(branches.get(op));
|
RBranch branch = (RBranch) Objects.requireNonNull(branches.get(op));
|
||||||
Varnode condVar = getConditionalBranchPredicate(op);
|
|
||||||
JitVal cond = state.getVar(condVar, reason);
|
final JitVal cond;
|
||||||
|
if (op instanceof ExitPcodeOp) {
|
||||||
|
cond = JitFailVal.INSTANCE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Varnode condVar = getConditionalBranchPredicate(op);
|
||||||
|
cond = state.getVar(condVar, reason);
|
||||||
|
}
|
||||||
|
|
||||||
dfm.notifyOp(new JitCBranchOp(op, branch, cond));
|
dfm.notifyOp(new JitCBranchOp(op, branch, cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +126,7 @@ class JitDataFlowExecutor extends PcodeExecutor<JitVal> {
|
|||||||
protected void recordIndirectBranch(PcodeOp op) {
|
protected void recordIndirectBranch(PcodeOp op) {
|
||||||
Varnode offVar = getIndirectBranchTarget(op);
|
Varnode offVar = getIndirectBranchTarget(op);
|
||||||
JitVal offset = state.getVar(offVar, reason);
|
JitVal offset = state.getVar(offVar, reason);
|
||||||
IndBranch branch = (IndBranch) Objects.requireNonNull(branches.get(op));
|
RIndBranch branch = (RIndBranch) Objects.requireNonNull(branches.get(op));
|
||||||
dfm.notifyOp(new JitBranchIndOp(op, offset, branch));
|
dfm.notifyOp(new JitBranchIndOp(op, offset, branch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
@@ -226,6 +226,11 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
|||||||
return decOp.hasSideEffects();
|
return decOp.hasSideEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean modifiesContext() {
|
||||||
|
return decOp.modifiesContext();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInlinePcode() {
|
public boolean canInlinePcode() {
|
||||||
return decOp.canInlinePcode();
|
return decOp.canInlinePcode();
|
||||||
|
|||||||
+10
-1
@@ -190,6 +190,7 @@ public interface JitOpVisitor {
|
|||||||
default void visitVal(JitVal v) {
|
default void visitVal(JitVal v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case JitConstVal constVal -> visitConstVal(constVal);
|
case JitConstVal constVal -> visitConstVal(constVal);
|
||||||
|
case JitFailVal failVal -> visitFailVal(failVal);
|
||||||
case JitVar jVar -> visitVar(jVar);
|
case JitVar jVar -> visitVar(jVar);
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
@@ -217,11 +218,19 @@ public interface JitOpVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visit a {@link JitConstVal}
|
* Visit a {@link JitConstVal}
|
||||||
*
|
*
|
||||||
* @param constVal the variable visited
|
* @param constVal the value visited
|
||||||
*/
|
*/
|
||||||
default void visitConstVal(JitConstVal constVal) {
|
default void visitConstVal(JitConstVal constVal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit a {@link JitFailVal}
|
||||||
|
*
|
||||||
|
* @param failVal the value visited
|
||||||
|
*/
|
||||||
|
default void visitFailVal(JitFailVal failVal) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit a {@link JitDirectMemoryVar}
|
* Visit a {@link JitDirectMemoryVar}
|
||||||
*
|
*
|
||||||
|
|||||||
+91
-23
@@ -27,6 +27,7 @@ import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.*;
|
|||||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowState;
|
import ghidra.pcode.emu.jit.analysis.JitDataFlowState;
|
||||||
import ghidra.pcode.emu.jit.op.JitNopOp;
|
import ghidra.pcode.emu.jit.op.JitNopOp;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
|
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||||
import ghidra.program.disassemble.Disassembler;
|
import ghidra.program.disassemble.Disassembler;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
@@ -79,7 +80,7 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
private final Map<Address, RegisterValue> futCtx = new HashMap<>();
|
private final Map<Address, RegisterValue> futCtx = new HashMap<>();
|
||||||
|
|
||||||
final List<PcodeOp> opsForThisStep = new ArrayList<>();
|
final List<PcodeOp> opsForThisStep = new ArrayList<>();
|
||||||
private final List<Branch> branchesForThisStep = new ArrayList<>();
|
private final List<SBranch> branchesForThisStep = new ArrayList<>();
|
||||||
|
|
||||||
private final Map<PcodeOp, DecodedPcodeOp> rewrites = new HashMap<>();
|
private final Map<PcodeOp, DecodedPcodeOp> rewrites = new HashMap<>();
|
||||||
|
|
||||||
@@ -311,7 +312,7 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void branchToAddress(PcodeOp op, Address target) {
|
protected void branchToAddress(PcodeOp op, Address target) {
|
||||||
branchesForThisStep.add(new ExtBranch(op, takeTargetContext(target)));
|
branchesForThisStep.add(new SExtBranch(op, takeTargetContext(target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -331,11 +332,11 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
if (termNop == null) {
|
if (termNop == null) {
|
||||||
termNop = new NopPcodeOp(at, tgtSeq);
|
termNop = new NopPcodeOp(at, tgtSeq);
|
||||||
}
|
}
|
||||||
branchesForThisStep.add(new IntBranch(op, termNop, false));
|
branchesForThisStep.add(new SIntBranch(op, termNop, false));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PcodeOp to = frame.getCode().get(op.getSeqnum().getTime() + relative);
|
PcodeOp to = frame.getCode().get(op.getSeqnum().getTime() + relative);
|
||||||
branchesForThisStep.add(new IntBranch(op, rewrite(to), false));
|
branchesForThisStep.add(new SIntBranch(op, rewrite(to), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,7 +352,7 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doExecuteIndirectBranch(PcodeOp op, PcodeFrame frame) {
|
protected void doExecuteIndirectBranch(PcodeOp op, PcodeFrame frame) {
|
||||||
branchesForThisStep.add(new IndBranch(op, flow));
|
branchesForThisStep.add(new SIndBranch(op, flow));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -450,9 +451,9 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
* reachability test between the two. The step has fall through if and only if a path is found.
|
* reachability test between the two. The step has fall through if and only if a path is found.
|
||||||
*
|
*
|
||||||
* @param from the instruction's or inject's p-code
|
* @param from the instruction's or inject's p-code
|
||||||
* @return true if the step falls through.
|
* @return the reachability of the fall-through flow
|
||||||
*/
|
*/
|
||||||
public boolean checkFallthroughAndAccumulate(PcodeProgram from) {
|
public Reachability checkFallthroughAndAccumulate(PcodeProgram from) {
|
||||||
if (instruction instanceof DecodeErrorInstruction) {
|
if (instruction instanceof DecodeErrorInstruction) {
|
||||||
stride.opsForStride.addAll(opsForThisStep);
|
stride.opsForStride.addAll(opsForThisStep);
|
||||||
for (Branch branch : branchesForThisStep) {
|
for (Branch branch : branchesForThisStep) {
|
||||||
@@ -461,28 +462,37 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
if (opsForThisStep.isEmpty()) {
|
if (opsForThisStep.isEmpty()) {
|
||||||
return true;
|
return Reachability.WITHOUT_CTXMOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitPcodeOp probeOp = new ExitPcodeOp(AddrCtx.NOWHERE);
|
ExitPcodeOp probeOp = ExitPcodeOp.exit(AddrCtx.NOWHERE);
|
||||||
opsForThisStep.add(probeOp);
|
opsForThisStep.add(probeOp);
|
||||||
ExtBranch probeBranch = new ExtBranch(probeOp, AddrCtx.NOWHERE);
|
SExtBranch probeBranch = new SExtBranch(probeOp, AddrCtx.NOWHERE);
|
||||||
branchesForThisStep.add(probeBranch);
|
branchesForThisStep.add(probeBranch);
|
||||||
|
|
||||||
PcodeProgram program = new PcodeProgram(from, opsForThisStep);
|
PcodeProgram program = new PcodeProgram(from, opsForThisStep);
|
||||||
BlockSplitter splitter = new BlockSplitter(program);
|
BlockSplitter splitter = new BlockSplitter(program) {
|
||||||
|
@Override
|
||||||
|
protected IntBranch newFallthroughIntBranch(PcodeOp from, PcodeOp to) {
|
||||||
|
return new SIntBranch(from, to, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
splitter.addBranches(branchesForThisStep);
|
splitter.addBranches(branchesForThisStep);
|
||||||
SequencedMap<PcodeOp, JitBlock> blocks = splitter.splitBlocks();
|
SequencedMap<PcodeOp, JitBlock> blocks = splitter.splitBlocks();
|
||||||
JitBlock entry = blocks.firstEntry().getValue();
|
JitBlock entry = blocks.firstEntry().getValue();
|
||||||
JitBlock exit = blocks.lastEntry().getValue();
|
JitBlock exit = blocks.lastEntry().getValue();
|
||||||
|
|
||||||
Set<JitBlock> reachable = new HashSet<>();
|
Map<JitBlock, Reachability> reachable = new HashMap<>();
|
||||||
collectReachable(reachable, entry);
|
collectReachable(reachable, entry, Reachability.WITHOUT_CTXMOD);
|
||||||
|
|
||||||
for (JitBlock block : blocks.values()) {
|
for (JitBlock block : blocks.values()) {
|
||||||
|
Reachability reach = reachable.get(block);
|
||||||
|
if (reach == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (PcodeOp op : block.getCode()) {
|
for (PcodeOp op : block.getCode()) {
|
||||||
if (op != probeOp) {
|
if (op != probeOp) {
|
||||||
stride.opsForStride.add(op);
|
stride.opsForStride.add(op);
|
||||||
@@ -490,37 +500,95 @@ class DecoderExecutor extends PcodeExecutor<Object>
|
|||||||
}
|
}
|
||||||
for (IntBranch branch : block.branchesFrom()) {
|
for (IntBranch branch : block.branchesFrom()) {
|
||||||
if (!branch.isFall()) {
|
if (!branch.isFall()) {
|
||||||
stride.passage.internalBranches.put(branch.from(), branch);
|
switch (branch) {
|
||||||
|
case SIntBranch ib -> stride.passage.internalBranches.put(ib.from(),
|
||||||
|
ib.withReach(reach));
|
||||||
|
default -> throw new AssertionError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Branch branch : block.branchesOut()) {
|
for (Branch branch : block.branchesOut()) {
|
||||||
if (branch != probeBranch) {
|
if (branch != probeBranch) {
|
||||||
switch (branch) {
|
switch (branch) {
|
||||||
case ExtBranch eb -> stride.passage.flowTo(eb);
|
case SExtBranch eb -> stride.passage.flowTo(eb.withReach(reach));
|
||||||
default -> stride.passage.otherBranches.put(branch.from(), branch);
|
case SIndBranch ib -> stride.passage.otherBranches.put(ib.from(),
|
||||||
|
ib.withReach(reach));
|
||||||
|
case PBranch pb -> stride.passage.otherBranches.put(pb.from(), pb);
|
||||||
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reachable.contains(exit);
|
return reachable.get(exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean blockModifiesContext(JitBlock block) {
|
||||||
|
for (PcodeOp op : block.getCode()) {
|
||||||
|
if (op.getOpcode() != PcodeOp.CALLOTHER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String name = block.getUseropName(getCallotherOpNumber(op));
|
||||||
|
if (name == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PcodeUseropDefinition<Object> userop = stride.passage.library().getUserops().get(name);
|
||||||
|
if (userop == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (userop.modifiesContext()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reachability test mentioned in {@link #checkFallthroughAndAccumulate(PcodeProgram)}
|
* The reachability test mentioned in {@link #checkFallthroughAndAccumulate(PcodeProgram)}
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Collects the set of blocks reachable from {@code cur} into the given mutable set.
|
* Collects the reachability of blocks reachable from {@code cur} into the given mutable map.
|
||||||
|
* The value indicates whether or not context modifications can occur along the paths to the
|
||||||
|
* block (key). If a block is not in the map, it is not reachable.
|
||||||
*
|
*
|
||||||
* @param into a mutable set for collecting reachable blocks
|
* @param into a mutable map for collecting reachable blocks
|
||||||
* @param cur the source block, or an intermediate during recursion
|
* @param cur the source block, or an intermediate during recursion
|
||||||
|
* @param the computed reachability of the source block. Use {@link Reachability#WITHOUT_CTXMOD}
|
||||||
|
* for the seed.
|
||||||
*/
|
*/
|
||||||
private void collectReachable(Set<JitBlock> into, JitBlock cur) {
|
private void collectReachable(Map<JitBlock, Reachability> into, JitBlock cur,
|
||||||
if (!into.add(cur)) {
|
Reachability how) {
|
||||||
|
Reachability curHow = into.get(cur);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-modifying userops are all considered hazards, but we shouldn't abort until after
|
||||||
|
* the instruction. If the exit is reachable without passing through a context modification,
|
||||||
|
* then we're good to proceed. Otherwise, no. Additionally, we're going to check all
|
||||||
|
* branches, direct or indirect, to see if they are reachable without context modification.
|
||||||
|
* If they are, then we treat them as usual. If not, then they will be treated as indirect,
|
||||||
|
* and we'll neglect to "retire" the context, because presumably, the userop will already
|
||||||
|
* have caused that retirement and modified it in place.
|
||||||
|
*
|
||||||
|
* If one branch is reachable by multiple paths where some require context modification and
|
||||||
|
* some do not, we'll keep a local variable at runtime to track whether a context-modifying
|
||||||
|
* userop has actually been executed. We'll generate code to check this variable at the
|
||||||
|
* branch site and treat it as a hazard if it is set.
|
||||||
|
*/
|
||||||
|
if (blockModifiesContext(cur)) {
|
||||||
|
// Not combine. If we're MAYBE here, we still become WITH_CTX.
|
||||||
|
how = Reachability.WITH_CTXMOD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
how = how.combine(curHow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (how == curHow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
into.put(cur, how);
|
||||||
|
|
||||||
for (BlockFlow flow : cur.flowsFrom().values()) {
|
for (BlockFlow flow : cur.flowsFrom().values()) {
|
||||||
collectReachable(into, flow.to());
|
collectReachable(into, flow.to(), how);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-15
@@ -42,9 +42,10 @@ class DecoderForOnePassage {
|
|||||||
private final int maxInstrs;
|
private final int maxInstrs;
|
||||||
private final int maxStrides;
|
private final int maxStrides;
|
||||||
|
|
||||||
final Map<PcodeOp, IntBranch> internalBranches = new HashMap<>();
|
final Map<PcodeOp, RIntBranch> internalBranches = new HashMap<>();
|
||||||
final SequencedMap<PcodeOp, ExtBranch> externalBranches = new LinkedHashMap<>();
|
// Sequenced, because this is also the seed queue
|
||||||
final Map<PcodeOp, Branch> otherBranches = new HashMap<>();
|
final SequencedMap<PcodeOp, RExtBranch> externalBranches = new LinkedHashMap<>();
|
||||||
|
final Map<PcodeOp, PBranch> otherBranches = new HashMap<>();
|
||||||
final Map<AddrCtx, PcodeOp> firstOps = new HashMap<>();
|
final Map<AddrCtx, PcodeOp> firstOps = new HashMap<>();
|
||||||
final List<DecodedStride> strides = new ArrayList<>();
|
final List<DecodedStride> strides = new ArrayList<>();
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class DecoderForOnePassage {
|
|||||||
this.maxInstrs = config.maxPassageInstructions();
|
this.maxInstrs = config.maxPassageInstructions();
|
||||||
this.maxStrides = config.maxPassageStrides();
|
this.maxStrides = config.maxPassageStrides();
|
||||||
EntryPcodeOp entryOp = new EntryPcodeOp(seed);
|
EntryPcodeOp entryOp = new EntryPcodeOp(seed);
|
||||||
externalBranches.put(entryOp, new ExtBranch(entryOp, seed));
|
externalBranches.put(entryOp, new RExtBranch(entryOp, seed, Reachability.WITHOUT_CTXMOD));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,20 +76,23 @@ class DecoderForOnePassage {
|
|||||||
void decodePassage() {
|
void decodePassage() {
|
||||||
while (opCount < maxOps && instructionCount < maxInstrs &&
|
while (opCount < maxOps && instructionCount < maxInstrs &&
|
||||||
strides.size() < maxStrides) {
|
strides.size() < maxStrides) {
|
||||||
Entry<PcodeOp, ExtBranch> nextEnt = externalBranches.pollFirstEntry();
|
Entry<PcodeOp, RExtBranch> nextEnt = externalBranches.pollFirstEntry();
|
||||||
if (nextEnt == null) {
|
if (nextEnt == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ExtBranch next = nextEnt.getValue();
|
RExtBranch next = nextEnt.getValue();
|
||||||
AddrCtx start = next.to();
|
AddrCtx start = next.to();
|
||||||
|
|
||||||
if (decoder.thread.hasEntry(start)) {
|
if (decoder.thread.hasEntry(start)) {
|
||||||
otherBranches.put(next.from(), next);
|
otherBranches.put(next.from(), next);
|
||||||
}
|
}
|
||||||
|
else if (!next.reach().canReachWithoutCtxMod()) {
|
||||||
|
otherBranches.put(next.from(), next);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
decodeStride(start);
|
decodeStride(start);
|
||||||
PcodeOp to = Objects.requireNonNull(firstOps.get(start));
|
PcodeOp to = Objects.requireNonNull(firstOps.get(start));
|
||||||
internalBranches.put(next.from(), new IntBranch(next.from(), to, false));
|
internalBranches.put(next.from(), next.toIntBranch(to));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,10 +111,14 @@ class DecoderForOnePassage {
|
|||||||
* @param from the op representing or causing the control flow
|
* @param from the op representing or causing the control flow
|
||||||
* @param to the target of the branch
|
* @param to the target of the branch
|
||||||
*/
|
*/
|
||||||
void flowTo(ExtBranch eb) {
|
void flowTo(RExtBranch eb) {
|
||||||
if (firstOps.containsKey(eb.to())) {
|
if (!eb.reach().canReachWithoutCtxMod()) {
|
||||||
IntBranch ib = new IntBranch(eb.from(), firstOps.get(eb.to()), false);
|
otherBranches.put(eb.from(), eb);
|
||||||
internalBranches.put(ib.from(), ib);
|
return;
|
||||||
|
}
|
||||||
|
PcodeOp to = firstOps.get(eb.to());
|
||||||
|
if (to != null) {
|
||||||
|
internalBranches.put(eb.from(), eb.toIntBranch(to));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
externalBranches.put(eb.from(), eb);
|
externalBranches.put(eb.from(), eb);
|
||||||
@@ -144,11 +152,15 @@ class DecoderForOnePassage {
|
|||||||
List<PcodeOp> code = strides.stream().flatMap(b -> b.ops().stream()).toList();
|
List<PcodeOp> code = strides.stream().flatMap(b -> b.ops().stream()).toList();
|
||||||
List<Instruction> instructions =
|
List<Instruction> instructions =
|
||||||
strides.stream().flatMap(b -> b.instructions().stream()).toList();
|
strides.stream().flatMap(b -> b.instructions().stream()).toList();
|
||||||
Map<PcodeOp, Branch> branches = otherBranches;
|
Map<PcodeOp, PBranch> branches = otherBranches;
|
||||||
branches.putAll(internalBranches);
|
branches.putAll(internalBranches);
|
||||||
for (ExtBranch eb : externalBranches.values()) {
|
for (RExtBranch eb : externalBranches.values()) {
|
||||||
if (firstOps.containsKey(eb.to())) {
|
if (!eb.reach().canReachWithoutCtxMod()) {
|
||||||
branches.put(eb.from(), new IntBranch(eb.from(), firstOps.get(eb.to()), false));
|
branches.put(eb.from(), eb);
|
||||||
|
}
|
||||||
|
PcodeOp to = firstOps.get(eb.to());
|
||||||
|
if (to != null) {
|
||||||
|
branches.put(eb.from(), eb.toIntBranch(to));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
branches.put(eb.from(), eb);
|
branches.put(eb.from(), eb);
|
||||||
|
|||||||
+32
-7
@@ -50,10 +50,10 @@ public class DecoderForOneStride {
|
|||||||
* Check whether the result falls through, accumulate its instructions and ops, and apply
|
* Check whether the result falls through, accumulate its instructions and ops, and apply
|
||||||
* any control-flow effects.
|
* any control-flow effects.
|
||||||
*
|
*
|
||||||
* @return true if the result falls through.
|
* @return the reachability of the fall-through flow
|
||||||
* @see DecoderExecutor#checkFallthroughAndAccumulate(PcodeProgram)
|
* @see DecoderExecutor#checkFallthroughAndAccumulate(PcodeProgram)
|
||||||
*/
|
*/
|
||||||
boolean checkFallthroughAndAccumulate() {
|
Reachability checkFallthroughAndAccumulate() {
|
||||||
return executor.checkFallthroughAndAccumulate(program);
|
return executor.checkFallthroughAndAccumulate(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,9 +123,10 @@ public class DecoderForOneStride {
|
|||||||
* exit branch.
|
* exit branch.
|
||||||
*/
|
*/
|
||||||
if (decoder.thread.hasEntry(at)) {
|
if (decoder.thread.hasEntry(at)) {
|
||||||
ExitPcodeOp exitOp = new ExitPcodeOp(at);
|
ExitPcodeOp exitOp = ExitPcodeOp.exit(at);
|
||||||
opsForStride.add(exitOp);
|
opsForStride.add(exitOp);
|
||||||
passage.otherBranches.put(exitOp, new ExtBranch(exitOp, at));
|
passage.otherBranches.put(exitOp,
|
||||||
|
new RExtBranch(exitOp, at, Reachability.WITHOUT_CTXMOD));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,19 +164,43 @@ public class DecoderForOneStride {
|
|||||||
|
|
||||||
StepResult result = stepAddrCtx(at);
|
StepResult result = stepAddrCtx(at);
|
||||||
|
|
||||||
if (result == null || !result.checkFallthroughAndAccumulate()) {
|
if (result == null) {
|
||||||
|
return toStride();
|
||||||
|
}
|
||||||
|
|
||||||
|
Reachability reach = result.checkFallthroughAndAccumulate();
|
||||||
|
if (reach == null) {
|
||||||
return toStride();
|
return toStride();
|
||||||
}
|
}
|
||||||
|
|
||||||
AddrCtx next = result.next();
|
AddrCtx next = result.next();
|
||||||
if (at.equals(next)) {
|
if (at.equals(next)) {
|
||||||
// Would happen because of inject without control flow
|
// Would happen because of inject without control flow
|
||||||
ExitPcodeOp exitOp = new ExitPcodeOp(at);
|
ExitPcodeOp exitOp = ExitPcodeOp.exit(at);
|
||||||
opsForStride.add(exitOp);
|
opsForStride.add(exitOp);
|
||||||
passage.otherBranches.put(exitOp, new ExtBranch(exitOp, at));
|
passage.otherBranches.put(exitOp, new RExtBranch(exitOp, at, reach));
|
||||||
return toStride();
|
return toStride();
|
||||||
}
|
}
|
||||||
at = next;
|
at = next;
|
||||||
|
|
||||||
|
switch (reach) {
|
||||||
|
case WITHOUT_CTXMOD -> {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case WITH_CTXMOD -> {
|
||||||
|
// Looks like the without-control-flow case, but at has advanced
|
||||||
|
ExitPcodeOp exitOp = ExitPcodeOp.exit(at);
|
||||||
|
opsForStride.add(exitOp);
|
||||||
|
passage.otherBranches.put(exitOp, new RExtBranch(exitOp, at, reach));
|
||||||
|
return toStride();
|
||||||
|
}
|
||||||
|
case MAYBE_CTXMOD -> {
|
||||||
|
ExitPcodeOp exitOp = ExitPcodeOp.cond(at);
|
||||||
|
opsForStride.add(exitOp);
|
||||||
|
passage.otherBranches.put(exitOp, new RExtBranch(exitOp, at, reach));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+5
@@ -118,6 +118,11 @@ public class DecoderUseropLibrary extends AnnotatedPcodeUseropLibrary<Object> {
|
|||||||
return rtOp.hasSideEffects();
|
return rtOp.hasSideEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean modifiesContext() {
|
||||||
|
return rtOp.modifiesContext();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInlinePcode() {
|
public boolean canInlinePcode() {
|
||||||
return rtOp.canInlinePcode();
|
return rtOp.canInlinePcode();
|
||||||
|
|||||||
+4
-2
@@ -22,6 +22,8 @@ import org.objectweb.asm.ClassVisitor;
|
|||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
|
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +65,7 @@ public record FieldForArrDirect(Address address) implements InstanceFieldReq {
|
|||||||
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_BYTE_ARR, null, null);
|
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_BYTE_ARR, null, null);
|
||||||
|
|
||||||
// [...]
|
// [...]
|
||||||
iv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||||
// [...,this]
|
// [...,this]
|
||||||
gen.generateLoadJitStateSpace(address.getAddressSpace(), iv);
|
gen.generateLoadJitStateSpace(address.getAddressSpace(), iv);
|
||||||
// [...,jitspace]
|
// [...,jitspace]
|
||||||
@@ -78,7 +80,7 @@ public record FieldForArrDirect(Address address) implements InstanceFieldReq {
|
|||||||
@Override
|
@Override
|
||||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||||
// [...]
|
// [...]
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
// [...,this]
|
// [...,this]
|
||||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
|
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
|
||||||
TDESC_BYTE_ARR);
|
TDESC_BYTE_ARR);
|
||||||
|
|||||||
+4
-2
@@ -24,6 +24,8 @@ import org.objectweb.asm.MethodVisitor;
|
|||||||
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.ExtBranch;
|
import ghidra.pcode.emu.jit.JitPassage.ExtBranch;
|
||||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
|
||||||
@@ -75,7 +77,7 @@ public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq {
|
|||||||
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_EXIT_SLOT, null, null);
|
cv.visitField(ACC_PRIVATE | ACC_FINAL, name(), TDESC_EXIT_SLOT, null, null);
|
||||||
|
|
||||||
// []
|
// []
|
||||||
iv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||||
// [this]
|
// [this]
|
||||||
iv.visitInsn(DUP);
|
iv.visitInsn(DUP);
|
||||||
// [this,this]
|
// [this,this]
|
||||||
@@ -93,7 +95,7 @@ public record FieldForExitSlot(AddrCtx target) implements InstanceFieldReq {
|
|||||||
@Override
|
@Override
|
||||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||||
// []
|
// []
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
// [this]
|
// [this]
|
||||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_EXIT_SLOT);
|
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_EXIT_SLOT);
|
||||||
// [slot]
|
// [slot]
|
||||||
|
|||||||
+4
-2
@@ -22,6 +22,8 @@ import org.objectweb.asm.ClassVisitor;
|
|||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
|
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +65,7 @@ public record FieldForSpaceIndirect(AddressSpace space) implements InstanceField
|
|||||||
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE, null, null);
|
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE, null, null);
|
||||||
|
|
||||||
// [...]
|
// [...]
|
||||||
iv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||||
// [...,this]
|
// [...,this]
|
||||||
gen.generateLoadJitStateSpace(space, iv);
|
gen.generateLoadJitStateSpace(space, iv);
|
||||||
// [...,this,jitspace]
|
// [...,this,jitspace]
|
||||||
@@ -75,7 +77,7 @@ public record FieldForSpaceIndirect(AddressSpace space) implements InstanceField
|
|||||||
@Override
|
@Override
|
||||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||||
// [...]
|
// [...]
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
// [...,this]
|
// [...,this]
|
||||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
|
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(),
|
||||||
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
|
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE_SPACE);
|
||||||
|
|||||||
+4
-2
@@ -21,6 +21,8 @@ import static org.objectweb.asm.Opcodes.*;
|
|||||||
import org.objectweb.asm.ClassVisitor;
|
import org.objectweb.asm.ClassVisitor;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.InitFixedLocal;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
|
import ghidra.pcode.emu.jit.analysis.JitDataFlowUseropLibrary;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||||
@@ -63,7 +65,7 @@ public record FieldForUserop(PcodeUseropDefinition<?> userop) implements Instanc
|
|||||||
null);
|
null);
|
||||||
|
|
||||||
// []
|
// []
|
||||||
iv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(iv);
|
||||||
// [this]
|
// [this]
|
||||||
iv.visitInsn(DUP);
|
iv.visitInsn(DUP);
|
||||||
// [this,this]
|
// [this,this]
|
||||||
@@ -79,7 +81,7 @@ public record FieldForUserop(PcodeUseropDefinition<?> userop) implements Instanc
|
|||||||
@Override
|
@Override
|
||||||
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, MethodVisitor rv) {
|
||||||
// []
|
// []
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
// [this]
|
// [this]
|
||||||
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_PCODE_USEROP_DEFINITION);
|
rv.visitFieldInsn(GETFIELD, gen.nameThis, name(), TDESC_PCODE_USEROP_DEFINITION);
|
||||||
// [userop]
|
// [userop]
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ public interface GenConsts {
|
|||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
|
||||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.LONG_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__RETIRE_COUNTER_AND_CONTEXT =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_COUNTER_AND_CONTEXT =
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
|
||||||
|
public static final String MDESC_JIT_COMPILED_PASSAGE__SET_COUNTER_AND_CONTEXT =
|
||||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(RegisterValue.class));
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_INT_RAW =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_INT_RAW =
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||||
|
|||||||
+51
-24
@@ -32,8 +32,7 @@ import ghidra.pcode.emu.jit.JitCompiler.Diag;
|
|||||||
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
import ghidra.pcode.emu.jit.JitPassage.AddrCtx;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
||||||
import ghidra.pcode.emu.jit.analysis.*;
|
import ghidra.pcode.emu.jit.analysis.*;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmLocal;
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.*;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.VarHandler;
|
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.gen.op.OpGen;
|
import ghidra.pcode.emu.jit.gen.op.OpGen;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
@@ -303,7 +302,7 @@ public class JitCodeGenerator {
|
|||||||
Type.getMethodDescriptor(Type.getType(JitPcodeThread.class)), null, null);
|
Type.getMethodDescriptor(Type.getType(JitPcodeThread.class)), null, null);
|
||||||
gtMv.visitCode();
|
gtMv.visitCode();
|
||||||
// []
|
// []
|
||||||
gtMv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(gtMv);
|
||||||
// [this]
|
// [this]
|
||||||
gtMv.visitFieldInsn(GETFIELD, nameThis, "thread", TDESC_JIT_PCODE_THREAD);
|
gtMv.visitFieldInsn(GETFIELD, nameThis, "thread", TDESC_JIT_PCODE_THREAD);
|
||||||
// [thread]
|
// [thread]
|
||||||
@@ -408,7 +407,7 @@ public class JitCodeGenerator {
|
|||||||
initMv.visitCode();
|
initMv.visitCode();
|
||||||
// Object.super()
|
// Object.super()
|
||||||
// []
|
// []
|
||||||
initMv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(initMv);
|
||||||
// [this]
|
// [this]
|
||||||
initMv.visitMethodInsn(INVOKESPECIAL, NAME_OBJECT, "<init>",
|
initMv.visitMethodInsn(INVOKESPECIAL, NAME_OBJECT, "<init>",
|
||||||
Type.getMethodDescriptor(Type.VOID_TYPE), false);
|
Type.getMethodDescriptor(Type.VOID_TYPE), false);
|
||||||
@@ -416,18 +415,18 @@ public class JitCodeGenerator {
|
|||||||
|
|
||||||
// this.thread = thread
|
// this.thread = thread
|
||||||
// []
|
// []
|
||||||
initMv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(initMv);
|
||||||
// [this]
|
// [this]
|
||||||
initMv.visitVarInsn(ALOAD, 1);
|
InitFixedLocal.THREAD.generateLoadCode(initMv);
|
||||||
// [this,state]
|
// [this,thread]
|
||||||
initMv.visitFieldInsn(PUTFIELD, nameThis, "thread", TDESC_JIT_PCODE_THREAD);
|
initMv.visitFieldInsn(PUTFIELD, nameThis, "thread", TDESC_JIT_PCODE_THREAD);
|
||||||
// []
|
// []
|
||||||
|
|
||||||
// this.state = thread.getState()
|
// this.state = thread.getState()
|
||||||
// []
|
// []
|
||||||
initMv.visitVarInsn(ALOAD, 0);
|
InitFixedLocal.THIS.generateLoadCode(initMv);
|
||||||
// [this]
|
// [this]
|
||||||
initMv.visitVarInsn(ALOAD, 1);
|
InitFixedLocal.THREAD.generateLoadCode(initMv);
|
||||||
// [this,thread]
|
// [this,thread]
|
||||||
initMv.visitMethodInsn(INVOKEVIRTUAL, NAME_JIT_PCODE_THREAD, "getState",
|
initMv.visitMethodInsn(INVOKEVIRTUAL, NAME_JIT_PCODE_THREAD, "getState",
|
||||||
MDESC_JIT_PCODE_THREAD__GET_STATE, false);
|
MDESC_JIT_PCODE_THREAD__GET_STATE, false);
|
||||||
@@ -453,8 +452,7 @@ public class JitCodeGenerator {
|
|||||||
* this.spaceInd_`space` =
|
* this.spaceInd_`space` =
|
||||||
* this.state.getForSpace(ADDRESS_FACTORY.getAddressSpace(`space.getSpaceID()`);
|
* this.state.getForSpace(ADDRESS_FACTORY.getAddressSpace(`space.getSpaceID()`);
|
||||||
*/
|
*/
|
||||||
|
InitFixedLocal.THIS.generateLoadCode(initMv);
|
||||||
iv.visitVarInsn(ALOAD, 0);
|
|
||||||
// [...,this]
|
// [...,this]
|
||||||
iv.visitFieldInsn(GETFIELD, nameThis, "state",
|
iv.visitFieldInsn(GETFIELD, nameThis, "state",
|
||||||
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE);
|
TDESC_JIT_BYTES_PCODE_EXECUTOR_STATE);
|
||||||
@@ -726,7 +724,7 @@ public class JitCodeGenerator {
|
|||||||
requestExceptionHandler(first, block).label(), NAME_THROWABLE);
|
requestExceptionHandler(first, block).label(), NAME_THROWABLE);
|
||||||
|
|
||||||
runMv.visitLabel(tryStart);
|
runMv.visitLabel(tryStart);
|
||||||
runMv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(runMv);
|
||||||
runMv.visitLdcInsn(block.instructionCount());
|
runMv.visitLdcInsn(block.instructionCount());
|
||||||
runMv.visitLdcInsn(block.trailingOpCount());
|
runMv.visitLdcInsn(block.trailingOpCount());
|
||||||
runMv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "count",
|
runMv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "count",
|
||||||
@@ -862,9 +860,9 @@ public class JitCodeGenerator {
|
|||||||
runMv.visitCode();
|
runMv.visitCode();
|
||||||
runMv.visitLabel(startLocals);
|
runMv.visitLabel(startLocals);
|
||||||
|
|
||||||
runMv.visitLocalVariable("this", "L" + nameThis + ";", null, startLocals, endLocals, 0);
|
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
||||||
runMv.visitLocalVariable("blockId", Type.getDescriptor(int.class), null, startLocals,
|
fixed.generateDeclCode(runMv, nameThis, startLocals, endLocals);
|
||||||
endLocals, 1);
|
}
|
||||||
|
|
||||||
for (JvmLocal local : am.allLocals()) {
|
for (JvmLocal local : am.allLocals()) {
|
||||||
local.generateDeclCode(this, startLocals, endLocals, runMv);
|
local.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||||
@@ -889,7 +887,7 @@ public class JitCodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// []
|
// []
|
||||||
runMv.visitVarInsn(ILOAD, 1);
|
RunFixedLocal.BLOCK_ID.generateLoadCode(runMv);
|
||||||
// [blockId]
|
// [blockId]
|
||||||
Label lblBadEntry = new Label();
|
Label lblBadEntry = new Label();
|
||||||
runMv.visitTableSwitchInsn(0, entries.size() - 1, lblBadEntry,
|
runMv.visitTableSwitchInsn(0, entries.size() - 1, lblBadEntry,
|
||||||
@@ -1041,6 +1039,32 @@ public class JitCodeGenerator {
|
|||||||
throw new AssertionError("Couldn't figure exit context for " + op);
|
throw new AssertionError("Couldn't figure exit context for " + op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The manners in which the program counter and decode context can be "retired."
|
||||||
|
*/
|
||||||
|
public enum RetireMode {
|
||||||
|
/**
|
||||||
|
* Retire into the emulator's counter/context and its machine state
|
||||||
|
*
|
||||||
|
* @see JitCompiledPassage#writeCounterAndContext(long, RegisterValue)
|
||||||
|
*/
|
||||||
|
WRITE(MDESC_JIT_COMPILED_PASSAGE__WRITE_COUNTER_AND_CONTEXT, "writeCounterAndContext"),
|
||||||
|
/**
|
||||||
|
* Retire into the emulator's counter/context, but not its machine state
|
||||||
|
*
|
||||||
|
* @see JitCompiledPassage#setCounterAndContext(long, RegisterValue)
|
||||||
|
*/
|
||||||
|
SET(MDESC_JIT_COMPILED_PASSAGE__SET_COUNTER_AND_CONTEXT, "setCounterAndContext");
|
||||||
|
|
||||||
|
private String mdesc;
|
||||||
|
private String mname;
|
||||||
|
|
||||||
|
private RetireMode(String mdesc, String mname) {
|
||||||
|
this.mdesc = mdesc;
|
||||||
|
this.mname = mname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit bytecode to set the emulator's counter and contextreg.
|
* Emit bytecode to set the emulator's counter and contextreg.
|
||||||
*
|
*
|
||||||
@@ -1057,12 +1081,13 @@ public class JitCodeGenerator {
|
|||||||
* branch target, which may be loaded from a varnode for an indirect branch.
|
* branch target, which may be loaded from a varnode for an indirect branch.
|
||||||
* @param ctx the contextreg value. For errors, this is the decode context of the op causing the
|
* @param ctx the contextreg value. For errors, this is the decode context of the op causing the
|
||||||
* error. For branches, this is the decode context at the target.
|
* error. For branches, this is the decode context at the target.
|
||||||
|
* @param mode whether to set the machine state, too
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
*/
|
*/
|
||||||
public void generateRetirePcCtx(Runnable pcGen, RegisterValue ctx,
|
public void generateRetirePcCtx(Runnable pcGen, RegisterValue ctx, RetireMode mode,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
// []
|
// []
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
// [this]
|
// [this]
|
||||||
pcGen.run();
|
pcGen.run();
|
||||||
// [this,pc:LONG]
|
// [this,pc:LONG]
|
||||||
@@ -1073,8 +1098,8 @@ public class JitCodeGenerator {
|
|||||||
requestStaticFieldForContext(ctx).generateLoadCode(this, rv);
|
requestStaticFieldForContext(ctx).generateLoadCode(this, rv);
|
||||||
}
|
}
|
||||||
// [this,pc:LONG,ctx:RV]
|
// [this,pc:LONG,ctx:RV]
|
||||||
rv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "retireCounterAndContext",
|
rv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, mode.mname,
|
||||||
MDESC_JIT_COMPILED_PASSAGE__RETIRE_COUNTER_AND_CONTEXT, true);
|
mode.mdesc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1082,18 +1107,20 @@ public class JitCodeGenerator {
|
|||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This retires all the variables of the current block as well as the program counter and decode
|
* This retires all the variables of the current block as well as the program counter and decode
|
||||||
* coontext. It does not generate the actual {@link Opcodes#ARETURN areturn} or
|
* context. It does not generate the actual {@link Opcodes#ARETURN areturn} or
|
||||||
* {@link Opcodes#ATHROW athrow}, but everything required up to that point.
|
* {@link Opcodes#ATHROW athrow}, but everything required up to that point.
|
||||||
*
|
*
|
||||||
* @param block the block containing the op at which we are exiting
|
* @param block the block containing the op at which we are exiting
|
||||||
* @param pcGen as in {@link #generateRetirePcCtx(Runnable, RegisterValue, MethodVisitor)}
|
* @param pcGen as in
|
||||||
* @param ctx as in {@link #generateRetirePcCtx(Runnable, RegisterValue, MethodVisitor)}
|
* {@link #generateRetirePcCtx(Runnable, RegisterValue, RetireMode, MethodVisitor)}
|
||||||
|
* @param ctx as in
|
||||||
|
* {@link #generateRetirePcCtx(Runnable, RegisterValue, RetireMode, MethodVisitor)}
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
*/
|
*/
|
||||||
public void generatePassageExit(JitBlock block, Runnable pcGen, RegisterValue ctx,
|
public void generatePassageExit(JitBlock block, Runnable pcGen, RegisterValue ctx,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
VarGen.computeBlockTransition(this, block, null).generate(rv);
|
VarGen.computeBlockTransition(this, block, null).generate(rv);
|
||||||
generateRetirePcCtx(pcGen, ctx, rv);
|
generateRetirePcCtx(pcGen, ctx, RetireMode.WRITE, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+48
-4
@@ -17,13 +17,17 @@ package ghidra.pcode.emu.jit.gen.op;
|
|||||||
|
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.JitPassage.RIndBranch;
|
||||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
|
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.BranchGen;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
import ghidra.pcode.emu.jit.op.JitBranchIndOp;
|
import ghidra.pcode.emu.jit.op.JitBranchIndOp;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The generator for a {@link JitBranchIndOp branchind}.
|
* The generator for a {@link JitBranchIndOp branchind}.
|
||||||
@@ -37,18 +41,58 @@ public enum BranchIndOpGen implements OpGen<JitBranchIndOp> {
|
|||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void generateRunCode(JitCodeGenerator gen, JitBranchIndOp op, JitBlock block,
|
* Generate code to retire the variables, write the dynamic pc value, and return from the
|
||||||
MethodVisitor rv) {
|
* passage
|
||||||
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param op the op
|
||||||
|
* @param ctx the context to write at exit, or null to not write the context
|
||||||
|
* @param block the block containing the op
|
||||||
|
* @param rv the run method visitor
|
||||||
|
*/
|
||||||
|
static void generateExitCode(JitCodeGenerator gen, JitBranchIndOp op, RegisterValue ctx,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
gen.generatePassageExit(block, () -> {
|
gen.generatePassageExit(block, () -> {
|
||||||
// [...]
|
// [...]
|
||||||
JitType targetType = gen.generateValReadCode(op.target(), op.targetType());
|
JitType targetType = gen.generateValReadCode(op.target(), op.targetType());
|
||||||
// [...,target:?]
|
// [...,target:?]
|
||||||
TypeConversions.generateToLong(targetType, LongJitType.I8, rv);
|
TypeConversions.generateToLong(targetType, LongJitType.I8, rv);
|
||||||
// [...,target:LONG]
|
// [...,target:LONG]
|
||||||
}, op.branch().flowCtx(), rv);
|
}, ctx, rv);
|
||||||
|
|
||||||
rv.visitInsn(ACONST_NULL);
|
rv.visitInsn(ACONST_NULL);
|
||||||
rv.visitInsn(ARETURN);
|
rv.visitInsn(ARETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch code generator for indirect branches
|
||||||
|
*/
|
||||||
|
static class IndBranchGen extends BranchGen<RIndBranch, JitBranchIndOp> {
|
||||||
|
/** Singleton */
|
||||||
|
static final IndBranchGen IND = new IndBranchGen();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Address exit(JitCodeGenerator gen, RIndBranch branch) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitBranchIndOp op, RIndBranch branch,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
|
generateExitCode(gen, op, branch.flowCtx(), block, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithCtxmod(JitCodeGenerator gen, JitBranchIndOp op, Address exit,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
|
generateExitCode(gen, op, null, block, rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateRunCode(JitCodeGenerator gen, JitBranchIndOp op, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
IndBranchGen.IND.generateCode(gen, op, op.branch(), block, rv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+169
-33
@@ -15,20 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.pcode.emu.jit.gen.op;
|
package ghidra.pcode.emu.jit.gen.op;
|
||||||
|
|
||||||
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED;
|
|
||||||
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_JIT_COMPILED_PASSAGE;
|
|
||||||
|
|
||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.*;
|
import ghidra.pcode.emu.jit.JitPassage.*;
|
||||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.gen.FieldForExitSlot;
|
import ghidra.pcode.emu.jit.gen.*;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.op.JitBranchOp;
|
import ghidra.pcode.emu.jit.op.JitBranchOp;
|
||||||
|
import ghidra.pcode.emu.jit.op.JitOp;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The generator for a {@link JitBranchOp branch}.
|
* The generator for a {@link JitBranchOp branch}.
|
||||||
@@ -48,49 +48,185 @@ public enum BranchOpGen implements OpGen<JitBranchOp> {
|
|||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit code that exits via a direct branch
|
* Generate code to retire the variables and write a given pc value.
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This emits the {@link ExtBranch} record case.
|
|
||||||
*
|
*
|
||||||
* @param gen the code generator
|
* @param gen the code generator
|
||||||
* @param exit the target causing us to exit
|
* @param exit the pc value to write
|
||||||
|
* @param ctx the context to write at exit, or null to not write the context
|
||||||
* @param block the block containing the op
|
* @param block the block containing the op
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the run method visitor
|
||||||
*/
|
*/
|
||||||
static void generateExtBranchCode(JitCodeGenerator gen, AddrCtx exit, JitBlock block,
|
static void generateRetireCode(JitCodeGenerator gen, Address exit, RegisterValue ctx,
|
||||||
MethodVisitor rv) {
|
JitBlock block, MethodVisitor rv) {
|
||||||
FieldForExitSlot slotField = gen.requestFieldForExitSlot(exit);
|
|
||||||
|
|
||||||
gen.generatePassageExit(block, () -> {
|
gen.generatePassageExit(block, () -> {
|
||||||
// [...]
|
// [...]
|
||||||
rv.visitLdcInsn(exit.address.getOffset());
|
rv.visitLdcInsn(exit.getOffset());
|
||||||
// [...,target:LONG]
|
// [...,target:LONG]
|
||||||
}, exit.rvCtx, rv);
|
}, ctx, rv);
|
||||||
|
}
|
||||||
|
|
||||||
// []
|
/**
|
||||||
slotField.generateLoadCode(gen, rv);
|
* Generate code to retire the variables, write a given pc value, and return from the passage.
|
||||||
// [slot]
|
*
|
||||||
rv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, "getChained",
|
* <p>
|
||||||
MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED, true);
|
* This will not write any decode context.
|
||||||
// [chained:ENTRY]
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param exit the pc value to write
|
||||||
|
* @param block the block containing the op
|
||||||
|
* @param rv the run method visitor
|
||||||
|
*/
|
||||||
|
static void generateExitCode(JitCodeGenerator gen, Address exit, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
generateRetireCode(gen, exit, null, block, rv);
|
||||||
|
rv.visitInsn(ACONST_NULL);
|
||||||
rv.visitInsn(ARETURN);
|
rv.visitInsn(ARETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch code generator
|
||||||
|
*
|
||||||
|
* @param <TB> the type of branch
|
||||||
|
* @param <TO> the type of op
|
||||||
|
*/
|
||||||
|
static abstract class BranchGen<TB extends RBranch, TO extends JitOp> {
|
||||||
|
/**
|
||||||
|
* Get the target address of the branch
|
||||||
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param branch the branch
|
||||||
|
* @return the target address
|
||||||
|
*/
|
||||||
|
abstract Address exit(JitCodeGenerator gen, TB branch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate code for the branch in the case a context modification has not occurred.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This means <em>no</em> context-modifying userop has been invoked.
|
||||||
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param op the branch op
|
||||||
|
* @param branch the branch from the op
|
||||||
|
* @param block the block containing the op
|
||||||
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
|
*/
|
||||||
|
abstract void generateCodeWithoutCtxmod(JitCodeGenerator gen, TO op, TB branch,
|
||||||
|
JitBlock block, MethodVisitor rv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate code for the branch in the case a context modification may have occurred.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This means a context-modifying userop has <em>certainly</em> been invoked, but not
|
||||||
|
* necessarily that the context has actually changed.
|
||||||
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param op the branch op
|
||||||
|
* @param branch the branch from the op
|
||||||
|
* @param block the block containing the op
|
||||||
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
|
*/
|
||||||
|
abstract void generateCodeWithCtxmod(JitCodeGenerator gen, TO op, Address exit,
|
||||||
|
JitBlock block, MethodVisitor rv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit code that jumps or exits via a direct branch
|
||||||
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param op the branch op
|
||||||
|
* @param branch the branch from the op
|
||||||
|
* @param block the block containing the op
|
||||||
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
|
*/
|
||||||
|
void generateCode(JitCodeGenerator gen, TO op, TB branch, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
switch (branch.reach()) {
|
||||||
|
case WITH_CTXMOD -> generateCodeWithCtxmod(gen, op, exit(gen, branch), block, rv);
|
||||||
|
case WITHOUT_CTXMOD -> generateCodeWithoutCtxmod(gen, op, branch, block, rv);
|
||||||
|
case MAYBE_CTXMOD -> {
|
||||||
|
Label withModctx = new Label();
|
||||||
|
RunFixedLocal.CTXMOD.generateLoadCode(rv);
|
||||||
|
rv.visitJumpInsn(IFNE, withModctx);
|
||||||
|
generateCodeWithoutCtxmod(gen, op, branch, block, rv);
|
||||||
|
rv.visitLabel(withModctx);
|
||||||
|
generateCodeWithCtxmod(gen, op, exit(gen, branch), block, rv);
|
||||||
|
}
|
||||||
|
default -> throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch code generator for internal branches
|
||||||
|
*/
|
||||||
|
static class IntBranchGen extends BranchGen<RIntBranch, JitOp> {
|
||||||
|
/** Singleton */
|
||||||
|
static final IntBranchGen INT = new IntBranchGen();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Address exit(JitCodeGenerator gen, RIntBranch branch) {
|
||||||
|
return gen.getAddressForOp(branch.to());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RIntBranch branch,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
|
JitBlock target = block.getTargetBlock(branch);
|
||||||
|
Label label = gen.labelForBlock(target);
|
||||||
|
VarGen.computeBlockTransition(gen, block, target).generate(rv);
|
||||||
|
rv.visitJumpInsn(GOTO, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
generateExitCode(gen, exit, block, rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch code generator for external branches
|
||||||
|
*/
|
||||||
|
static class ExtBranchGen extends BranchGen<RExtBranch, JitOp> {
|
||||||
|
/** Singleton */
|
||||||
|
static final ExtBranchGen EXT = new ExtBranchGen();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Address exit(JitCodeGenerator gen, RExtBranch branch) {
|
||||||
|
return branch.to().address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RExtBranch branch,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
|
AddrCtx exit = branch.to();
|
||||||
|
FieldForExitSlot slotField = gen.requestFieldForExitSlot(exit);
|
||||||
|
|
||||||
|
generateRetireCode(gen, exit.address, exit.rvCtx, block, rv);
|
||||||
|
|
||||||
|
// []
|
||||||
|
slotField.generateLoadCode(gen, rv);
|
||||||
|
// [slot]
|
||||||
|
rv.visitMethodInsn(INVOKESTATIC, GenConsts.NAME_JIT_COMPILED_PASSAGE, "getChained",
|
||||||
|
GenConsts.MDESC_JIT_COMPILED_PASSAGE__GET_CHAINED, true);
|
||||||
|
// [chained:ENTRY]
|
||||||
|
rv.visitInsn(ARETURN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
generateExitCode(gen, exit, block, rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateRunCode(JitCodeGenerator gen, JitBranchOp op, JitBlock block,
|
public void generateRunCode(JitCodeGenerator gen, JitBranchOp op, JitBlock block,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
|
|
||||||
switch (op.branch()) {
|
switch (op.branch()) {
|
||||||
case IntBranch ib -> {
|
case RIntBranch ib -> IntBranchGen.INT.generateCode(gen, op, ib, block, rv);
|
||||||
JitBlock target = block.getTargetBlock(ib);
|
case RExtBranch eb -> ExtBranchGen.EXT.generateCode(gen, op, eb, block, rv);
|
||||||
Label label = gen.labelForBlock(target);
|
|
||||||
VarGen.computeBlockTransition(gen, block, target).generate(rv);
|
|
||||||
rv.visitJumpInsn(GOTO, label);
|
|
||||||
}
|
|
||||||
case ExtBranch eb -> {
|
|
||||||
generateExtBranchCode(gen, eb.to(), block, rv);
|
|
||||||
}
|
|
||||||
default -> throw new AssertionError("Branch type confusion");
|
default -> throw new AssertionError("Branch type confusion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-24
@@ -18,15 +18,22 @@ package ghidra.pcode.emu.jit.gen.op;
|
|||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.ExtBranch;
|
import ghidra.pcode.emu.jit.JitPassage.*;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.IntBranch;
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitDataFlowModel;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
|
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.ExtBranchGen;
|
||||||
|
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.IntBranchGen;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||||
import ghidra.pcode.emu.jit.op.JitCBranchOp;
|
import ghidra.pcode.emu.jit.op.JitCBranchOp;
|
||||||
|
import ghidra.pcode.emu.jit.op.JitOp;
|
||||||
|
import ghidra.pcode.emu.jit.var.JitFailVal;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The generator for a {@link JitCBranchOp cbranch}.
|
* The generator for a {@link JitCBranchOp cbranch}.
|
||||||
@@ -48,35 +55,102 @@ public enum CBranchOpGen implements OpGen<JitCBranchOp> {
|
|||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch code generator for internal conditional branches
|
||||||
|
*/
|
||||||
|
static class IntCBranchGen extends IntBranchGen {
|
||||||
|
/** Singleton */
|
||||||
|
static final IntCBranchGen C_INT = new IntCBranchGen();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RIntBranch branch,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
|
JitBlock target = block.getTargetBlock(branch);
|
||||||
|
Label label = gen.labelForBlock(target);
|
||||||
|
BlockTransition transition = VarGen.computeBlockTransition(gen, block, target);
|
||||||
|
if (transition.needed()) {
|
||||||
|
Label fall = new Label();
|
||||||
|
rv.visitJumpInsn(IFEQ, fall);
|
||||||
|
transition.generate(rv);
|
||||||
|
rv.visitJumpInsn(GOTO, label);
|
||||||
|
rv.visitLabel(fall);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rv.visitJumpInsn(IFNE, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
Label fall = new Label();
|
||||||
|
rv.visitJumpInsn(IFEQ, fall);
|
||||||
|
super.generateCodeWithCtxmod(gen, op, exit, block, rv);
|
||||||
|
rv.visitLabel(fall);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branch code generator for external conditional branches
|
||||||
|
*/
|
||||||
|
static class ExtCBranchGen extends ExtBranchGen {
|
||||||
|
/** Singleton */
|
||||||
|
static final ExtCBranchGen C_EXT = new ExtCBranchGen();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithoutCtxmod(JitCodeGenerator gen, JitOp op, RExtBranch branch,
|
||||||
|
JitBlock block, MethodVisitor rv) {
|
||||||
|
Label fall = new Label();
|
||||||
|
rv.visitJumpInsn(IFEQ, fall);
|
||||||
|
super.generateCodeWithoutCtxmod(gen, op, branch, block, rv);
|
||||||
|
rv.visitLabel(fall);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generateCodeWithCtxmod(JitCodeGenerator gen, JitOp op, Address exit, JitBlock block,
|
||||||
|
MethodVisitor rv) {
|
||||||
|
Label fall = new Label();
|
||||||
|
rv.visitJumpInsn(IFEQ, fall);
|
||||||
|
super.generateCodeWithCtxmod(gen, op, exit, block, rv);
|
||||||
|
rv.visitLabel(fall);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @implNote In addition to implementing the proper logic for a conditional branch, this
|
||||||
|
* contains a special case for synthetic branches created using
|
||||||
|
* {@link ExitPcodeOp#cond(AddrCtx)}. Such synthetic ops are employed to check for
|
||||||
|
* context modification at instruction fall through. It's rare, but if there are
|
||||||
|
* multiple paths in an instruction's p-code or an injection, where one causes context
|
||||||
|
* modification and the other does not, then we must check for context modification at
|
||||||
|
* run time.
|
||||||
|
* <p>
|
||||||
|
* Conventionally, all {@link PcodeOp#CBRANCH} ops should have the condition as its
|
||||||
|
* second operand. Our special "conditional exit" does not. The
|
||||||
|
* {@link JitDataFlowModel} recognizes this and uses {@link JitFailVal} for
|
||||||
|
* {@link JitCBranchOp#cond()}. The "fail" value asserts that it never gets generated,
|
||||||
|
* which will ensure we apply special handling here.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void generateRunCode(JitCodeGenerator gen, JitCBranchOp op, JitBlock block,
|
public void generateRunCode(JitCodeGenerator gen, JitCBranchOp op, JitBlock block,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
|
if (op.op() instanceof ExitPcodeOp && op.branch() instanceof RExtBranch eb) {
|
||||||
|
assert eb.reach() == Reachability.MAYBE_CTXMOD;
|
||||||
|
Label fall = new Label();
|
||||||
|
RunFixedLocal.CTXMOD.generateLoadCode(rv);
|
||||||
|
rv.visitJumpInsn(IFEQ, fall);
|
||||||
|
BranchOpGen.generateExitCode(gen, eb.to().address, block, rv);
|
||||||
|
rv.visitLabel(fall);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
JitType cType = gen.generateValReadCode(op.cond(), op.condType());
|
JitType cType = gen.generateValReadCode(op.cond(), op.condType());
|
||||||
TypeConversions.generateIntToBool(cType, rv);
|
TypeConversions.generateIntToBool(cType, rv);
|
||||||
|
|
||||||
switch (op.branch()) {
|
switch (op.branch()) {
|
||||||
case IntBranch ib -> {
|
case RIntBranch ib -> IntCBranchGen.C_INT.generateCode(gen, op, ib, block, rv);
|
||||||
JitBlock target = block.getTargetBlock(ib);
|
case RExtBranch eb -> ExtCBranchGen.C_EXT.generateCode(gen, op, eb, block, rv);
|
||||||
Label label = gen.labelForBlock(target);
|
|
||||||
BlockTransition transition = VarGen.computeBlockTransition(gen, block, target);
|
|
||||||
if (transition.needed()) {
|
|
||||||
Label fall = new Label();
|
|
||||||
rv.visitJumpInsn(IFEQ, fall);
|
|
||||||
transition.generate(rv);
|
|
||||||
rv.visitJumpInsn(GOTO, label);
|
|
||||||
rv.visitLabel(fall);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rv.visitJumpInsn(IFNE, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ExtBranch eb -> {
|
|
||||||
Label fall = new Label();
|
|
||||||
rv.visitJumpInsn(IFEQ, fall);
|
|
||||||
BranchOpGen.generateExtBranchCode(gen, eb.to(), block, rv);
|
|
||||||
rv.visitLabel(fall);
|
|
||||||
}
|
|
||||||
default -> throw new AssertionError("Branch type confusion");
|
default -> throw new AssertionError("Branch type confusion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -25,8 +25,10 @@ import org.objectweb.asm.*;
|
|||||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
||||||
import ghidra.pcode.emu.jit.analysis.*;
|
import ghidra.pcode.emu.jit.analysis.*;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.gen.*;
|
import ghidra.pcode.emu.jit.gen.*;
|
||||||
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.RetireMode;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
@@ -111,10 +113,10 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
|||||||
|
|
||||||
gen.generateRetirePcCtx(() -> {
|
gen.generateRetirePcCtx(() -> {
|
||||||
rv.visitLdcInsn(gen.getAddressForOp(op).getOffset());
|
rv.visitLdcInsn(gen.getAddressForOp(op).getOffset());
|
||||||
}, gen.getExitContext(op), rv);
|
}, gen.getExitContext(op), RetireMode.SET, rv);
|
||||||
|
|
||||||
// []
|
// []
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
// [this]
|
// [this]
|
||||||
gen.requestFieldForUserop(userop).generateLoadCode(gen, rv);
|
gen.requestFieldForUserop(userop).generateLoadCode(gen, rv);
|
||||||
// [this,userop]
|
// [this,userop]
|
||||||
@@ -217,7 +219,7 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
|||||||
* @return true if applicable
|
* @return true if applicable
|
||||||
*/
|
*/
|
||||||
public static boolean canDoDirectInvocation(JitCallOtherOpIf op) {
|
public static boolean canDoDirectInvocation(JitCallOtherOpIf op) {
|
||||||
if (!op.userop().isFunctional()) {
|
if (!op.userop().isFunctional() || op.userop().modifiesContext()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,6 +240,10 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
|||||||
@Override
|
@Override
|
||||||
public void generateRunCode(JitCodeGenerator gen, JitCallOtherOpIf op, JitBlock block,
|
public void generateRunCode(JitCodeGenerator gen, JitCallOtherOpIf op, JitBlock block,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
|
if (op.userop().modifiesContext()) {
|
||||||
|
rv.visitLdcInsn(1);
|
||||||
|
RunFixedLocal.CTXMOD.generateStoreCode(rv);
|
||||||
|
}
|
||||||
if (canDoDirectInvocation(op)) {
|
if (canDoDirectInvocation(op)) {
|
||||||
generateRunCodeUsingDirectStrategy(gen, op, block, rv);
|
generateRunCodeUsingDirectStrategy(gen, op, block, rv);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -20,6 +20,7 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
|||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.DecodeErrorPcodeOp;
|
import ghidra.pcode.emu.jit.JitPassage.DecodeErrorPcodeOp;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
@@ -50,7 +51,7 @@ public enum UnimplementedOpGen implements OpGen<JitUnimplementedOp> {
|
|||||||
|
|
||||||
String message = gen.getErrorMessage(op.op());
|
String message = gen.getErrorMessage(op.op());
|
||||||
if (op.op() instanceof DecodeErrorPcodeOp) {
|
if (op.op() instanceof DecodeErrorPcodeOp) {
|
||||||
rv.visitVarInsn(ALOAD, 0);
|
RunFixedLocal.THIS.generateLoadCode(rv);
|
||||||
rv.visitLdcInsn(message);
|
rv.visitLdcInsn(message);
|
||||||
rv.visitLdcInsn(counter);
|
rv.visitLdcInsn(counter);
|
||||||
rv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "createDecodeError",
|
rv.visitMethodInsn(INVOKEINTERFACE, NAME_JIT_COMPILED_PASSAGE, "createDecodeError",
|
||||||
|
|||||||
+22
-10
@@ -20,7 +20,6 @@ import java.util.*;
|
|||||||
|
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
import ghidra.pcode.emu.PcodeThread;
|
|
||||||
import ghidra.pcode.emu.jit.JitCompiler;
|
import ghidra.pcode.emu.jit.JitCompiler;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.*;
|
import ghidra.pcode.emu.jit.JitPassage.*;
|
||||||
import ghidra.pcode.emu.jit.JitPcodeThread;
|
import ghidra.pcode.emu.jit.JitPcodeThread;
|
||||||
@@ -1380,21 +1379,34 @@ public interface JitCompiledPassage {
|
|||||||
* Set the bound thread's program counter and decode context.
|
* Set the bound thread's program counter and decode context.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This is called during retirement, i.e., upon exiting a passage or entering a hazard. This
|
* This is called during retirement, i.e., upon exiting a passage. This just converts things to
|
||||||
* just converts things to the right type and invokes
|
* the right type and invokes
|
||||||
* {@link PcodeThread#overrideCounter(Address)} and
|
* {@link JitPcodeThread#writeCounterAndContext(Address, RegisterValue)}.
|
||||||
* {@link PcodeThread#overrideContext(RegisterValue)}.
|
|
||||||
*
|
*
|
||||||
* @param counter the offset of the next instruction to execute
|
* @param counter the offset of the next instruction to execute
|
||||||
* @param context the decode context for the next instruction
|
* @param context the decode context for the next instruction
|
||||||
*/
|
*/
|
||||||
default void retireCounterAndContext(long counter, RegisterValue context) {
|
default void writeCounterAndContext(long counter, RegisterValue context) {
|
||||||
JitPcodeThread thread = thread();
|
JitPcodeThread thread = thread();
|
||||||
Address pc = thread.getLanguage().getDefaultSpace().getAddress(counter);
|
Address pc = thread.getLanguage().getDefaultSpace().getAddress(counter);
|
||||||
thread.overrideCounter(pc);
|
thread.writeCounterAndContext(pc, context);
|
||||||
if (context != null) {
|
}
|
||||||
thread.overrideContext(context);
|
|
||||||
}
|
/**
|
||||||
|
* Set the bound thread's program counter and decode context, without writing it to the machine
|
||||||
|
* state.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is called during retirement upon entering a hazard. This just converts things to the
|
||||||
|
* right type and invokes {@link JitPcodeThread#setCounterAndContext(Address, RegisterValue)}.
|
||||||
|
*
|
||||||
|
* @param counter the offset of the next instruction to execute
|
||||||
|
* @param context the decode context for the next instruction
|
||||||
|
*/
|
||||||
|
default void setCounterAndContext(long counter, RegisterValue context) {
|
||||||
|
JitPcodeThread thread = thread();
|
||||||
|
Address pc = thread.getLanguage().getDefaultSpace().getAddress(counter);
|
||||||
|
thread.setCounterAndContext(pc, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.jit.gen.var;
|
||||||
|
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||||
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
|
import ghidra.pcode.emu.jit.var.JitFailVal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The generator that is forbidden from actually generating.
|
||||||
|
*/
|
||||||
|
public enum FailValGen implements ValGen<JitFailVal> {
|
||||||
|
/** Singleton */
|
||||||
|
GEN;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateValInitCode(JitCodeGenerator gen, JitFailVal v, MethodVisitor iv) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JitType generateValReadCode(JitCodeGenerator gen, JitFailVal v,
|
||||||
|
JitTypeBehavior typeReq, MethodVisitor rv) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,6 +118,7 @@ public interface ValGen<V extends JitVal> {
|
|||||||
static <V extends JitVal> ValGen<V> lookup(V v) {
|
static <V extends JitVal> ValGen<V> lookup(V v) {
|
||||||
return (ValGen<V>) switch (v) {
|
return (ValGen<V>) switch (v) {
|
||||||
case JitConstVal c -> ConstValGen.GEN;
|
case JitConstVal c -> ConstValGen.GEN;
|
||||||
|
case JitFailVal m -> FailValGen.GEN;
|
||||||
case JitVar vv -> VarGen.lookup(vv);
|
case JitVar vv -> VarGen.lookup(vv);
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ package ghidra.pcode.emu.jit.op;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.IndBranch;
|
import ghidra.pcode.emu.jit.JitPassage.RIndBranch;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||||
import ghidra.pcode.emu.jit.var.JitVal;
|
import ghidra.pcode.emu.jit.var.JitVal;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
@@ -29,7 +29,7 @@ import ghidra.program.model.pcode.PcodeOp;
|
|||||||
* @param target the use-def node for the target offset
|
* @param target the use-def node for the target offset
|
||||||
* @param branch the branch record created for the p-code op
|
* @param branch the branch record created for the p-code op
|
||||||
*/
|
*/
|
||||||
public record JitBranchIndOp(PcodeOp op, JitVal target, IndBranch branch) implements JitOp {
|
public record JitBranchIndOp(PcodeOp op, JitVal target, RIndBranch branch) implements JitOp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canBeRemoved() {
|
public boolean canBeRemoved() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ package ghidra.pcode.emu.jit.op;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.Branch;
|
import ghidra.pcode.emu.jit.JitPassage.RBranch;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||||
import ghidra.pcode.emu.jit.var.JitVal;
|
import ghidra.pcode.emu.jit.var.JitVal;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
@@ -28,7 +28,7 @@ import ghidra.program.model.pcode.PcodeOp;
|
|||||||
* @param op the p-code op
|
* @param op the p-code op
|
||||||
* @param branch the branch record created for the p-code op
|
* @param branch the branch record created for the p-code op
|
||||||
*/
|
*/
|
||||||
public record JitBranchOp(PcodeOp op, Branch branch) implements JitOp {
|
public record JitBranchOp(PcodeOp op, RBranch branch) implements JitOp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canBeRemoved() {
|
public boolean canBeRemoved() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ package ghidra.pcode.emu.jit.op;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitPassage.Branch;
|
import ghidra.pcode.emu.jit.JitPassage.RBranch;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
import ghidra.pcode.emu.jit.analysis.JitTypeBehavior;
|
||||||
import ghidra.pcode.emu.jit.var.JitVal;
|
import ghidra.pcode.emu.jit.var.JitVal;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
@@ -29,7 +29,7 @@ import ghidra.program.model.pcode.PcodeOp;
|
|||||||
* @param branch the branch record created for the p-code op
|
* @param branch the branch record created for the p-code op
|
||||||
* @param cond the use-def node for the branch condition
|
* @param cond the use-def node for the branch condition
|
||||||
*/
|
*/
|
||||||
public record JitCBranchOp(PcodeOp op, Branch branch, JitVal cond)
|
public record JitCBranchOp(PcodeOp op, RBranch branch, JitVal cond)
|
||||||
implements JitOp {
|
implements JitOp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.jit.var;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.op.JitOp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value that is forbidden from being translated
|
||||||
|
*/
|
||||||
|
public enum JitFailVal implements JitVal {
|
||||||
|
/** Singleton */
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ValUse> uses() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUse(JitOp op, int position) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUse(JitOp op, int position) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
-17
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.pcode.emulate;
|
package ghidra.pcode.emulate;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.pcode.emulate.callother.OpBehaviorOther;
|
import ghidra.pcode.emulate.callother.OpBehaviorOther;
|
||||||
import ghidra.pcode.error.LowlevelError;
|
import ghidra.pcode.error.LowlevelError;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
@@ -23,14 +25,10 @@ import ghidra.program.model.lang.RegisterValue;
|
|||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>EmulateInstructionStateModifier</code> defines a language specific
|
* <code>EmulateInstructionStateModifier</code> defines a language specific handler to assist
|
||||||
* handler to assist emulation with adjusting the current execution state,
|
* emulation with adjusting the current execution state, providing support for custom pcodeop's
|
||||||
* providing support for custom pcodeop's (i.e., CALLOTHER).
|
* (i.e., CALLOTHER). The implementation of this interface must provide a public constructor which
|
||||||
* The implementation of this interface must provide a public constructor which
|
|
||||||
* takes a single Emulate argument.
|
* takes a single Emulate argument.
|
||||||
*/
|
*/
|
||||||
public abstract class EmulateInstructionStateModifier {
|
public abstract class EmulateInstructionStateModifier {
|
||||||
@@ -47,6 +45,7 @@ public abstract class EmulateInstructionStateModifier {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a pcodeop behavior corresponding to a CALLOTHER opcode.
|
* Register a pcodeop behavior corresponding to a CALLOTHER opcode.
|
||||||
|
*
|
||||||
* @param opName name as defined within language via "define pcodeop"
|
* @param opName name as defined within language via "define pcodeop"
|
||||||
* @param pcodeOpBehavior
|
* @param pcodeOpBehavior
|
||||||
*/
|
*/
|
||||||
@@ -66,9 +65,10 @@ public abstract class EmulateInstructionStateModifier {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a CALLOTHER op
|
* Execute a CALLOTHER op
|
||||||
|
*
|
||||||
* @param op
|
* @param op
|
||||||
* @return true if corresponding pcodeop was registered and emulation support is
|
* @return true if corresponding pcodeop was registered and emulation support is performed, or
|
||||||
* performed, or false if corresponding pcodeop is not supported by this class.
|
* false if corresponding pcodeop is not supported by this class.
|
||||||
* @throws LowlevelError
|
* @throws LowlevelError
|
||||||
*/
|
*/
|
||||||
public final boolean executeCallOther(PcodeOp op) throws LowlevelError {
|
public final boolean executeCallOther(PcodeOp op) throws LowlevelError {
|
||||||
@@ -85,20 +85,23 @@ public abstract class EmulateInstructionStateModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulation callback immediately before the first instruction is executed.
|
* Emulation callback immediately before the first instruction is executed. This callback
|
||||||
* This callback permits any language specific initializations to be performed.
|
* permits any language specific initializations to be performed.
|
||||||
|
*
|
||||||
* @param emulate
|
* @param emulate
|
||||||
* @param current_address intial execute address
|
* @param current_address intial execute address
|
||||||
* @param contextRegisterValue initial context value or null if not applicable or unknown
|
* @param contextRegisterValue initial context value or null if not applicable or unknown
|
||||||
* @throws LowlevelError
|
* @throws LowlevelError
|
||||||
*/
|
*/
|
||||||
public void initialExecuteCallback(Emulate emulate, Address current_address, RegisterValue contextRegisterValue) throws LowlevelError {
|
public void initialExecuteCallback(Emulate emulate, Address current_address,
|
||||||
|
RegisterValue contextRegisterValue) throws LowlevelError {
|
||||||
// no default implementation
|
// no default implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulation callback immediately following execution of the lastExecuteAddress.
|
* Emulation callback immediately following execution of the lastExecuteAddress. One use of this
|
||||||
* One use of this callback is to modify the flowing/future context state.
|
* callback is to modify the flowing/future context state.
|
||||||
|
*
|
||||||
* @param emulate
|
* @param emulate
|
||||||
* @param lastExecuteAddress
|
* @param lastExecuteAddress
|
||||||
* @param lastExecutePcode
|
* @param lastExecutePcode
|
||||||
@@ -111,4 +114,16 @@ public abstract class EmulateInstructionStateModifier {
|
|||||||
throws LowlevelError {
|
throws LowlevelError {
|
||||||
// no default implementation
|
// no default implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the map of registered pcode userop behaviors
|
||||||
|
*
|
||||||
|
* @return the map, by userop index.
|
||||||
|
*/
|
||||||
|
public Map<Integer, OpBehaviorOther> getPcodeOpMap() {
|
||||||
|
if (pcodeOpMap == null) {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(pcodeOpMap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-1
@@ -214,6 +214,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||||||
private final AnnotatedPcodeUseropLibrary<T> library;
|
private final AnnotatedPcodeUseropLibrary<T> library;
|
||||||
private final boolean isFunctional;
|
private final boolean isFunctional;
|
||||||
private final boolean hasSideEffects;
|
private final boolean hasSideEffects;
|
||||||
|
private final boolean modifiesContext;
|
||||||
private final boolean canInline;
|
private final boolean canInline;
|
||||||
private final MethodHandle handle;
|
private final MethodHandle handle;
|
||||||
|
|
||||||
@@ -256,8 +257,9 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||||||
}
|
}
|
||||||
initFinished();
|
initFinished();
|
||||||
this.isFunctional = annot.functional();
|
this.isFunctional = annot.functional();
|
||||||
this.canInline = annot.canInline();
|
|
||||||
this.hasSideEffects = annot.hasSideEffects();
|
this.hasSideEffects = annot.hasSideEffects();
|
||||||
|
this.modifiesContext = annot.modifiesContext();
|
||||||
|
this.canInline = annot.canInline();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -312,6 +314,11 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||||||
return hasSideEffects;
|
return hasSideEffects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean modifiesContext() {
|
||||||
|
return modifiesContext;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInlinePcode() {
|
public boolean canInlinePcode() {
|
||||||
return canInline;
|
return canInline;
|
||||||
@@ -662,6 +669,17 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
|||||||
*/
|
*/
|
||||||
boolean hasSideEffects() default true;
|
boolean hasSideEffects() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to indicate the userop can modify the decode context.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Failure to indicate context modifications can lead to erroneous decodes and thus
|
||||||
|
* incorrect execution results.
|
||||||
|
*
|
||||||
|
* @see PcodeUseropLibrary.PcodeUseropDefinition#modifiesContext()
|
||||||
|
*/
|
||||||
|
boolean modifiesContext() default false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true to suggest inlining.
|
* Set to true to suggest inlining.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -247,4 +247,11 @@ public class PcodeProgram {
|
|||||||
public String format() {
|
public String format() {
|
||||||
return format(false);
|
return format(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUseropName(int opNo) {
|
||||||
|
if (opNo < language.getNumberOfUserDefinedOpNames()) {
|
||||||
|
return language.getUserDefinedOpName(opNo);
|
||||||
|
}
|
||||||
|
return useropNames.get(opNo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,6 +187,19 @@ public interface PcodeUseropLibrary<T> {
|
|||||||
*/
|
*/
|
||||||
boolean hasSideEffects();
|
boolean hasSideEffects();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that this userop may modify the decode context.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This means that the userop may set a field in {@code contextreg}, which could thus affect
|
||||||
|
* how subsequent instructions are decoded. Executors which decode ahead will have to
|
||||||
|
* consider this effect.
|
||||||
|
*
|
||||||
|
* @return true if this can modify the context.
|
||||||
|
* @see PcodeUserop#modifiesContext()
|
||||||
|
*/
|
||||||
|
boolean modifiesContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether or not this userop definition produces p-code suitable for inlining in
|
* Indicates whether or not this userop definition produces p-code suitable for inlining in
|
||||||
* place of its invocation.
|
* place of its invocation.
|
||||||
|
|||||||
+13
@@ -203,6 +203,19 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @implNote We could scan the p-code ops for any that write to the contextreg; however, at the
|
||||||
|
* moment, that is highly unconventional and perhaps even considered an error. If that
|
||||||
|
* becomes more common, or even recommended, then we can detect it and behave
|
||||||
|
* accordingly during interpretation (whether for execution or translation).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean modifiesContext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInlinePcode() {
|
public boolean canInlinePcode() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -136,6 +136,15 @@
|
|||||||
</unaffected>
|
</unaffected>
|
||||||
</prototype>
|
</prototype>
|
||||||
|
|
||||||
|
<callotherfixup targetop="setISAMode">
|
||||||
|
<pcode incidentalcopy="true">
|
||||||
|
<!-- NOP -->
|
||||||
|
<body><![CDATA[
|
||||||
|
r0 = r0;
|
||||||
|
]]></body>
|
||||||
|
</pcode>
|
||||||
|
</callotherfixup>
|
||||||
|
|
||||||
<callfixup name="switch8_r3">
|
<callfixup name="switch8_r3">
|
||||||
<target name="switch8_r3"/>
|
<target name="switch8_r3"/>
|
||||||
<target name="__ARM_common_switch8"/>
|
<target name="__ARM_common_switch8"/>
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ define pcodeop ReverseBitOrder;
|
|||||||
define pcodeop SendEvent;
|
define pcodeop SendEvent;
|
||||||
define pcodeop setEndianState;
|
define pcodeop setEndianState;
|
||||||
|
|
||||||
|
# Copies ISAModeSwitch to TMode
|
||||||
|
define pcodeop setISAMode;
|
||||||
|
|
||||||
macro affectflags() {
|
macro affectflags() {
|
||||||
CY = tmpCY; ZR = tmpZR; NG = tmpNG; OV = tmpOV;
|
CY = tmpCY; ZR = tmpZR; NG = tmpNG; OV = tmpOV;
|
||||||
}
|
}
|
||||||
@@ -197,11 +200,16 @@ macro affect_resflags() {
|
|||||||
ZR = tmpZR; NG = tmpNG;
|
ZR = tmpZR; NG = tmpNG;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro SetThumbMode(value) {
|
macro SetISAModeSwitch(value) {
|
||||||
ISAModeSwitch = value;
|
ISAModeSwitch = value;
|
||||||
TB = ISAModeSwitch;
|
TB = ISAModeSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro SetThumbMode(value) {
|
||||||
|
SetISAModeSwitch(value);
|
||||||
|
setISAMode();
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# simple branch, not inter-working
|
# simple branch, not inter-working
|
||||||
macro BranchWritePC(addr) {
|
macro BranchWritePC(addr) {
|
||||||
|
|||||||
@@ -80,6 +80,15 @@
|
|||||||
</prototype>
|
</prototype>
|
||||||
</default_proto>
|
</default_proto>
|
||||||
|
|
||||||
|
<callotherfixup targetop="setISAMode">
|
||||||
|
<pcode incidentalcopy="true">
|
||||||
|
<!-- NOP -->
|
||||||
|
<body><![CDATA[
|
||||||
|
r0 = r0;
|
||||||
|
]]></body>
|
||||||
|
</pcode>
|
||||||
|
</callotherfixup>
|
||||||
|
|
||||||
<callfixup name="switch8_r3">
|
<callfixup name="switch8_r3">
|
||||||
<target name="switch8_r3"/>
|
<target name="switch8_r3"/>
|
||||||
<pcode>
|
<pcode>
|
||||||
|
|||||||
@@ -128,4 +128,12 @@
|
|||||||
</prototype>
|
</prototype>
|
||||||
</default_proto>
|
</default_proto>
|
||||||
|
|
||||||
|
<callotherfixup targetop="setISAMode">
|
||||||
|
<pcode incidentalcopy="true">
|
||||||
|
<!-- NOP -->
|
||||||
|
<body><![CDATA[
|
||||||
|
r0 = r0;
|
||||||
|
]]></body>
|
||||||
|
</pcode>
|
||||||
|
</callotherfixup>
|
||||||
</compiler_spec>
|
</compiler_spec>
|
||||||
|
|||||||
@@ -2317,7 +2317,7 @@ ArmPCRelImmed12: reloff is U23=0 & immed & rotate
|
|||||||
:blx HAddr24 is $(AMODE) & CALLoverride=0 & ARMcond=0 & cond=15 & c2527=5 & H24=0 & HAddr24
|
:blx HAddr24 is $(AMODE) & CALLoverride=0 & ARMcond=0 & cond=15 & c2527=5 & H24=0 & HAddr24
|
||||||
{
|
{
|
||||||
lr = inst_next;
|
lr = inst_next;
|
||||||
SetThumbMode(1);
|
SetISAModeSwitch(1); # TMode done by HAddr24's globalset
|
||||||
call HAddr24;
|
call HAddr24;
|
||||||
# don't do causes decompiler trouble TB = 0;
|
# don't do causes decompiler trouble TB = 0;
|
||||||
} # Always changes to THUMB mode
|
} # Always changes to THUMB mode
|
||||||
@@ -2325,7 +2325,7 @@ ArmPCRelImmed12: reloff is U23=0 & immed & rotate
|
|||||||
:blx HAddr24 is $(AMODE) & CALLoverride=1 & ARMcond=0 & cond=15 & c2527=5 & H24=0 & HAddr24
|
:blx HAddr24 is $(AMODE) & CALLoverride=1 & ARMcond=0 & cond=15 & c2527=5 & H24=0 & HAddr24
|
||||||
{
|
{
|
||||||
lr = inst_next;
|
lr = inst_next;
|
||||||
SetThumbMode(1);
|
SetISAModeSwitch(1); # TMode done by HAddr24's globalset
|
||||||
goto HAddr24;
|
goto HAddr24;
|
||||||
} # Always changes to THUMB mode
|
} # Always changes to THUMB mode
|
||||||
|
|
||||||
@@ -2333,7 +2333,7 @@ ArmPCRelImmed12: reloff is U23=0 & immed & rotate
|
|||||||
:blx HAddr24 is $(AMODE) & ARMcond=0 & CALLoverride=0 & cond=15 & c2527=5 & H24=1 & HAddr24
|
:blx HAddr24 is $(AMODE) & ARMcond=0 & CALLoverride=0 & cond=15 & c2527=5 & H24=1 & HAddr24
|
||||||
{
|
{
|
||||||
lr = inst_next;
|
lr = inst_next;
|
||||||
SetThumbMode(1);
|
SetISAModeSwitch(1); # TMode done by HAddr24's globalset
|
||||||
call HAddr24;
|
call HAddr24;
|
||||||
# don't do causes decompiler trouble TB = 0;
|
# don't do causes decompiler trouble TB = 0;
|
||||||
} # Always changes to THUMB mode
|
} # Always changes to THUMB mode
|
||||||
@@ -2341,7 +2341,7 @@ ArmPCRelImmed12: reloff is U23=0 & immed & rotate
|
|||||||
:blx HAddr24 is $(AMODE) & ARMcond=0 & CALLoverride=1 & cond=15 & c2527=5 & H24=1 & HAddr24
|
:blx HAddr24 is $(AMODE) & ARMcond=0 & CALLoverride=1 & cond=15 & c2527=5 & H24=1 & HAddr24
|
||||||
{
|
{
|
||||||
lr = inst_next;
|
lr = inst_next;
|
||||||
SetThumbMode(1);
|
SetISAModeSwitch(1); # TMode done by HAddr24's globalset
|
||||||
goto HAddr24;
|
goto HAddr24;
|
||||||
} # Always changes to THUMB mode
|
} # Always changes to THUMB mode
|
||||||
|
|
||||||
|
|||||||
+86
-100
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -19,11 +19,12 @@ import java.math.BigInteger;
|
|||||||
|
|
||||||
import ghidra.pcode.emulate.Emulate;
|
import ghidra.pcode.emulate.Emulate;
|
||||||
import ghidra.pcode.emulate.EmulateInstructionStateModifier;
|
import ghidra.pcode.emulate.EmulateInstructionStateModifier;
|
||||||
|
import ghidra.pcode.emulate.callother.OpBehaviorOther;
|
||||||
import ghidra.pcode.error.LowlevelError;
|
import ghidra.pcode.error.LowlevelError;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.lang.RegisterValue;
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
public class ARMEmulateInstructionStateModifier extends EmulateInstructionStateModifier {
|
public class ARMEmulateInstructionStateModifier extends EmulateInstructionStateModifier {
|
||||||
|
|
||||||
@@ -45,74 +46,77 @@ public class ARMEmulateInstructionStateModifier extends EmulateInstructionStateM
|
|||||||
aMode = new RegisterValue(TModeReg, BigInteger.ZERO);
|
aMode = new RegisterValue(TModeReg, BigInteger.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerPcodeOpBehavior("setISAMode", new SetISAModeOpBehavior());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We could registerPcodeOpBehavior for one or more of the following pcodeop's:
|
* We could registerPcodeOpBehavior for one or more of the following pcodeop's:
|
||||||
*
|
* <p>
|
||||||
Absolute
|
* Absolute<br/>
|
||||||
ClearExclusiveLocal
|
* ClearExclusiveLocal<br/>
|
||||||
DataMemoryBarrier
|
* DataMemoryBarrier<br/>
|
||||||
DataSynchronizationBarrier
|
* DataSynchronizationBarrier<br/>
|
||||||
ExclusiveAccess
|
* ExclusiveAccess<br/>
|
||||||
HintDebug
|
* HintDebug<br/>
|
||||||
HintPreloadData
|
* HintPreloadData<br/>
|
||||||
HintPreloadDataForWrite
|
* HintPreloadDataForWrite<br/>
|
||||||
HintPreloadInstruction
|
* HintPreloadInstruction<br/>
|
||||||
HintYield
|
* HintYield <br/>
|
||||||
IndexCheck
|
* IndexCheck<br/>
|
||||||
InstructionSynchronizationBarrier
|
* InstructionSynchronizationBarrier<br/>
|
||||||
ReverseBitOrder
|
* ReverseBitOrder<br/>
|
||||||
SendEvent
|
* SendEvent<br/>
|
||||||
SignedDoesSaturate
|
* SignedDoesSaturate<br/>
|
||||||
SignedSaturate
|
* SignedSaturate<br/>
|
||||||
UnsignedDoesSaturate
|
* UnsignedDoesSaturate<br/>
|
||||||
UnsignedSaturate
|
* UnsignedSaturate<br/>
|
||||||
WaitForEvent
|
* WaitForEvent<br/>
|
||||||
WaitForInterrupt
|
* WaitForInterrupt<br/>
|
||||||
coprocessor_function
|
* coprocessor_function<br/>
|
||||||
coprocessor_function2
|
* coprocessor_function2<br/>
|
||||||
coprocessor_load
|
* coprocessor_load<br/>
|
||||||
coprocessor_load2
|
* coprocessor_load2<br/>
|
||||||
coprocessor_loadlong
|
* coprocessor_loadlong<br/>
|
||||||
coprocessor_loadlong2
|
* coprocessor_loadlong2<br/>
|
||||||
coprocessor_movefrom
|
* coprocessor_movefrom<br/>
|
||||||
coprocessor_movefrom2
|
* coprocessor_movefrom2<br/>
|
||||||
coprocessor_moveto
|
* coprocessor_moveto<br/>
|
||||||
coprocessor_moveto2
|
* coprocessor_moveto2<br/>
|
||||||
coprocessor_store
|
* coprocessor_store<br/>
|
||||||
coprocessor_store2
|
* coprocessor_store2<br/>
|
||||||
coprocessor_storelong
|
* coprocessor_storelong<br/>
|
||||||
coprocessor_storelong2
|
* coprocessor_storelong2<br/>
|
||||||
disableDataAbortInterrupts
|
* disableDataAbortInterrupts<br/>
|
||||||
disableFIQinterrupts
|
* disableFIQinterrupts<br/>
|
||||||
disableIRQinterrupts
|
* disableIRQinterrupts<br/>
|
||||||
enableDataAbortInterrupts
|
* enableDataAbortInterrupts<br/>
|
||||||
enableFIQinterrupts
|
* enableFIQinterrupts<br/>
|
||||||
enableIRQinterrupts
|
* enableIRQinterrupts<br/>
|
||||||
hasExclusiveAccess
|
* hasExclusiveAccess<br/>
|
||||||
isCurrentModePrivileged
|
* isCurrentModePrivileged<br/>
|
||||||
isFIQinterruptsEnabled
|
* isFIQinterruptsEnabled<br/>
|
||||||
isIRQinterruptsEnabled
|
* isIRQinterruptsEnabled<br/>
|
||||||
isThreadMode
|
* isThreadMode<br/>
|
||||||
jazelle_branch
|
* jazelle_branch<br/>
|
||||||
setAbortMode
|
* setAbortMode<br/>
|
||||||
setFIQMode
|
* setFIQMode<br/>
|
||||||
setIRQMode
|
* setIRQMode<br/>
|
||||||
setSupervisorMode
|
* setSupervisorMode<br/>
|
||||||
setSystemMode
|
* setSystemMode<br/>
|
||||||
setThreadModePrivileged
|
* setThreadModePrivileged<br/>
|
||||||
setUndefinedMode
|
* setUndefinedMode<br/>
|
||||||
setUserMode
|
* setUserMode<br/>
|
||||||
software_breakpoint
|
* software_breakpoint<br/>
|
||||||
software_interrupt
|
* software_interrupt<br/>
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize TB register based upon context-register state before first instruction is executed.
|
* Initialize TB register based upon context-register state before first instruction is
|
||||||
|
* executed.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initialExecuteCallback(Emulate emulate, Address current_address, RegisterValue contextRegisterValue) throws LowlevelError {
|
public void initialExecuteCallback(Emulate emulate, Address current_address,
|
||||||
|
RegisterValue contextRegisterValue) throws LowlevelError {
|
||||||
if (TModeReg == null) {
|
if (TModeReg == null) {
|
||||||
return; // Thumb mode not supported
|
return; // Thumb mode not supported
|
||||||
}
|
}
|
||||||
@@ -127,46 +131,28 @@ public class ARMEmulateInstructionStateModifier extends EmulateInstructionStateM
|
|||||||
emu.getMemoryState().setValue(TBreg, tModeValue);
|
emu.getMemoryState().setValue(TBreg, tModeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
class SetISAModeOpBehavior implements OpBehaviorOther {
|
||||||
* Handle odd addresses which may occur when jumping/returning indirectly
|
@Override
|
||||||
* to Thumb mode. It is assumed that language will properly handle
|
public void evaluate(Emulate emu, Varnode out, Varnode[] inputs) {
|
||||||
* context changes during the flow of execution, we need only fix
|
Address currentAddress = emu.getExecuteAddress();
|
||||||
* the current program counter.
|
long tbValue = emu.getMemoryState().getValue(TBreg);
|
||||||
*/
|
if (tbValue == 1) {
|
||||||
@Override
|
// Thumb mode
|
||||||
public void postExecuteCallback(Emulate emulate, Address lastExecuteAddress,
|
emu.setContextRegisterValue(tMode); // change context to be consistent with TB value
|
||||||
PcodeOp[] lastExecutePcode, int lastPcodeIndex, Address currentAddress)
|
if ((currentAddress.getOffset() & 0x1) == 1) {
|
||||||
throws LowlevelError {
|
emu.setExecuteAddress(currentAddress.previous());
|
||||||
if (TModeReg == null) {
|
}
|
||||||
return; // Thumb mode not supported
|
|
||||||
}
|
|
||||||
if (lastPcodeIndex < 0) {
|
|
||||||
// ignore fall-through condition
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int lastOp = lastExecutePcode[lastPcodeIndex].getOpcode();
|
|
||||||
if (lastOp != PcodeOp.BRANCH && lastOp != PcodeOp.CBRANCH && lastOp != PcodeOp.BRANCHIND &&
|
|
||||||
lastOp != PcodeOp.CALL && lastOp != PcodeOp.CALLIND && lastOp != PcodeOp.RETURN) {
|
|
||||||
// only concerned with Branch, Call or Return ops
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
long tbValue = emu.getMemoryState().getValue(TBreg);
|
|
||||||
if (tbValue == 1) {
|
|
||||||
// Thumb mode
|
|
||||||
emu.setContextRegisterValue(tMode); // change context to be consistent with TB value
|
|
||||||
if ((currentAddress.getOffset() & 0x1) == 1) {
|
|
||||||
emulate.setExecuteAddress(currentAddress.previous());
|
|
||||||
}
|
}
|
||||||
}
|
else if (tbValue == 0) {
|
||||||
else if (tbValue == 0) {
|
|
||||||
|
|
||||||
if ((currentAddress.getOffset() & 0x1) == 1) {
|
if ((currentAddress.getOffset() & 0x1) == 1) {
|
||||||
throw new LowlevelError(
|
throw new LowlevelError(
|
||||||
"Flow to odd address occurred without setting TB register (Thumb mode)");
|
"Flow to odd address occurred without setting TB register (Thumb mode)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARM mode
|
||||||
|
emu.setContextRegisterValue(aMode); // change context to be consistent with TB value
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARM mode
|
|
||||||
emu.setContextRegisterValue(aMode); // change context to be consistent with TB value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-4
@@ -26,8 +26,7 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import generic.test.AbstractGTest;
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.GhidraTestApplicationLayout;
|
import ghidra.GhidraTestApplicationLayout;
|
||||||
import ghidra.app.plugin.assembler.Assemblers;
|
import ghidra.app.plugin.assembler.*;
|
||||||
import ghidra.app.plugin.assembler.AssemblyBuffer;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
|
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.ApplicationConfiguration;
|
import ghidra.framework.ApplicationConfiguration;
|
||||||
@@ -303,11 +302,11 @@ public abstract class AbstractPcodeEmulatorTest extends AbstractGTest {
|
|||||||
public void testSkipThumbStaysThumb() throws Exception {
|
public void testSkipThumbStaysThumb() throws Exception {
|
||||||
PcodeEmulator emu = createEmulator(getLanguage(LANGID_ARMV8));
|
PcodeEmulator emu = createEmulator(getLanguage(LANGID_ARMV8));
|
||||||
PcodeArithmetic<byte[]> arithmetic = emu.getArithmetic();
|
PcodeArithmetic<byte[]> arithmetic = emu.getArithmetic();
|
||||||
AddressSpace space = emu.getLanguage().getDefaultSpace();
|
Language language = emu.getLanguage();
|
||||||
|
AddressSpace space = language.getDefaultSpace();
|
||||||
Address entry = space.getAddress(0x00400000);
|
Address entry = space.getAddress(0x00400000);
|
||||||
AssemblyBuffer asm = new AssemblyBuffer(Assemblers.getAssembler(emu.getLanguage()), entry);
|
AssemblyBuffer asm = new AssemblyBuffer(Assemblers.getAssembler(emu.getLanguage()), entry);
|
||||||
|
|
||||||
Language language = asm.getAssembler().getLanguage();
|
|
||||||
Register regCtx = language.getContextBaseRegister();
|
Register regCtx = language.getContextBaseRegister();
|
||||||
Register regT = language.getRegister("T");
|
Register regT = language.getRegister("T");
|
||||||
RegisterValue rvDefault = new RegisterValue(regCtx,
|
RegisterValue rvDefault = new RegisterValue(regCtx,
|
||||||
@@ -429,4 +428,60 @@ public abstract class AbstractPcodeEmulatorTest extends AbstractGTest {
|
|||||||
arithmetic.toLong(thread.getState().getVar(r1, Reason.INSPECT), Purpose.INSPECT));
|
arithmetic.toLong(thread.getState().getVar(r1, Reason.INSPECT), Purpose.INSPECT));
|
||||||
assertEquals(target, thread.getCounter());
|
assertEquals(target, thread.getCounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testArmPltIntoThumbFunction() throws Exception {
|
||||||
|
PcodeEmulator emu = createEmulator(getLanguage(LANGID_ARMV8));
|
||||||
|
PcodeArithmetic<byte[]> arithmetic = emu.getArithmetic();
|
||||||
|
Language language = emu.getLanguage();
|
||||||
|
AddressSpace space = language.getDefaultSpace();
|
||||||
|
Address pltEntry = space.getAddress(0x00500000);
|
||||||
|
Assembler asm = Assemblers.getAssembler(language);
|
||||||
|
AssemblyBuffer pltAsm = new AssemblyBuffer(asm, pltEntry);
|
||||||
|
|
||||||
|
Register regCtx = language.getContextBaseRegister();
|
||||||
|
Register regT = language.getRegister("T");
|
||||||
|
RegisterValue rvDefault = new RegisterValue(regCtx, pltAsm.getAssembler()
|
||||||
|
.getContextAt(pltAsm.getNext())
|
||||||
|
.toBigInteger(regCtx.getNumBytes()));
|
||||||
|
RegisterValue rvThumb = rvDefault.assign(regT, BigInteger.ONE);
|
||||||
|
AssemblyPatternBlock ctxThumb = AssemblyPatternBlock.fromRegisterValue(rvThumb);
|
||||||
|
|
||||||
|
Address gotThumbFunc = space.getAddress(0x00510234);
|
||||||
|
|
||||||
|
long gotOffset = gotThumbFunc.getOffset() - pltEntry.getOffset() - 0x10000 - 8;
|
||||||
|
|
||||||
|
pltAsm.assemble("adr r12, 0x%s".formatted(pltEntry.add(8))); //("add r12, pc, #0, 12"); ?
|
||||||
|
pltAsm.assemble("add r12, r12, #0x10000"); // #16, 20");
|
||||||
|
// Assembler bug doesn't allow space in , # in this case
|
||||||
|
pltAsm.assemble("ldr pc, [r12,#0x%x]!".formatted(gotOffset));
|
||||||
|
|
||||||
|
Address funcEntry = space.getAddress(0x00400000);
|
||||||
|
AssemblyBuffer funcAsm = new AssemblyBuffer(asm, funcEntry);
|
||||||
|
|
||||||
|
funcAsm.assemble("adds r0, #1", ctxThumb);
|
||||||
|
Address funcEnd = funcAsm.getNext();
|
||||||
|
|
||||||
|
byte[] pltBytes = pltAsm.getBytes();
|
||||||
|
emu.getSharedState().setVar(pltEntry, pltBytes.length, false, pltBytes);
|
||||||
|
byte[] funcBytes = funcAsm.getBytes();
|
||||||
|
emu.getSharedState().setVar(funcEntry, funcBytes.length, false, funcBytes);
|
||||||
|
// +1 for THUMB mode
|
||||||
|
byte[] gotBytes = arithmetic.fromConst(funcEntry.getOffset() + 1, 4);
|
||||||
|
emu.getSharedState().setVar(gotThumbFunc, gotBytes.length, false, gotBytes);
|
||||||
|
|
||||||
|
PcodeThread<byte[]> thread = emu.newThread();
|
||||||
|
thread.overrideCounter(pltEntry);
|
||||||
|
thread.overrideContextWithDefault();
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.run();
|
||||||
|
}
|
||||||
|
catch (DecodePcodeExecutionException e) {
|
||||||
|
assertEquals(funcEnd, e.getProgramCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r0 = emu.getLanguage().getRegister("r0");
|
||||||
|
assertEquals(1,
|
||||||
|
arithmetic.toLong(thread.getState().getVar(r0, Reason.INSPECT), Purpose.INSPECT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-12
@@ -2213,12 +2213,11 @@ public class JitCodeGeneratorTest extends AbstractJitTest {
|
|||||||
Translation tr = translateLang(ID_TOYBE64, 0x00400000, """
|
Translation tr = translateLang(ID_TOYBE64, 0x00400000, """
|
||||||
imm r0,#123
|
imm r0,#123
|
||||||
add r0,#7
|
add r0,#7
|
||||||
""",
|
""", Map.ofEntries(
|
||||||
Map.ofEntries(
|
Map.entry(0x00400002L, """
|
||||||
Map.entry(0x00400002L, """
|
r1 = sleigh_userop(r0, 4:8);
|
||||||
r1 = sleigh_userop(r0, 4:8);
|
emu_exec_decoded();
|
||||||
emu_exec_decoded();
|
""")));
|
||||||
""")));
|
|
||||||
|
|
||||||
tr.runDecodeErr(0x00400004);
|
tr.runDecodeErr(0x00400004);
|
||||||
assertEquals(123 + 7, tr.getLongRegVal("r0"));
|
assertEquals(123 + 7, tr.getLongRegVal("r0"));
|
||||||
@@ -2230,12 +2229,11 @@ public class JitCodeGeneratorTest extends AbstractJitTest {
|
|||||||
Translation tr = translateLang(ID_TOYBE64, 0x00400000, """
|
Translation tr = translateLang(ID_TOYBE64, 0x00400000, """
|
||||||
imm r0,#123
|
imm r0,#123
|
||||||
add r0,#7
|
add r0,#7
|
||||||
""",
|
""", Map.ofEntries(
|
||||||
Map.ofEntries(
|
Map.entry(0x00400002L, """
|
||||||
Map.entry(0x00400002L, """
|
r1 = sleigh_userop(r0, 4:8);
|
||||||
r1 = sleigh_userop(r0, 4:8);
|
emu_skip_decoded();
|
||||||
emu_skip_decoded();
|
""")));
|
||||||
""")));
|
|
||||||
|
|
||||||
tr.runDecodeErr(0x00400004);
|
tr.runDecodeErr(0x00400004);
|
||||||
assertEquals(123, tr.getLongRegVal("r0"));
|
assertEquals(123, tr.getLongRegVal("r0"));
|
||||||
@@ -2260,4 +2258,57 @@ public class JitCodeGeneratorTest extends AbstractJitTest {
|
|||||||
}).count();
|
}).count();
|
||||||
assertEquals(1, countSCarrys);
|
assertEquals(1, countSCarrys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCtxHazardousFallthrough() throws Exception {
|
||||||
|
Translation tr = translateLang(ID_ARMv8LE, 0x00400000, """
|
||||||
|
mov r0,#6
|
||||||
|
mov r1,#7
|
||||||
|
""", Map.ofEntries(
|
||||||
|
Map.entry(0x00400000L, """
|
||||||
|
setISAMode(1:1);
|
||||||
|
emu_exec_decoded();
|
||||||
|
""")));
|
||||||
|
|
||||||
|
tr.runClean();
|
||||||
|
assertEquals(6, tr.getLongRegVal("r0"));
|
||||||
|
// Should not execute second instruction, because of injected ctx change
|
||||||
|
assertEquals(0, tr.getLongRegVal("r1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCtxMaybeHazardousFallthrough() throws Exception {
|
||||||
|
/**
|
||||||
|
* For this test to produce the "MAYBE" case, the multiple paths have to be
|
||||||
|
* <em>internal</em> to an instruction (or inject). All that logic is only applied on an
|
||||||
|
* instruction-by-instruction basis.
|
||||||
|
*/
|
||||||
|
Translation tr = translateLang(ID_ARMv8LE, 0x00400000, """
|
||||||
|
mov r0,#6
|
||||||
|
mov r1,#7
|
||||||
|
""", Map.ofEntries(
|
||||||
|
Map.entry(0x00400000L, """
|
||||||
|
if (!ZR) goto <skip>;
|
||||||
|
ISAModeSwitch = 1;
|
||||||
|
setISAMode(ISAModeSwitch);
|
||||||
|
<skip>
|
||||||
|
emu_exec_decoded();
|
||||||
|
""")));
|
||||||
|
|
||||||
|
tr.setLongRegVal("r1", 0); // Reset
|
||||||
|
tr.setLongRegVal("ZR", 0);
|
||||||
|
// Since ctx wasn't touched at runtime, we fall out of program
|
||||||
|
tr.runDecodeErr(0x00400008);
|
||||||
|
assertEquals(6, tr.getLongRegVal("r0"));
|
||||||
|
assertEquals(7, tr.getLongRegVal("r1"));
|
||||||
|
assertEquals(0, tr.getLongRegVal("ISAModeSwitch"));
|
||||||
|
|
||||||
|
tr.setLongRegVal("r1", 0); // Reset
|
||||||
|
tr.setLongRegVal("ZR", 1);
|
||||||
|
// Hazard causes exit before 2nd instruction
|
||||||
|
tr.runClean();
|
||||||
|
assertEquals(6, tr.getLongRegVal("r0"));
|
||||||
|
assertEquals(0, tr.getLongRegVal("r1"));
|
||||||
|
assertEquals(1, tr.getLongRegVal("ISAModeSwitch"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user