From f4d25ccebb23f2f2722210aa86a2789eae22d90a Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Wed, 29 Jul 2020 13:39:20 -0400 Subject: [PATCH] InvokeMethodsTest passes --- .../app/util/pcodeInject/InvokeMethods.java | 56 ++++++------ .../app/util/pcodeInject/PcodeOpEmitter.java | 19 +++- .../util/pcodeInject/InvokeMethodsTest.java | 88 +++++++++++-------- .../pcodeInject/ReferenceMethodsTest.java | 4 +- 4 files changed, 97 insertions(+), 70 deletions(-) diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/InvokeMethods.java b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/InvokeMethods.java index 5adc94b485..543a5f9622 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/InvokeMethods.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/InvokeMethods.java @@ -44,16 +44,16 @@ public class InvokeMethods { /** * Emits the pcode for an invoke instruction. + * @param pCode is the pcode accumulator * @param offset - the index of the constant pool element containing a symbolic reference * to a method or a call site specifier. * @param constantPool - the constant pool * @param type - the JavaInvocationType of the invocation - * @return - the pcode as a string */ - public static String getPcodeForInvoke(int offset, AbstractConstantPoolInfoJava[] constantPool, + public static void getPcodeForInvoke(PcodeOpEmitter pCode, int offset, + AbstractConstantPoolInfoJava[] constantPool, JavaInvocationType type) { - StringBuilder pCode = new StringBuilder(); String descriptor = DescriptorDecoder.getDescriptorForInvoke(offset, constantPool, type); List categories = DescriptorDecoder.getParameterCategories(descriptor); @@ -67,33 +67,31 @@ public class InvokeMethods { } emitPcodeToMoveParams(pCode, categories, includeThisPointer, stackPurge); emitPcodeToResolveMethodReference(pCode, offset, constantPool, type); - PcodeTextEmitter.emitIndirectCall(pCode, CALL_TARGET); + pCode.emitIndirectCall(CALL_TARGET); JavaComputationalCategory retType = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(descriptor); switch (retType) { case CAT_1: - PcodeTextEmitter.emitPushCat1Value(pCode, CAT_1_RETURN); + pCode.emitPushCat1Value(CAT_1_RETURN); break; case CAT_2: - PcodeTextEmitter.emitPushCat2Value(pCode, CAT_2_RETURN); + pCode.emitPushCat2Value(CAT_2_RETURN); break; default: break; } - return pCode.toString(); } /** * Emits the pcode for an invoke instruction. + * @param pCode is the pcode accumulator * @param offset - the index of the constant pool element containing a symbolic reference * to a method or a call site specifier. * @param constantPool - the constant pool - * @return - the pcode as a string */ - public static String getPcodeForInvokeDynamic(int offset, + public static void getPcodeForInvokeDynamic(PcodeOpEmitter pCode, int offset, AbstractConstantPoolInfoJava[] constantPool) { - StringBuilder pCode = new StringBuilder(); String invokeDynamicDescriptor = DescriptorDecoder.getDescriptorForInvoke(offset, constantPool, JavaInvocationType.INVOKE_DYNAMIC); List categories = @@ -104,21 +102,20 @@ public class InvokeMethods { emitPcodeToMoveParams(pCode, categories, false, stackPurge); emitPcodeToResolveMethodReference(pCode, offset, constantPool, JavaInvocationType.INVOKE_DYNAMIC); - PcodeTextEmitter.emitIndirectCall(pCode, CALL_TARGET); + pCode.emitIndirectCall(CALL_TARGET); JavaComputationalCategory retType = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(invokeDynamicDescriptor); switch (retType) { case CAT_1: - PcodeTextEmitter.emitPushCat1Value(pCode, CAT_1_RETURN); + pCode.emitPushCat1Value(CAT_1_RETURN); break; case CAT_2: - PcodeTextEmitter.emitPushCat2Value(pCode, CAT_2_RETURN); + pCode.emitPushCat2Value(CAT_2_RETURN); break; default: break; } - return pCode.toString(); } /** @@ -128,25 +125,26 @@ public class InvokeMethods { * @param pCode - the pcode buffer * @param categories - the list of computational categories on the top of the stack * @param includeThisPointer - true if the first element on the stack is an implicit this parameter + * @param totalSize - */ - static void emitPcodeToMoveParams(StringBuilder pCode, + static void emitPcodeToMoveParams(PcodeOpEmitter pCode, List categories, boolean includeThisPointer, int totalSize) { //pop the parameters off of the stack for (int i = categories.size() - 1; i >= 0; --i) { switch (categories.get(i)) { case CAT_1: - PcodeTextEmitter.emitPopCat1Value(pCode, PARAMETER + Integer.toString(i)); + pCode.emitPopCat1Value(PARAMETER + Integer.toString(i)); totalSize -= 4; - PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, + pCode.emitWriteToMemory(PARAM_SPACE, 4, Integer.toString(totalSize) + ":4", PARAMETER + Integer.toString(i)); break; case CAT_2: - PcodeTextEmitter.emitPopCat1Value(pCode, PARAMETER + Integer.toString(i)); - PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, + pCode.emitPopCat1Value(PARAMETER + Integer.toString(i)); + pCode.emitWriteToMemory(PARAM_SPACE, 4, Integer.toString(totalSize - 8) + ":4", PARAMETER + Integer.toString(i)); - PcodeTextEmitter.emitPopCat1Value(pCode, PARAMETER_PART2 + Integer.toString(i)); - PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, + pCode.emitPopCat1Value(PARAMETER_PART2 + Integer.toString(i)); + pCode.emitWriteToMemory(PARAM_SPACE, 4, Integer.toString(totalSize - 4) + ":4", PARAMETER_PART2 + Integer.toString(i)); totalSize -= 8; @@ -157,9 +155,9 @@ public class InvokeMethods { } //pop off the this pointer if there is one if (includeThisPointer) { - PcodeTextEmitter.emitPopCat1Value(pCode, THIS); + pCode.emitPopCat1Value(THIS); totalSize -= 4; - PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, + pCode.emitWriteToMemory(PARAM_SPACE, 4, Integer.toString(totalSize) + ":4", THIS); } @@ -172,31 +170,31 @@ public class InvokeMethods { * @param constantPool - the constant pool * @param type - the type of the invocation */ - static void emitPcodeToResolveMethodReference(StringBuilder pCode, int offset, + static void emitPcodeToResolveMethodReference(PcodeOpEmitter pCode, int offset, AbstractConstantPoolInfoJava[] constantPool, JavaInvocationType type) { switch (type) { case INVOKE_DYNAMIC: - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, + pCode.emitAssignRegisterFromPcodeOpCall(CALL_TARGET, ConstantPoolJava.CPOOL_OP, STATIC_OFFSET, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKEDYNAMIC); break; case INVOKE_INTERFACE: - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, + pCode.emitAssignRegisterFromPcodeOpCall(CALL_TARGET, ConstantPoolJava.CPOOL_OP, THIS, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKEINTERFACE); break; case INVOKE_SPECIAL: - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, + pCode.emitAssignRegisterFromPcodeOpCall(CALL_TARGET, ConstantPoolJava.CPOOL_OP, THIS, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKESPECIAL); break; case INVOKE_STATIC: - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, + pCode.emitAssignRegisterFromPcodeOpCall(CALL_TARGET, ConstantPoolJava.CPOOL_OP, STATIC_OFFSET, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKESTATIC); break; case INVOKE_VIRTUAL: - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, + pCode.emitAssignRegisterFromPcodeOpCall(CALL_TARGET, ConstantPoolJava.CPOOL_OP, THIS, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKEVIRTUAL); break; diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/PcodeOpEmitter.java b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/PcodeOpEmitter.java index 1500173dc3..3c2fd13dca 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/PcodeOpEmitter.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/PcodeOpEmitter.java @@ -114,6 +114,13 @@ public class PcodeOpEmitter { defSpaceId = getConstant(defSpace.getSpaceID(), 8); } + public void defineTemp(String name, int size) { + Varnode vn = findVarnode(name, size); + if (!vn.isUnique() || vn.getSize() != size) { + throw new IllegalArgumentException("Name is already assigned: " + name); + } + } + /** * Emits pcode to push a value of computational category 1 onto the stack. * @param valueName - name of varnode to push. @@ -253,7 +260,7 @@ public class PcodeOpEmitter { * @param pcodeop * @param args */ - public void emitAssignRegisterFromPcodeOpCall(StringBuilder pCode, String register, + public void emitAssignRegisterFromPcodeOpCall(String register, String pcodeop, String... args) { Symbol useropSym = language.getSymbolTable().findGlobalSymbol(pcodeop); Varnode out = findRegister(register); @@ -290,7 +297,15 @@ public class PcodeOpEmitter { AddressSpace spc = language.getAddressFactory().getAddressSpace(space); // TODO: find correct space id in[0] = getConstant(spc.getSpaceID(), 8); - in[1] = findRegister(offset); + if (offset.charAt(0) <= '9') { + String[] piece = offset.split(":"); + int sz = Integer.parseInt(piece[1]); + long val = Long.decode(piece[0]); + in[1] = getConstant(val, sz); + } + else { + in[1] = findRegister(offset); + } in[2] = findVarnode(value, size); PcodeOp op = new PcodeOp(opAddress, seqnum++, PcodeOp.STORE, in); opList.add(op); diff --git a/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/InvokeMethodsTest.java b/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/InvokeMethodsTest.java index 59f282d342..b5d6b96bd8 100644 --- a/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/InvokeMethodsTest.java +++ b/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/InvokeMethodsTest.java @@ -21,12 +21,27 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.junit.Before; import org.junit.Test; +import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.javaclass.format.DescriptorDecoder; import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava; +import ghidra.program.model.address.Address; +import ghidra.program.model.lang.LanguageID; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; -public class InvokeMethodsTest { +public class InvokeMethodsTest extends AbstractGhidraHeadlessIntegrationTest { + + private SleighLanguage language; + private Address opAddress; + + @Before + public void setUp() throws Exception { + language = + (SleighLanguage) getLanguageService().getLanguage(new LanguageID("JVM:BE:32:default")); + opAddress = language.getAddressFactory().getDefaultAddressSpace().getAddress(0x10000); + } @Test public void testEmitPcodeToResolveMethodReferenceInvokeDynamic() throws IOException { @@ -43,15 +58,14 @@ public class InvokeMethodsTest { byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile); AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes); - StringBuilder pCode = new StringBuilder(); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_DYNAMIC); - StringBuilder expected = new StringBuilder(); - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); + expected.emitAssignRegisterFromPcodeOpCall(InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_INVOKEDYNAMIC); - assertEquals("incorrect pcode for dynamic invocation", expected.toString(), - pCode.toString()); + assertEquals("incorrect pcode for dynamic invocation", expected, pCode); } @Test @@ -70,16 +84,17 @@ public class InvokeMethodsTest { byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile); AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes); - StringBuilder pCode = new StringBuilder(); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); + pCode.defineTemp(InvokeMethods.THIS, 4); InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_INTERFACE); - StringBuilder expected = new StringBuilder(); - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); + expected.defineTemp(InvokeMethods.THIS, 4); + expected.emitAssignRegisterFromPcodeOpCall(InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, InvokeMethods.THIS, "1", ConstantPoolJava.CPOOL_INVOKEINTERFACE); - assertEquals("incorrect pcode for interface method invocation", expected.toString(), - pCode.toString()); + assertEquals("incorrect pcode for interface method invocation", expected, pCode); } @Test @@ -98,16 +113,17 @@ public class InvokeMethodsTest { byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile); AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes); - StringBuilder pCode = new StringBuilder(); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); + pCode.defineTemp(InvokeMethods.THIS, 4); InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_SPECIAL); - StringBuilder expected = new StringBuilder(); - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); + expected.defineTemp(InvokeMethods.THIS, 4); + expected.emitAssignRegisterFromPcodeOpCall(InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, InvokeMethods.THIS, "1", ConstantPoolJava.CPOOL_INVOKESPECIAL); - assertEquals("incorrect pcode for special method invocation", expected.toString(), - pCode.toString()); + assertEquals("incorrect pcode for special method invocation", expected, pCode); } @Test @@ -126,15 +142,14 @@ public class InvokeMethodsTest { byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile); AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes); - StringBuilder pCode = new StringBuilder(); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_STATIC); - StringBuilder expected = new StringBuilder(); - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); + expected.emitAssignRegisterFromPcodeOpCall(InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_INVOKESTATIC); - assertEquals("incorrect pcode for static method invocation", expected.toString(), - pCode.toString()); + assertEquals("incorrect pcode for static method invocation", expected, pCode); } @Test @@ -153,25 +168,25 @@ public class InvokeMethodsTest { byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile); AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes); - StringBuilder pCode = new StringBuilder(); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); + pCode.defineTemp(InvokeMethods.THIS, 4); InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_VIRTUAL); - StringBuilder expected = new StringBuilder(); - PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); + expected.defineTemp(InvokeMethods.THIS, 4); + expected.emitAssignRegisterFromPcodeOpCall(InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, InvokeMethods.THIS, "1", ConstantPoolJava.CPOOL_INVOKEVIRTUAL); - assertEquals("incorrect pcode for static method invocation", expected.toString(), - pCode.toString()); + assertEquals("incorrect pcode for static method invocation", expected, pCode); } @Test public void testEmitPcodeToReverseStackNoParamsNoThis() { - StringBuilder pCode = new StringBuilder(); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); //InvokeMethods.emitPcodeToReverseStack(pCode, new ArrayList(), false); - StringBuilder expected = new StringBuilder(); - assertEquals("incorrect pcode reversing stack: no params no this", expected.toString(), - pCode.toString()); + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); + assertEquals("incorrect pcode reversing stack: no params no this", expected, pCode); } //test bad category @@ -190,10 +205,10 @@ public class InvokeMethodsTest { byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile); AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes); - String pCode = - InvokeMethods.getPcodeForInvoke(1, constantPool, JavaInvocationType.INVOKE_DYNAMIC); + PcodeOpEmitter pCode = new PcodeOpEmitter(language, opAddress); + InvokeMethods.getPcodeForInvoke(pCode, 1, constantPool, JavaInvocationType.INVOKE_DYNAMIC); - StringBuilder expected = new StringBuilder(); + PcodeOpEmitter expected = new PcodeOpEmitter(language, opAddress); String descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool, JavaInvocationType.INVOKE_DYNAMIC); List categories = @@ -202,11 +217,10 @@ public class InvokeMethodsTest { InvokeMethods.emitPcodeToMoveParams(expected, categories, false, 24); InvokeMethods.emitPcodeToResolveMethodReference(expected, 1, constantPool, JavaInvocationType.INVOKE_DYNAMIC); - PcodeTextEmitter.emitIndirectCall(expected, InvokeMethods.CALL_TARGET); - PcodeTextEmitter.emitPushCat1Value(expected, InvokeMethods.CAT_1_RETURN); - - assertEquals("incorrect pcode for invoke dynamic: (JJII)I", expected.toString(), pCode); + expected.emitIndirectCall(InvokeMethods.CALL_TARGET); + expected.emitPushCat1Value(InvokeMethods.CAT_1_RETURN); + assertEquals("incorrect pcode for invoke dynamic: (JJII)I", expected, pCode); } } diff --git a/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/ReferenceMethodsTest.java b/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/ReferenceMethodsTest.java index 2fa09d2b23..3cf46c0fb9 100644 --- a/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/ReferenceMethodsTest.java +++ b/Ghidra/Processors/JVM/src/test/java/ghidra/app/util/pcodeInject/ReferenceMethodsTest.java @@ -31,8 +31,8 @@ import ghidra.test.AbstractGhidraHeadlessIntegrationTest; public class ReferenceMethodsTest extends AbstractGhidraHeadlessIntegrationTest { - SleighLanguage language; - Address opAddress; + private SleighLanguage language; + private Address opAddress; public ReferenceMethodsTest() {