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"); * 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.
@@ -59,7 +59,8 @@ public abstract class AnnotatedEmuSyscallUseropLibrary<T> extends AnnotatedPcode
* An annotation to export a method as a system call in the library. * An annotation to export a method as a system call in the library.
* *
* <p> * <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) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@@ -120,12 +121,14 @@ public abstract class AnnotatedEmuSyscallUseropLibrary<T> extends AnnotatedPcode
/** /**
* Export a userop as a system call * Export a userop as a system call
* *
* @param number the opIndex assigned to the userop
* @param opdef the userop * @param opdef the userop
* @param convention the syscall calling convention for the emulated platform
* @return the syscall definition * @return the syscall definition
*/ */
public UseropEmuSyscallDefinition<T> newBoundSyscall(PcodeUseropDefinition<T> opdef, public UseropEmuSyscallDefinition<T> newBoundSyscall(long number,
PrototypeModel convention) { PcodeUseropDefinition<T> opdef, PrototypeModel convention) {
return new UseropEmuSyscallDefinition<>(opdef, program, convention, dtMachineWord); return new UseropEmuSyscallDefinition<>(number, opdef, program, convention, dtMachineWord);
} }
protected void mapAndBindSyscalls(Class<?> cls) { protected void mapAndBindSyscalls(Class<?> cls) {
@@ -149,7 +152,7 @@ public abstract class AnnotatedEmuSyscallUseropLibrary<T> extends AnnotatedPcode
} }
PrototypeModel convention = mapConventions.get(number); PrototypeModel convention = mapConventions.get(number);
EmuSyscallDefinition<T> existed = EmuSyscallDefinition<T> existed =
syscallMap.put(number, newBoundSyscall(opdef, convention)); syscallMap.put(number, newBoundSyscall(number, opdef, convention));
if (existed != null) { if (existed != null) {
throw new IllegalArgumentException("Duplicate @" + throw new IllegalArgumentException("Duplicate @" +
EmuSyscall.class.getSimpleName() + " annotated methods with name " + name); EmuSyscall.class.getSimpleName() + " annotated methods with name " + name);

View File

@@ -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.
@@ -22,11 +22,12 @@ import ghidra.lifecycle.Unfinished;
import ghidra.pcode.emu.sys.EmuSyscallLibrary.EmuSyscallDefinition; import ghidra.pcode.emu.sys.EmuSyscallLibrary.EmuSyscallDefinition;
import ghidra.pcode.exec.*; import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition; import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage; 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 * 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; return dtPointer;
} }
protected final PcodeOp op; // fabricated for analyses that provide originating op info
protected final PcodeUseropDefinition<T> opdef; protected final PcodeUseropDefinition<T> opdef;
protected final List<Varnode> inVars; protected final List<Varnode> inVars;
protected final Varnode outVar; protected final Varnode outVar;
@@ -64,12 +66,13 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
* Construct a syscall definition * Construct a syscall definition
* *
* @see AnnotatedEmuSyscallUseropLibrary * @see AnnotatedEmuSyscallUseropLibrary
* @param number the opIndex assigned to this userop
* @param opdef the wrapped userop definition * @param opdef the wrapped userop definition
* @param program the program, used for storage computation * @param program the program, used for storage computation
* @param convention the "syscall" calling convention * @param convention the "syscall" calling convention
* @param dtMachineWord the "pointer" data type * @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) { PrototypeModel convention, DataType dtMachineWord) {
this.opdef = opdef; this.opdef = opdef;
@@ -87,9 +90,16 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
outVar = getSingleVnStorage(vss[0]); outVar = getSingleVnStorage(vss[0]);
inVars = Arrays.asList(new Varnode[inputCount]); 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++) { 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 @Override
public void invoke(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library) { public void invoke(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library) {
try { try {
opdef.execute(executor, library, outVar, inVars); opdef.execute(executor, library, op, outVar, inVars);
} }
catch (PcodeExecutionException e) { catch (PcodeExecutionException e) {
throw 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 * @param userop the definition to pre-fetch
* @see JitDataFlowUseropLibrary * @see JitDataFlowUseropLibrary
*/ */
public record FieldForUserop(PcodeUseropDefinition<?> userop) public record FieldForUserop(PcodeUseropDefinition<byte[]> userop)
implements InstanceFieldReq<TRef<PcodeUseropDefinition<?>>> { implements InstanceFieldReq<TRef<PcodeUseropDefinition<byte[]>>> {
@Override @Override
public String name() { public String name() {
return "userop_" + userop.getName(); return "userop_" + userop.getName();
@@ -81,10 +81,10 @@ public record FieldForUserop(PcodeUseropDefinition<?> userop)
@Override @Override
public <THIS extends JitCompiledPassage, N extends Next> 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) { genLoad(Emitter<N> em, Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen) {
return em return em
.emit(Op::aload, localThis) .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.address.*;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; 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<LowlevelError> T_LOWLEVEL_ERROR = Types.refOf(LowlevelError.class);
public static final TRef<Math> T_MATH = Types.refOf(Math.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<Object> T_OBJECT = Types.refOf(Object.class);
public static final TRef<PcodeOp> T_PCODE_OP = Types.refOf(PcodeOp.class);
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
public static final TRef<PcodeUseropDefinition<?>> T_PCODE_USEROP_DEFINITION = public static final TRef<PcodeUseropDefinition<?>> T_PCODE_USEROP_DEFINITION =
(TRef) Types.refOf(PcodeUseropDefinition.class); (TRef) Types.refOf(PcodeUseropDefinition.class);
@SuppressWarnings({ "unchecked", "rawtypes" }) @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 = public static final TRef<PcodeUseropLibrary<?>> T_PCODE_USEROP_LIBRARY =
(TRef) Types.refOf(PcodeUseropLibrary.class); (TRef) Types.refOf(PcodeUseropLibrary.class);
public static final TRef<PrintStream> T_PRINT_STREAM = Types.refOf(PrintStream.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>, public static final MthDesc<TRef<ExitSlot>,
Ent<Ent<Bot, TLong>, TRef<RegisterValue>>> MDESC_JIT_COMPILED_PASSAGE__CREATE_EXIT_SLOT = 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(); 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>, public static final MthDesc<TRef<Varnode>,
Ent<Ent<Ent<Ent<Bot, TRef<AddressFactory>>, TRef<String>>, TLong>, Ent<Ent<Ent<Ent<Bot, TRef<AddressFactory>>, TRef<String>>, TLong>,
TInt>> MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE = TInt>> MDESC_JIT_COMPILED_PASSAGE__CREATE_VARNODE =
@@ -204,17 +220,20 @@ public interface GenConsts {
public static final MthDesc<TRef<Language>, public static final MthDesc<TRef<Language>,
Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE = Ent<Bot, TRef<String>>> MDESC_JIT_COMPILED_PASSAGE__GET_LANGUAGE =
MthDesc.returns(T_LANGUAGE).param(T_STRING).build(); 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 = 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, public static final MthDesc<TVoid,
Ent<Ent<Ent<Bot, TRef<PcodeUseropDefinition<?>>>, TRef<Varnode>>, Ent<Ent<Bot, TRef<PcodeUseropDefinition<byte[]>>>,
TRef<Varnode[]>>> MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP = TRef<PcodeOp>>> MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
MthDesc.returns(Types.T_VOID) MthDesc.deriveInst(JitCompiledPassage::invokeUserop)
.param(T_PCODE_USEROP_DEFINITION) .check(MthDesc::returns, Types.T_VOID)
.param(T_VARNODE) .check(MthDesc::param, T_PCODE_USEROP_DEFINITION__BYTEARR)
.param(TARR_VARNODE) .check(MthDesc::param, T_PCODE_OP)
.build(); .check(MthDesc::build);
public static final MthDesc<TVoid, public static final MthDesc<TVoid,
Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>, Ent<Ent<Ent<Bot, TRef<int[]>>, TRef<int[]>>,
TRef<int[]>>> MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP = 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(); MthDesc.returns(Types.T_VOID).param(T_STRING).build();
public static final MthDesc<TRef<String>, Ent<Bot, TRef<Object[]>>> MDESC_STRING__FORMATTED = public static final MthDesc<TRef<String>, Ent<Bot, TRef<Object[]>>> MDESC_STRING__FORMATTED =
MthDesc.returns(T_STRING).param(TARR_OBJECT).build(); MthDesc.returns(T_STRING).param(TARR_OBJECT).build();
public static final MthDesc<TDouble, Ent<Bot, TDouble>> MDESC_$DOUBLE_UNOP = public static final MthDesc<TDouble, Ent<Bot, TDouble>> MDESC_$DOUBLE_UNOP =
MthDesc.returns(Types.T_DOUBLE).param(Types.T_DOUBLE).build(); MthDesc.returns(Types.T_DOUBLE).param(Types.T_DOUBLE).build();
public static final MthDesc<TFloat, Ent<Bot, TFloat>> MDESC_$FLOAT_UNOP = 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.io.*;
import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodHandles.Lookup;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
import org.apache.commons.lang3.reflect.TypeLiteral; import org.apache.commons.lang3.reflect.TypeLiteral;
import org.objectweb.asm.*; import org.objectweb.asm.*;
@@ -198,7 +199,23 @@ public class JitCodeGenerator<THIS extends JitCompiledPassage> {
* @param vn the varnode * @param vn the varnode
*/ */
public VarnodeKey(Varnode vn) { 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<Address, FieldForArrDirect> fieldsForArrDirect = new HashMap<>();
private final Map<RegisterValue, FieldForContext> fieldsForContext = new HashMap<>(); private final Map<RegisterValue, FieldForContext> fieldsForContext = new HashMap<>();
private final Map<VarnodeKey, FieldForVarnode> fieldsForVarnode = 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<String, FieldForUserop> fieldsForUserop = new HashMap<>();
private final Map<AddrCtx, FieldForExitSlot> fieldsForExitSlot = 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 * Request a field for the given userop
* *
* @param userop the userop * @param userop the userop
* @return the field request * @return the field request
*/ */
public FieldForUserop requestFieldForUserop(PcodeUseropDefinition<?> userop) { public FieldForUserop requestFieldForUserop(PcodeUseropDefinition<byte[]> userop) {
return fieldsForUserop.computeIfAbsent(userop.getName(), n -> { return fieldsForUserop.computeIfAbsent(userop.getName(), n -> {
FieldForUserop f = new FieldForUserop(userop); FieldForUserop f = new FieldForUserop(userop);
return f; return f;
@@ -990,6 +1020,9 @@ public class JitCodeGenerator<THIS extends JitCompiledPassage> {
for (FieldForVarnode fVn : fieldsForVarnode.values()) { for (FieldForVarnode fVn : fieldsForVarnode.values()) {
em = fVn.genClInitCode(em, this, cv); em = fVn.genClInitCode(em, this, cv);
} }
for (FieldForPcodeOp fOp : fieldsForOp.values()) {
em = fOp.genClInitCode(em, this, cv);
}
return em; 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 * @param <T> the JVM type of the field
*/ */
public interface StaticFieldReq<T extends BNonVoid> extends FieldReq<T> { public interface StaticFieldReq<T extends BNonVoid> extends FieldReq<T> {
/** /**
* Emit the field declaration and its initialization bytecode * 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;
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition; import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
/** /**
* The generator for a {@link JitCallOtherOpIf callother}. * The generator for a {@link JitCallOtherOpIf callother}.
@@ -66,12 +65,10 @@ import ghidra.program.model.pcode.Varnode;
* *
* <p> * <p>
* For the Standard strategy, we emit code to retire the program counter, decode context, and all * 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 * live variables. We then request a field to hold the {@link PcodeOp#CALLOTHER} p-code op and the
* code to prepare its arguments and place them on the stack, namely the output varnode and an array * userop, and emit code to load them. We then emit code to invoke
* for the input varnodes. We request a field for each varnode and emit code to load them as needed. * {@link JitCompiledPassage#invokeUserop(PcodeUseropDefinition, PcodeOp)}. The userop definition
* For the array, we emit code to construct and fill it. We then emit code to invoke * handles retrieving all of its inputs and writing the output, directly to the
* {@link JitCompiledPassage#invokeUserop(PcodeUseropDefinition, Varnode, Varnode[])}. 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 * {@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 * live variables. If any errors occur, execution is interrupted as usual, and our state is
* consistent. * consistent.
@@ -92,16 +89,6 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
/** The generator singleton */ /** The generator singleton */
GEN; 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) * 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 * 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. * 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 = BlockTransition<THIS> transition =
VarGen.computeBlockTransition(localThis, gen, block, null); VarGen.computeBlockTransition(localThis, gen, block, null);
PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op)); PcGen pcGen = PcGen.loadOffset(gen.getAddressForOp(op));
var emArr = em return new LiveOpResult(em
.emit(transition::genFwd) .emit(transition::genFwd)
.emit(gen::genRetirePcCtx, localThis, pcGen, gen.getExitContext(op), .emit(gen::genRetirePcCtx, localThis, pcGen, gen.getExitContext(op), RetireMode.SET)
RetireMode.SET)
.emit(Op::aload, localThis) .emit(Op::aload, localThis)
.emit(gen.requestFieldForUserop(userop)::genLoad, localThis, gen) .emit(useropField::genLoad, localThis, gen)
.emit(CallOtherOpGen::genLoadVarnodeOrNull, localThis, gen, op.getOutput()) .emit(opField::genLoad, gen)
.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(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "invokeUserop", .emit(Op::invokeinterface, T_JIT_COMPILED_PASSAGE, "invokeUserop",
MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP) MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP)
.step(Inv::takeArg) .step(Inv::takeArg)
.step(Inv::takeArg) .step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::takeObjRef) .step(Inv::takeObjRef)
.step(Inv::retVoid) .step(Inv::retVoid)
.emit(transition::genInv)); .emit(transition::genInv));
@@ -199,10 +176,10 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
* @return the result of emitting the userop's bytecode * @return the result of emitting the userop's bytecode
*/ */
public static <THIS extends JitCompiledPassage, LIB extends PcodeUseropLibrary<?>> OpResult public static <THIS extends JitCompiledPassage, LIB extends PcodeUseropLibrary<?>> OpResult
genRunDirectStrategy(Emitter<Bot> em, genRunDirectStrategy(Emitter<Bot> em, Local<TRef<THIS>> localThis,
Local<TRef<THIS>> localThis, JitCodeGenerator<THIS> gen, JitCallOtherOpIf op, JitCodeGenerator<THIS> gen, JitCallOtherOpIf op, JitBlock block, Scope scope) {
JitBlock block, Scope scope) { @SuppressWarnings({ "rawtypes", "unchecked" })
FieldForUserop useropField = gen.requestFieldForUserop(op.userop()); FieldForUserop useropField = gen.requestFieldForUserop((PcodeUseropDefinition) op.userop());
// Set<Varnode> live = gen.vsm.getLiveVars(block); // 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.Address;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.*;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.util.DefaultLanguageService; import ghidra.program.util.DefaultLanguageService;
/** /**
@@ -2077,6 +2076,22 @@ public interface JitCompiledPassage {
return new Varnode(factory.getAddressSpace(space).getAddress(offset), size); 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. * Get this instance's bound thread.
* *
@@ -2148,15 +2163,12 @@ public interface JitCompiledPassage {
* via the Standard strategy. * via the Standard strategy.
* *
* @param userop the userop definition * @param userop the userop definition
* @param output an optional output operand * @param op the {@link PcodeOp#CALLOTHER} op
* @param inputs the input operands
* @see JitDataFlowUseropLibrary * @see JitDataFlowUseropLibrary
* @see PcodeUseropDefinition#execute(PcodeExecutor, PcodeUseropLibrary, Varnode, List) * @see PcodeUseropDefinition#execute(PcodeExecutor, PcodeUseropLibrary, PcodeOp)
*/ */
default void invokeUserop(PcodeUseropDefinition<byte[]> userop, Varnode output, default void invokeUserop(PcodeUseropDefinition<byte[]> userop, PcodeOp op) {
Varnode[] inputs) { userop.execute(thread().getExecutor(), thread().getUseropLibrary(), op);
userop.execute(thread().getExecutor(), thread().getUseropLibrary(), output,
Arrays.asList(inputs));
} }
/** /**

View File

@@ -166,6 +166,24 @@ public interface Methods {
return new MthDescCheckedBuilderR<>(); 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 * Begin building a method descriptor derived from the given method reference
* *
@@ -199,6 +217,21 @@ public interface Methods {
return new MthDescCheckedBuilderR<>(); 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 * Begin building a method descriptor derived from the given method reference
* *
@@ -234,6 +267,61 @@ public interface Methods {
return new MthDescCheckedBuilderR<>(); 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 * Specify the return type of a checked builder
* <p> * <p>

View File

@@ -17,6 +17,7 @@ package ghidra.pcode.exec;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.plugin.processors.sleigh.template.OpTpl; import ghidra.app.plugin.processors.sleigh.template.OpTpl;
@@ -254,4 +255,26 @@ public class PcodeProgram {
} }
return useropNames.get(opNo); 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 executor the executor invoking this userop.
* @param library the complete library for this execution. Note the library may have been * @param library the complete library for this execution. Note the library may have been
* composed from more than the one defining this userop. * composed from more than the one defining this userop.
* @param outVar if invoked as an rval, the destination varnode for the userop's output. * @param op the {@link PcodeOp#CALLOTHER} op
* 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 outVar if invoked as an rval, the destination varnode for the userop's output. * @param outVar if invoked as an rval, the destination varnode for the userop's output.
* Otherwise, {@code null}. * Otherwise, {@code null}.
* @param inVars the input varnodes as ordered in the source. * @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.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.program.util.DefaultLanguageService; import ghidra.program.util.DefaultLanguageService;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
@@ -220,12 +221,14 @@ public abstract class AbstractJitCodeGeneratorTest extends AbstractJitTest {
public static class TestUseropLibrary extends AnnotatedPcodeUseropLibrary<byte[]> { public static class TestUseropLibrary extends AnnotatedPcodeUseropLibrary<byte[]> {
boolean gotJavaUseropCall = false; boolean gotJavaUseropCall = false;
PcodeOp recordedOp = null;
boolean gotFuncUseropCall = false; boolean gotFuncUseropCall = false;
boolean gotSleighUseropCall = false; boolean gotSleighUseropCall = false;
@PcodeUserop @PcodeUserop
public long java_userop(long a, long b) { public long java_userop(long a, long b, @OpOp PcodeOp op) {
gotJavaUseropCall = true; gotJavaUseropCall = true;
recordedOp = op;
return 2 * a + b; return 2 * a + b;
} }

View File

@@ -26,11 +26,14 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodInsnNode;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.PcodeOpKey;
import ghidra.pcode.exec.InterruptPcodeExecutionException; import ghidra.pcode.exec.InterruptPcodeExecutionException;
import ghidra.pcode.exec.SleighLinkException; import ghidra.pcode.exec.SleighLinkException;
import ghidra.pcode.floatformat.FloatFormat; import ghidra.pcode.floatformat.FloatFormat;
import ghidra.program.model.lang.Endian; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.LanguageID; 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.Varnode;
public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGeneratorTest { public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGeneratorTest {
@@ -434,10 +437,21 @@ public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGen
Translation tr = translateSleigh(getLanguageID(), """ Translation tr = translateSleigh(getLanguageID(), """
r0 = java_userop(6:8, 2:8); r0 = java_userop(6:8, 2:8);
"""); """);
AddressFactory factory = tr.program().getLanguage().getAddressFactory();
Register regR0 = tr.program().getLanguage().getRegister("r0");
assertFalse(tr.library().gotJavaUseropCall); assertFalse(tr.library().gotJavaUseropCall);
tr.runFallthrough(); tr.runFallthrough();
assertTrue(tr.library().gotJavaUseropCall); assertTrue(tr.library().gotJavaUseropCall);
assertEquals(14, tr.getLongRegVal("r0")); 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 @Test
@@ -445,10 +459,20 @@ public abstract class AbstractToyJitCodeGeneratorTest extends AbstractJitCodeGen
Translation tr = translateSleigh(getLanguageID(), """ Translation tr = translateSleigh(getLanguageID(), """
java_userop(6:8, 2:8); java_userop(6:8, 2:8);
"""); """);
AddressFactory factory = tr.program().getLanguage().getAddressFactory();
assertFalse(tr.library().gotJavaUseropCall); assertFalse(tr.library().gotJavaUseropCall);
tr.runFallthrough(); tr.runFallthrough();
assertTrue(tr.library().gotJavaUseropCall); assertTrue(tr.library().gotJavaUseropCall);
assertEquals(0, tr.getLongRegVal("r0")); 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 @Test