mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-08 21:47:59 -05:00
GP-6159: Require op argument in PcodeUseropDefinition.execute().
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -59,7 +59,8 @@ public abstract class AnnotatedEmuSyscallUseropLibrary<T> extends AnnotatedPcode
|
||||
* An annotation to export a method as a system call in the library.
|
||||
*
|
||||
* <p>
|
||||
* The method must also be exported in the userop library, likely via {@link PcodeUserop}.
|
||||
* The method must also be exported in the userop library, likely via
|
||||
* {@link ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.PcodeUserop @PcodeUserop}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@@ -120,12 +121,14 @@ public abstract class AnnotatedEmuSyscallUseropLibrary<T> extends AnnotatedPcode
|
||||
/**
|
||||
* Export a userop as a system call
|
||||
*
|
||||
* @param number the opIndex assigned to the userop
|
||||
* @param opdef the userop
|
||||
* @param convention the syscall calling convention for the emulated platform
|
||||
* @return the syscall definition
|
||||
*/
|
||||
public UseropEmuSyscallDefinition<T> newBoundSyscall(PcodeUseropDefinition<T> opdef,
|
||||
PrototypeModel convention) {
|
||||
return new UseropEmuSyscallDefinition<>(opdef, program, convention, dtMachineWord);
|
||||
public UseropEmuSyscallDefinition<T> newBoundSyscall(long number,
|
||||
PcodeUseropDefinition<T> opdef, PrototypeModel convention) {
|
||||
return new UseropEmuSyscallDefinition<>(number, opdef, program, convention, dtMachineWord);
|
||||
}
|
||||
|
||||
protected void mapAndBindSyscalls(Class<?> cls) {
|
||||
@@ -149,7 +152,7 @@ public abstract class AnnotatedEmuSyscallUseropLibrary<T> extends AnnotatedPcode
|
||||
}
|
||||
PrototypeModel convention = mapConventions.get(number);
|
||||
EmuSyscallDefinition<T> existed =
|
||||
syscallMap.put(number, newBoundSyscall(opdef, convention));
|
||||
syscallMap.put(number, newBoundSyscall(number, opdef, convention));
|
||||
if (existed != null) {
|
||||
throw new IllegalArgumentException("Duplicate @" +
|
||||
EmuSyscall.class.getSimpleName() + " annotated methods with name " + name);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -22,11 +22,12 @@ import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.pcode.emu.sys.EmuSyscallLibrary.EmuSyscallDefinition;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.pcode.*;
|
||||
|
||||
/**
|
||||
* A system call that is defined by delegating to a p-code userop
|
||||
@@ -56,6 +57,7 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
|
||||
return dtPointer;
|
||||
}
|
||||
|
||||
protected final PcodeOp op; // fabricated for analyses that provide originating op info
|
||||
protected final PcodeUseropDefinition<T> opdef;
|
||||
protected final List<Varnode> inVars;
|
||||
protected final Varnode outVar;
|
||||
@@ -64,12 +66,13 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
|
||||
* Construct a syscall definition
|
||||
*
|
||||
* @see AnnotatedEmuSyscallUseropLibrary
|
||||
* @param number the opIndex assigned to this userop
|
||||
* @param opdef the wrapped userop definition
|
||||
* @param program the program, used for storage computation
|
||||
* @param convention the "syscall" calling convention
|
||||
* @param dtMachineWord the "pointer" data type
|
||||
*/
|
||||
public UseropEmuSyscallDefinition(PcodeUseropDefinition<T> opdef, Program program,
|
||||
public UseropEmuSyscallDefinition(long number, PcodeUseropDefinition<T> opdef, Program program,
|
||||
PrototypeModel convention, DataType dtMachineWord) {
|
||||
this.opdef = opdef;
|
||||
|
||||
@@ -87,9 +90,16 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
|
||||
|
||||
outVar = getSingleVnStorage(vss[0]);
|
||||
inVars = Arrays.asList(new Varnode[inputCount]);
|
||||
Varnode[] opIns = new Varnode[inputCount + 1];
|
||||
opIns[0] = new Varnode(program.getAddressFactory().getConstantAddress(number), 4);
|
||||
for (int i = 0; i < inputCount; i++) {
|
||||
inVars.set(i, getSingleVnStorage(vss[i + 1]));
|
||||
Varnode vnIn = getSingleVnStorage(vss[i + 1]);
|
||||
inVars.set(i, vnIn);
|
||||
opIns[i + 1] = vnIn;
|
||||
}
|
||||
|
||||
op = new PcodeOp(new SequenceNumber(Address.NO_ADDRESS, 0), PcodeOp.CALLOTHER, opIns,
|
||||
outVar);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +119,7 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
|
||||
@Override
|
||||
public void invoke(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library) {
|
||||
try {
|
||||
opdef.execute(executor, library, outVar, inVars);
|
||||
opdef.execute(executor, library, op, outVar, inVars);
|
||||
}
|
||||
catch (PcodeExecutionException e) {
|
||||
throw e;
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.util.*;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Ent;
|
||||
import ghidra.pcode.emu.jit.gen.util.Emitter.Next;
|
||||
import ghidra.pcode.emu.jit.gen.util.Methods.Inv;
|
||||
import ghidra.pcode.emu.jit.gen.util.Types.TRef;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
public class FieldForPcodeOp implements StaticFieldReq<TRef<PcodeOp>> {
|
||||
|
||||
static String nameVn(Varnode vn) {
|
||||
if (vn == null) {
|
||||
return "null";
|
||||
}
|
||||
return "%s_%x_%d".formatted(vn.getAddress().getAddressSpace().getName(), vn.getOffset(),
|
||||
vn.getSize());
|
||||
}
|
||||
|
||||
static String nameInputs(Varnode[] inputs) {
|
||||
return Stream.of(inputs).map(FieldForPcodeOp::nameVn).collect(Collectors.joining("__"));
|
||||
}
|
||||
|
||||
private final PcodeOp op;
|
||||
private final FieldForVarnode outVnReq;
|
||||
private final List<FieldForVarnode> inVnReqs;
|
||||
|
||||
public FieldForPcodeOp(JitCodeGenerator<?> gen, PcodeOp op) {
|
||||
this.op = op;
|
||||
|
||||
this.outVnReq =
|
||||
op.getOutput() == null ? null : gen.requestStaticFieldForVarnode(op.getOutput());
|
||||
this.inVnReqs = Stream.of(op.getInputs()).map(gen::requestStaticFieldForVarnode).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "%s__%s__%s".formatted(nameVn(op.getOutput()), op.getMnemonic(),
|
||||
nameInputs(op.getInputs()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<N> genClInitCode(Emitter<N> em, JitCodeGenerator<?> gen,
|
||||
ClassVisitor cv) {
|
||||
Fld.decl(cv, ACC_PRIVATE | ACC_STATIC | ACC_FINAL, GenConsts.T_PCODE_OP, name());
|
||||
|
||||
var emIns = em
|
||||
.emit(gen::genAddress, op.getSeqnum().getTarget())
|
||||
.emit(Op::ldc__i, op.getSeqnum().getTime())
|
||||
.emit(Op::ldc__i, op.getOpcode())
|
||||
.emit(Op::ldc__i, op.getNumInputs())
|
||||
.emit(Op::anewarray, T_VARNODE);
|
||||
for (int i = 0; i < op.getNumInputs(); i++) {
|
||||
emIns = emIns
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__i, i)
|
||||
.emit(inVnReqs.get(i)::genLoad, gen)
|
||||
.emit(Op::aastore);
|
||||
}
|
||||
var emOut = outVnReq == null
|
||||
? emIns.emit(Op::aconst_null, T_VARNODE)
|
||||
: emIns.emit(outVnReq::genLoad, gen);
|
||||
return emOut
|
||||
.emit(Op::invokestatic, T_JIT_COMPILED_PASSAGE, "createOp",
|
||||
MDESC_JIT_COMPILED_PASSAGE__CREATE_OP, true)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::ret)
|
||||
.emit(Op::putstatic, gen.typeThis, name(), T_PCODE_OP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Next> Emitter<Ent<N, TRef<PcodeOp>>> genLoad(Emitter<N> em,
|
||||
JitCodeGenerator<?> gen) {
|
||||
return em
|
||||
.emit(Op::getstatic, gen.typeThis, name(), T_PCODE_OP);
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
* @param userop the definition to pre-fetch
|
||||
* @see JitDataFlowUseropLibrary
|
||||
*/
|
||||
public record FieldForUserop(PcodeUseropDefinition<?> userop)
|
||||
implements InstanceFieldReq<TRef<PcodeUseropDefinition<?>>> {
|
||||
public record FieldForUserop(PcodeUseropDefinition<byte[]> userop)
|
||||
implements InstanceFieldReq<TRef<PcodeUseropDefinition<byte[]>>> {
|
||||
@Override
|
||||
public String name() {
|
||||
return "userop_" + userop.getName();
|
||||
@@ -81,10 +81,10 @@ public record FieldForUserop(PcodeUseropDefinition<?> userop)
|
||||
|
||||
@Override
|
||||
public <THIS extends JitCompiledPassage, N extends Next>
|
||||
Emitter<Ent<N, TRef<PcodeUseropDefinition<?>>>>
|
||||
Emitter<Ent<N, TRef<PcodeUseropDefinition<byte[]>>>>
|
||||
genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
|
||||
return em
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(Op::getfield, gen.typeThis, name(), T_PCODE_USEROP_DEFINITION);
|
||||
.emit(Op::getfield, gen.typeThis, name(), T_PCODE_USEROP_DEFINITION__BYTEARR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
@@ -86,10 +87,14 @@ public interface GenConsts {
|
||||
public static final TRef<LowlevelError> T_LOWLEVEL_ERROR = Types.refOf(LowlevelError.class);
|
||||
public static final TRef<Math> T_MATH = Types.refOf(Math.class);
|
||||
public static final TRef<Object> T_OBJECT = Types.refOf(Object.class);
|
||||
public static final TRef<PcodeOp> T_PCODE_OP = Types.refOf(PcodeOp.class);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<PcodeUseropDefinition<?>> T_PCODE_USEROP_DEFINITION =
|
||||
(TRef) Types.refOf(PcodeUseropDefinition.class);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<PcodeUseropDefinition<byte[]>> T_PCODE_USEROP_DEFINITION__BYTEARR =
|
||||
(TRef) T_PCODE_USEROP_DEFINITION;
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static final TRef<PcodeUseropLibrary<?>> T_PCODE_USEROP_LIBRARY =
|
||||
(TRef) Types.refOf(PcodeUseropLibrary.class);
|
||||
public static final TRef<PrintStream> T_PRINT_STREAM = Types.refOf(PrintStream.class);
|
||||
@@ -189,6 +194,17 @@ public interface GenConsts {
|
||||
public static final MthDesc<TRef<ExitSlot>,
|
||||
Ent<Ent<Bot, TLong>, TRef<RegisterValue>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT =
|
||||
MthDesc.returns(T_EXIT_SLOT).param(Types.T_LONG).param(T_REGISTER_VALUE).build();
|
||||
public static final MthDesc<TRef<PcodeOp>,
|
||||
Ent<Ent<Ent<Ent<Ent<Bot, TRef<Address>>, TInt>, TInt>, TRef<Varnode[]>>,
|
||||
TRef<Varnode>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_OP =
|
||||
MthDesc.derive(JitCompiledPassage::createOp)
|
||||
.check(MthDesc::returns, T_PCODE_OP)
|
||||
.check(MthDesc::param, T_ADDRESS)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, Types.T_INT)
|
||||
.check(MthDesc::param, TARR_VARNODE)
|
||||
.check(MthDesc::param, T_VARNODE)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TRef<Varnode>,
|
||||
Ent<Ent<Ent<Ent<Bot, TRef<AddressFactory>>, TRef<String>>, TLong>,
|
||||
TInt>> MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE =
|
||||
@@ -204,17 +220,20 @@ public interface GenConsts {
|
||||
public static final MthDesc<TRef<Language>,
|
||||
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE =
|
||||
MthDesc.returns(T_LANGUAGE).param(T_STRING).build();
|
||||
public static final MthDesc<TRef<PcodeUseropDefinition<?>>,
|
||||
public static final MthDesc<TRef<PcodeUseropDefinition<byte[]>>,
|
||||
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_USEROP_DEFINITION =
|
||||
MthDesc.returns(T_PCODE_USEROP_DEFINITION).param(T_STRING).build();
|
||||
MthDesc.deriveInst(JitCompiledPassage::getUseropDefinition)
|
||||
.check(MthDesc::returns, T_PCODE_USEROP_DEFINITION__BYTEARR)
|
||||
.check(MthDesc::param, T_STRING)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Bot, TRef<PcodeUseropDefinition<?>>>, TRef<Varnode>>,
|
||||
TRef<Varnode[]>>> MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
|
||||
MthDesc.returns(Types.T_VOID)
|
||||
.param(T_PCODE_USEROP_DEFINITION)
|
||||
.param(T_VARNODE)
|
||||
.param(TARR_VARNODE)
|
||||
.build();
|
||||
Ent<Ent<Bot, TRef<PcodeUseropDefinition<byte[]>>>,
|
||||
TRef<PcodeOp>>> MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
|
||||
MthDesc.deriveInst(JitCompiledPassage::invokeUserop)
|
||||
.check(MthDesc::returns, Types.T_VOID)
|
||||
.check(MthDesc::param, T_PCODE_USEROP_DEFINITION__BYTEARR)
|
||||
.check(MthDesc::param, T_PCODE_OP)
|
||||
.check(MthDesc::build);
|
||||
public static final MthDesc<TVoid,
|
||||
Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>,
|
||||
TRef<int[]>>> MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP =
|
||||
@@ -346,7 +365,6 @@ public interface GenConsts {
|
||||
MthDesc.returns(Types.T_VOID).param(T_STRING).build();
|
||||
public static final MthDesc<TRef<String>, Ent<Bot, TRef<Object[]>>> MDESC_STRING__FORMATTED =
|
||||
MthDesc.returns(T_STRING).param(TARR_OBJECT).build();
|
||||
|
||||
public static final MthDesc<TDouble, Ent<Bot, TDouble>> MDESC_$DOUBLE_UNOP =
|
||||
MthDesc.returns(Types.T_DOUBLE).param(Types.T_DOUBLE).build();
|
||||
public static final MthDesc<TFloat, Ent<Bot, TFloat>> MDESC_$FLOAT_UNOP =
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.objectweb.asm.Opcodes.*;
|
||||
import java.io.*;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.reflect.TypeLiteral;
|
||||
import org.objectweb.asm.*;
|
||||
@@ -198,7 +199,23 @@ public class JitCodeGenerator<THIS extends JitCompiledPassage> {
|
||||
* @param vn the varnode
|
||||
*/
|
||||
public VarnodeKey(Varnode vn) {
|
||||
this(vn.getSpace(), vn.getOffset(), vn.getSize());
|
||||
this(vn == null ? 0 : vn.getSpace(), vn == null ? 0 : vn.getOffset(),
|
||||
vn == null ? 0 : vn.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The key for a p-code op, to ensure we control "equality"
|
||||
*/
|
||||
record PcodeOpKey(VarnodeKey out, int opcode, List<VarnodeKey> ins) {
|
||||
/**
|
||||
* Extract/construct thhe key for a given op
|
||||
*
|
||||
* @param op the p-code op
|
||||
*/
|
||||
public PcodeOpKey(PcodeOp op) {
|
||||
this(new VarnodeKey(op.getOutput()), op.getOpcode(),
|
||||
Stream.of(op.getInputs()).map(VarnodeKey::new).toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +236,7 @@ public class JitCodeGenerator<THIS extends JitCompiledPassage> {
|
||||
private final Map<Address, FieldForArrDirect> fieldsForArrDirect = new HashMap<>();
|
||||
private final Map<RegisterValue, FieldForContext> fieldsForContext = new HashMap<>();
|
||||
private final Map<VarnodeKey, FieldForVarnode> fieldsForVarnode = new HashMap<>();
|
||||
private final Map<PcodeOpKey, FieldForPcodeOp> fieldsForOp = new HashMap<>();
|
||||
private final Map<String, FieldForUserop> fieldsForUserop = new HashMap<>();
|
||||
private final Map<AddrCtx, FieldForExitSlot> fieldsForExitSlot = new HashMap<>();
|
||||
|
||||
@@ -507,13 +525,25 @@ public class JitCodeGenerator<THIS extends JitCompiledPassage> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a field for the given p-code op
|
||||
* <p>
|
||||
* This will request fields for each varnode for the op's operands
|
||||
*
|
||||
* @param op the p-code op
|
||||
* @return the field request
|
||||
*/
|
||||
public FieldForPcodeOp requestStaticFieldForOp(PcodeOp op) {
|
||||
return fieldsForOp.computeIfAbsent(new PcodeOpKey(op), ok -> new FieldForPcodeOp(this, op));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a field for the given userop
|
||||
*
|
||||
* @param userop the userop
|
||||
* @return the field request
|
||||
*/
|
||||
public FieldForUserop requestFieldForUserop(PcodeUseropDefinition<?> userop) {
|
||||
public FieldForUserop requestFieldForUserop(PcodeUseropDefinition<byte[]> userop) {
|
||||
return fieldsForUserop.computeIfAbsent(userop.getName(), n -> {
|
||||
FieldForUserop f = new FieldForUserop(userop);
|
||||
return f;
|
||||
@@ -990,6 +1020,9 @@ public class JitCodeGenerator<THIS extends JitCompiledPassage> {
|
||||
for (FieldForVarnode fVn : fieldsForVarnode.values()) {
|
||||
em = fVn.genClInitCode(em, this, cv);
|
||||
}
|
||||
for (FieldForPcodeOp fOp : fieldsForOp.values()) {
|
||||
em = fOp.genClInitCode(em, this, cv);
|
||||
}
|
||||
return em;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import ghidra.pcode.emu.jit.gen.util.Types.BNonVoid;
|
||||
* @param <T> the JVM type of the field
|
||||
*/
|
||||
public interface StaticFieldReq<T extends BNonVoid> extends FieldReq<T> {
|
||||
|
||||
/**
|
||||
* Emit the field declaration and its initialization bytecode
|
||||
*
|
||||
|
||||
@@ -48,7 +48,6 @@ import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.OpOutput;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* The generator for a {@link JitCallOtherOpIf callother}.
|
||||
@@ -66,12 +65,10 @@ import ghidra.program.model.pcode.Varnode;
|
||||
*
|
||||
* <p>
|
||||
* For the Standard strategy, we emit code to retire the program counter, decode context, and all
|
||||
* live variables. We then request a field to hold the userop and emit code to load it. We then emit
|
||||
* code to prepare its arguments and place them on the stack, namely the output varnode and an array
|
||||
* for the input varnodes. We request a field for each varnode and emit code to load them as needed.
|
||||
* For the array, we emit code to construct and fill it. We then emit code to invoke
|
||||
* {@link JitCompiledPassage#invokeUserop(PcodeUseropDefinition, Varnode, Varnode[])}. The userop
|
||||
* definition handles retrieving all of its inputs and writing the output, directly to the
|
||||
* live variables. We then request a field to hold the {@link PcodeOp#CALLOTHER} p-code op and the
|
||||
* userop, and emit code to load them. We then emit code to invoke
|
||||
* {@link JitCompiledPassage#invokeUserop(PcodeUseropDefinition, PcodeOp)}. The userop definition
|
||||
* handles retrieving all of its inputs and writing the output, directly to the
|
||||
* {@link JitBytesPcodeExecutorState state}. Thus, we now need only to emit code to re-birth all the
|
||||
* live variables. If any errors occur, execution is interrupted as usual, and our state is
|
||||
* consistent.
|
||||
@@ -92,16 +89,6 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
/** The generator singleton */
|
||||
GEN;
|
||||
|
||||
private static <THIS extends JitCompiledPassage, N extends Next> Emitter<Ent<N, TRef<Varnode>>>
|
||||
genLoadVarnodeOrNull(Emitter<N> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, Varnode vn) {
|
||||
if (vn == null) {
|
||||
return em.emit(Op::aconst_null, T_VARNODE);
|
||||
}
|
||||
FieldForVarnode field = gen.requestStaticFieldForVarnode(vn);
|
||||
return em.emit(field::genLoad, gen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code to implement the Standard strategy (see the class documentation)
|
||||
*
|
||||
@@ -127,35 +114,25 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
* NOTE: The output variable should be "alive", so we need not store it into a local. It'll
|
||||
* be made alive in the return block transition.
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
FieldForUserop useropField = gen.requestFieldForUserop((PcodeUseropDefinition) userop);
|
||||
FieldForPcodeOp opField = gen.requestStaticFieldForOp(op);
|
||||
|
||||
BlockTransition<THIS> transition =
|
||||
VarGen.computeBlockTransition(localThis, gen, block, null);
|
||||
|
||||
PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op));
|
||||
|
||||
var emArr = em
|
||||
return new LiveOpResult(em
|
||||
.emit(transition::genFwd)
|
||||
.emit(gen::genRetirePcCtx, localThis, pcGen, gen.getExitContext(op),
|
||||
RetireMode.SET)
|
||||
.emit(gen::genRetirePcCtx, localThis, pcGen, gen.getExitContext(op), RetireMode.SET)
|
||||
.emit(Op::aload, localThis)
|
||||
.emit(gen.requestFieldForUserop(userop)::genLoad, localThis, gen)
|
||||
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getOutput())
|
||||
.emit(Op::ldc__i, op.getNumInputs() - 1)
|
||||
.emit(Op::anewarray, T_VARNODE);
|
||||
|
||||
for (int i = 1; i < op.getNumInputs(); i++) {
|
||||
emArr = emArr
|
||||
.emit(Op::dup)
|
||||
.emit(Op::ldc__i, i - 1)
|
||||
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getInput(i))
|
||||
.emit(Op::aastore);
|
||||
}
|
||||
|
||||
return new LiveOpResult(emArr
|
||||
.emit(useropField::genLoad, localThis, gen)
|
||||
.emit(opField::genLoad, gen)
|
||||
.emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "invokeUserop",
|
||||
MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeArg)
|
||||
.step(Inv::takeObjRef)
|
||||
.step(Inv::retVoid)
|
||||
.emit(transition::genInv));
|
||||
@@ -199,10 +176,10 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||
* @return the result of emitting the userop's bytecode
|
||||
*/
|
||||
public static <THIS extends JitCompiledPassage, LIB extends PcodeUseropLibrary<?>> OpResult
|
||||
genRunDirectStrategy(Emitter<Bot> em,
|
||||
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitCallOtherOpIf op,
|
||||
JitBlock block, Scope scope) {
|
||||
FieldForUserop useropField = gen.requestFieldForUserop(op.userop());
|
||||
genRunDirectStrategy(Emitter<Bot> em, Local<TRef<THIS>> localThis,
|
||||
JitCodeGenerator<THIS> gen, JitCallOtherOpIf op, JitBlock block, Scope scope) {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
FieldForUserop useropField = gen.requestFieldForUserop((PcodeUseropDefinition) op.userop());
|
||||
|
||||
// Set<Varnode> live = gen.vsm.getLiveVars(block);
|
||||
/**
|
||||
|
||||
@@ -34,8 +34,7 @@ import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
|
||||
/**
|
||||
@@ -2077,6 +2076,22 @@ public interface JitCompiledPassage {
|
||||
return new Varnode(factory.getAddressSpace(space).getAddress(offset), size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a p-code op
|
||||
*
|
||||
* @param factory the language's address factory
|
||||
* @param space the name of the space of the op's sequence number
|
||||
* @param offset the address offset of the op's sequence number
|
||||
* @param index the index of the op's sequence number
|
||||
* @param op the opcode
|
||||
* @param inputs the inputs
|
||||
* @param output the output
|
||||
* @return the op
|
||||
*/
|
||||
static PcodeOp createOp(Address target, int sq, int opcode, Varnode[] inputs, Varnode output) {
|
||||
return new PcodeOp(new SequenceNumber(target, sq), opcode, inputs, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this instance's bound thread.
|
||||
*
|
||||
@@ -2148,15 +2163,12 @@ public interface JitCompiledPassage {
|
||||
* via the Standard strategy.
|
||||
*
|
||||
* @param userop the userop definition
|
||||
* @param output an optional output operand
|
||||
* @param inputs the input operands
|
||||
* @param op the {@link PcodeOp#CALLOTHER} op
|
||||
* @see JitDataFlowUseropLibrary
|
||||
* @see PcodeUseropDefinition#execute(PcodeExecutor, PcodeUseropLibrary, Varnode, List)
|
||||
* @see PcodeUseropDefinition#execute(PcodeExecutor, PcodeUseropLibrary, PcodeOp)
|
||||
*/
|
||||
default void invokeUserop(PcodeUseropDefinition<byte[]> userop, Varnode output,
|
||||
Varnode[] inputs) {
|
||||
userop.execute(thread().getExecutor(), thread().getUseropLibrary(), output,
|
||||
Arrays.asList(inputs));
|
||||
default void invokeUserop(PcodeUseropDefinition<byte[]> userop, PcodeOp op) {
|
||||
userop.execute(thread().getExecutor(), thread().getUseropLibrary(), op);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,6 +166,24 @@ public interface Methods {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building an instance method descriptor derived from the given method reference
|
||||
* <p>
|
||||
* This implicitly drops the object reference that is normally included in static references
|
||||
* to instance methods.
|
||||
*
|
||||
* @see #derive(Function)
|
||||
* @param <R> the return type, boxed
|
||||
* @param <A0> the object reference type, dropped
|
||||
* @param <A1> another argument type, boxed
|
||||
* @param func the method reference
|
||||
* @return the checked builder
|
||||
*/
|
||||
public static <R, A0, A1> MthDescCheckedBuilderR<R, CkEnt<CkBot, A1>>
|
||||
deriveInst(BiFunction<A0, A1, R> func) {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building a method descriptor derived from the given method reference
|
||||
*
|
||||
@@ -199,6 +217,21 @@ public interface Methods {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building a method descriptor derived from the given method reference
|
||||
*
|
||||
* @see #derive(Function)
|
||||
* @param <A0> the object type, dropped
|
||||
* @param <A1> another argument type, boxed
|
||||
* @param <A2> another argument type, boxed
|
||||
* @param func the method reference
|
||||
* @return the checked builder
|
||||
*/
|
||||
public static <A0, A1, A2> MthDescCheckedBuilderR<Void, CkEnt<CkEnt<CkBot, A2>, A1>>
|
||||
deriveInst(A3Consumer<A0, A1, A2> func) {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building a method descriptor derived from the given method reference
|
||||
*
|
||||
@@ -234,6 +267,61 @@ public interface Methods {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building an instance method descriptor derived from the given method reference
|
||||
*
|
||||
* @see #derive(Function)
|
||||
* @param <A0> the object reference
|
||||
* @param <A1> another argument type, boxed
|
||||
* @param <A2> another argument type, boxed
|
||||
* @param <A3> another argument type, boxed
|
||||
* @param func the method reference
|
||||
* @return the checked builder
|
||||
*/
|
||||
public static <A0, A1, A2, A3>
|
||||
MthDescCheckedBuilderR<Void, CkEnt<CkEnt<CkEnt<CkBot, A3>, A2>, A1>>
|
||||
deriveInst(A4Consumer<A0, A1, A2, A3> func) {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building a method descriptor derived from the given method reference
|
||||
*
|
||||
* @see #derive(Function)
|
||||
* @param <R> the return type, boxed
|
||||
* @param <A0> the first argument type, boxed
|
||||
* @param <A1> another argument type, boxed
|
||||
* @param <A2> another argument type, boxed
|
||||
* @param <A3> another argument type, boxed
|
||||
* @param func the method reference
|
||||
* @return the checked builder
|
||||
*/
|
||||
public static <R, A0, A1, A2, A3, A4>
|
||||
MthDescCheckedBuilderR<R,
|
||||
CkEnt<CkEnt<CkEnt<CkEnt<CkEnt<CkBot, A4>, A3>, A2>, A1>, A0>>
|
||||
derive(A5Function<A0, A1, A2, A3, A4, R> func) {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin building a method descriptor derived from the given method reference
|
||||
*
|
||||
* @see #derive(Function)
|
||||
* @param <R> the return type, boxed
|
||||
* @param <A0> the first argument type, boxed
|
||||
* @param <A1> another argument type, boxed
|
||||
* @param <A2> another argument type, boxed
|
||||
* @param <A3> another argument type, boxed
|
||||
* @param func the method reference
|
||||
* @return the checked builder
|
||||
*/
|
||||
public static <R, A0, A1, A2, A3, A4, A5,
|
||||
A6> MthDescCheckedBuilderR<R,
|
||||
CkEnt<CkEnt<CkEnt<CkEnt<CkEnt<CkEnt<CkEnt<CkBot, A6>, A5>, A4>, A3>, A2>, A1>, A0>>
|
||||
derive(A7Function<A0, A1, A2, A3, A4, A5, A6, R> func) {
|
||||
return new MthDescCheckedBuilderR<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the return type of a checked builder
|
||||
* <p>
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.pcode.exec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.plugin.processors.sleigh.template.OpTpl;
|
||||
@@ -254,4 +255,26 @@ public class PcodeProgram {
|
||||
}
|
||||
return useropNames.get(opNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing/debug only: Get the userop number for a given name
|
||||
*
|
||||
* @implNote There is no index by name, so this exhaustively searches the language- and
|
||||
* library-defined userops.
|
||||
* @param name the name
|
||||
* @return the number, or -1 if not found
|
||||
*/
|
||||
public int getUseropNumber(String name) {
|
||||
for (int i = 0; i < language.getNumberOfUserDefinedOpNames(); i++) {
|
||||
if (name.equals(language.getUserDefinedOpName(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (Entry<Integer, String> ent : useropNames.entrySet()) {
|
||||
if (name.equals(ent.getValue())) {
|
||||
return ent.getKey();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,23 +126,7 @@ public interface PcodeUseropLibrary<T> {
|
||||
* @param executor the executor invoking this userop.
|
||||
* @param library the complete library for this execution. Note the library may have been
|
||||
* composed from more than the one defining this userop.
|
||||
* @param outVar if invoked as an rval, the destination varnode for the userop's output.
|
||||
* Otherwise, {@code null}.
|
||||
* @param inVars the input varnodes as ordered in the source.
|
||||
* @see AnnotatedPcodeUseropLibrary.AnnotatedPcodeUseropDefinition
|
||||
*/
|
||||
default void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
|
||||
Varnode outVar, List<Varnode> inVars) {
|
||||
execute(executor, library, null, outVar, inVars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke/execute the userop.
|
||||
*
|
||||
* @param executor the executor invoking this userop.
|
||||
* @param library the complete library for this execution. Note the library may have been
|
||||
* composed from more than the one defining this userop.
|
||||
* @param op the CALLOTHER p-code op
|
||||
* @param op the {@link PcodeOp#CALLOTHER} op
|
||||
* @param outVar if invoked as an rval, the destination varnode for the userop's output.
|
||||
* Otherwise, {@code null}.
|
||||
* @param inVars the input varnodes as ordered in the source.
|
||||
|
||||
@@ -49,6 +49,7 @@ import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.util.NumericUtilities;
|
||||
@@ -220,12 +221,14 @@ public abstract class AbstractJitCodeGeneratorTest extends AbstractJitTest {
|
||||
|
||||
public static class TestUseropLibrary extends AnnotatedPcodeUseropLibrary<byte[]> {
|
||||
boolean gotJavaUseropCall = false;
|
||||
PcodeOp recordedOp = null;
|
||||
boolean gotFuncUseropCall = false;
|
||||
boolean gotSleighUseropCall = false;
|
||||
|
||||
@PcodeUserop
|
||||
public long java_userop(long a, long b) {
|
||||
public long java_userop(long a, long b, @OpOp PcodeOp op) {
|
||||
gotJavaUseropCall = true;
|
||||
recordedOp = op;
|
||||
return 2 * a + b;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,11 +26,14 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcodeOpKey;
|
||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
||||
import ghidra.pcode.exec.SleighLinkException;
|
||||
import ghidra.pcode.floatformat.FloatFormat;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGeneratorTest {
|
||||
@@ -434,10 +437,21 @@ public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGen
|
||||
Translation tr = translateSleigh(getLanguageID(), """
|
||||
r0 = java_userop(6:8, 2:8);
|
||||
""");
|
||||
AddressFactory factory = tr.program().getLanguage().getAddressFactory();
|
||||
Register regR0 = tr.program().getLanguage().getRegister("r0");
|
||||
|
||||
assertFalse(tr.library().gotJavaUseropCall);
|
||||
tr.runFallthrough();
|
||||
assertTrue(tr.library().gotJavaUseropCall);
|
||||
assertEquals(14, tr.getLongRegVal("r0"));
|
||||
|
||||
int opNo = tr.program().getUseropNumber("java_userop");
|
||||
PcodeOp exp = new PcodeOp(Address.NO_ADDRESS, 0, PcodeOp.CALLOTHER, new Varnode[] {
|
||||
new Varnode(factory.getConstantAddress(opNo), 4),
|
||||
new Varnode(factory.getConstantAddress(6), 8),
|
||||
new Varnode(factory.getConstantAddress(2), 8)
|
||||
}, new Varnode(regR0.getAddress(), regR0.getNumBytes()));
|
||||
assertEquals(new PcodeOpKey(exp), new PcodeOpKey(tr.library().recordedOp));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -445,10 +459,20 @@ public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGen
|
||||
Translation tr = translateSleigh(getLanguageID(), """
|
||||
java_userop(6:8, 2:8);
|
||||
""");
|
||||
AddressFactory factory = tr.program().getLanguage().getAddressFactory();
|
||||
|
||||
assertFalse(tr.library().gotJavaUseropCall);
|
||||
tr.runFallthrough();
|
||||
assertTrue(tr.library().gotJavaUseropCall);
|
||||
assertEquals(0, tr.getLongRegVal("r0"));
|
||||
|
||||
int opNo = tr.program().getUseropNumber("java_userop");
|
||||
PcodeOp exp = new PcodeOp(Address.NO_ADDRESS, 0, PcodeOp.CALLOTHER, new Varnode[] {
|
||||
new Varnode(factory.getConstantAddress(opNo), 4),
|
||||
new Varnode(factory.getConstantAddress(6), 8),
|
||||
new Varnode(factory.getConstantAddress(2), 8)
|
||||
}, null);
|
||||
assertEquals(new PcodeOpKey(exp), new PcodeOpKey(tr.library().recordedOp));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user