mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-09 22:17:55 -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");
|
* 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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
* @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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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);
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user