GP-6159: Require op argument in PcodeUseropDefinition.execute().

This commit is contained in:
Dan
2025-12-22 15:57:37 +00:00
parent dc5836119f
commit 7d2a11d803
14 changed files with 379 additions and 97 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
/**

View File

@@ -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);
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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