From ea851ffe7b3363a2de1223a40fcce730f1327883 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Thu, 5 Nov 2020 16:22:58 -0500 Subject: [PATCH] GP-272 - Gnu Demangler - updated to handle a lambda function as a function parameter --- .../demangler/gnu/GnuDemanglerParser.java | 36 ++++++++++++++----- .../demangler/GnuDemanglerParserTest.java | 34 ++++++++++++++++++ .../gnu/GnuDemanglerIntegrationTest.java | 31 ++++++++++++++++ 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java index 416f77bfcc..130b834cd6 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java @@ -47,6 +47,7 @@ public class GnuDemanglerParser { private static final String OPERATOR = "operator"; private static final String LAMBDA = "lambda"; + private static final String LAMBDA_START = "{lambda"; private static final String VAR_ARGS = "..."; private static final String THUNK = "thunk"; private static final String CONST = " const"; @@ -60,7 +61,7 @@ public class GnuDemanglerParser { * * Parts: -optional spaces * -optional (const) (non-capture group) - * -followed by '()' with optinal parameter text (capture group 1) + * -followed by '()' with optional parameter text (capture group 1) * * Note: this pattern is used for matching the arguments string, in the above examples it * would be: @@ -191,16 +192,18 @@ public class GnuDemanglerParser { * Pattern for newer C++ lambda syntax: * * Sample: {lambda(void const*, unsigned int)#1} + * {lambda(NS1::Class1 const&, int, int)#1} const& * - * Pattern: [optional text] brace lambda([parameters])#digits brace + * Pattern: [optional text] brace lambda([parameters])#digits brace [optional modifiers] * * Parts: * -full text without leading characters (capture group 1) * -parameters of the lambda function (capture group 2) * -trailing id (capture group 3) + * -trailing modifiers (e.g., const, &) (capture group 4) */ private static final Pattern LAMBDA_PATTERN = - Pattern.compile(".*(\\{" + LAMBDA + "\\((.*)\\)(#\\d+)\\})"); + Pattern.compile(".*(\\{" + LAMBDA + "\\((.*)\\)(#\\d+)\\})(\\s*(?:const|\\*|\\&)*){0,1}"); /* * Sample: {unnamed type#1} @@ -396,7 +399,7 @@ public class GnuDemanglerParser { if (lambdaName != null) { String uniqueName = lambdaName.getFullText(); String escapedLambda = removeBadSpaces(uniqueName); - simpleName = simpleName.replace("{lambda", escapedLambda); + simpleName = simpleName.replace(LAMBDA_START, escapedLambda); function = new DemangledLambda(mangledSource, demangled, null); function.setSignature(lambdaName.getFullText()); } @@ -440,6 +443,7 @@ public class GnuDemanglerParser { } private LambdaName getLambdaName(String name) { + Matcher matcher = LAMBDA_PATTERN.matcher(name); if (!matcher.matches()) { return null; @@ -448,7 +452,8 @@ public class GnuDemanglerParser { String fullText = matcher.group(1); String params = matcher.group(2); String trailing = matcher.group(3); - return new LambdaName(fullText, params, trailing); + String modifiers = matcher.group(4); + return new LambdaName(fullText, params, trailing, modifiers); } private String stripOffTemplates(String string) { @@ -699,7 +704,11 @@ public class GnuDemanglerParser { // e.g., unsigned long (*)(long const &) // array pointer/refs // e.g., short (&)[7] - // + // lambda function + // e.g., {lambda(NS1::Class1 const&, int, int)#1} const& + // + + LambdaName lambdaName = getLambdaName(datatype); // check for array case Matcher arrayMatcher = ARRAY_POINTER_REFERENCE_PATTERN.matcher(datatype); @@ -710,6 +719,13 @@ public class GnuDemanglerParser { ddt.setNamespace(namespace); i = arrayMatcher.end(); } + else if (lambdaName != null) { + String fullText = lambdaName.getFullText(); + ddt.setName(fullText); + int offset = fullText.indexOf('('); + int remaining = fullText.length() - offset; + i = i + remaining; + } else { int startParenCount = StringUtilities.countOccurrences(datatype.substring(i), '('); @@ -1003,7 +1019,8 @@ public class GnuDemanglerParser { List parameters = parseParameters(paramerterString); DemangledFunctionPointer dfp = new DemangledFunctionPointer(mangledSource, demangledSource); - dfp.setReturnType(parseDataType(returnType)); + DemangledDataType returnDataType = parseDataType(returnType); + dfp.setReturnType(returnDataType); for (DemangledDataType parameter : parameters) { dfp.addParameter(parameter); } @@ -1486,11 +1503,13 @@ public class GnuDemanglerParser { private String fullText; private String params; private String trailing; + private String modifiers; - LambdaName(String fullText, String params, String trailing) { + LambdaName(String fullText, String params, String trailing, String modifiers) { this.fullText = fullText; this.params = params; this.trailing = trailing; + this.modifiers = modifiers == null ? "" : modifiers; } String getFullText() { @@ -1503,6 +1522,7 @@ public class GnuDemanglerParser { return builder.append("fullText", fullText) .append("params", params) .append("trailing", trailing) + .append("modifiers", modifiers) .toString(); } } diff --git a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java index af908cb4fc..4c87890528 100644 --- a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java +++ b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java @@ -1460,6 +1460,40 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { signature); } + @Test + public void testFunctionWithLambdaParameter() throws Exception { + + // + // Mangled: _ZN3JSC9Structure3addILNS0_9ShouldPinE1EZNS_8JSObject35prepareToPutDirectWithoutTransitionERNS_2VMENS_12PropertyNameEjjPS0_EUlRKNS_24GCSafeConcurrentJSLockerEiiE_EEiS5_S6_jRKT0_ + // + // Demangled: int + // JSC::Structure::add< + // (JSC::Structure::ShouldPin)1, + // JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&, JSC::PropertyName, unsigned int, unsigned int, JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker const&, int, int)#1} + // >( + // JSC::VM&, JSC::PropertyName, + // unsigned int, + // JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&, JSC::PropertyName, unsigned int, unsigned int, JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker const&, int, int)#1} const& + // ) + // + // + + DemangledObject object = parser.parse( + "_ZN3JSC9Structure3addILNS0_9ShouldPinE1EZNS_8JSObject35prepareToPutDirectWithoutTransitionERNS_2VMENS_12PropertyNameEjjPS0_EUlRKNS_24GCSafeConcurrentJSLockerEiiE_EEiS5_S6_jRKT0_", + "int JSC::Structure::add<(JSC::Structure::ShouldPin)1, JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&, JSC::PropertyName, unsigned int, unsigned int, JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker const&, int, int)#1}>(JSC::VM&, JSC::PropertyName, unsigned int, JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&, JSC::PropertyName, unsigned int, unsigned int, JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker const&, int, int)#1} const&)"); + assertNotNull(object); + assertType(object, DemangledFunction.class); + + String name = + "add<(JSC::Structure::ShouldPin)1,JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&,JSC::PropertyName,unsigned_int,unsigned_int,JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker_const&,int,int)#1}>"; + assertName(object, name, "JSC", "Structure"); + + String signature = object.getSignature(false); + assertEquals( + "int JSC::Structure::add<(JSC::Structure::ShouldPin)1,JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&,JSC::PropertyName,unsigned_int,unsigned_int,JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker_const&,int,int)#1}>(JSC::VM &,JSC::PropertyName,unsigned int,JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&,JSC::PropertyName,unsigned_int,unsigned_int,JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker const&, int, int)#1} const &)", + signature); + } + @Test public void testFunctionInLambdaNamespace() throws Exception { diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/gnu/GnuDemanglerIntegrationTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/gnu/GnuDemanglerIntegrationTest.java index 5de5575264..cee9fe64fa 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/gnu/GnuDemanglerIntegrationTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/gnu/GnuDemanglerIntegrationTest.java @@ -101,6 +101,37 @@ public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrati assertNotNull(cmd.getDemangledObject()); } + @Test + public void testParsingFunctionWithLambdaParameter() throws Exception { + + // + // This shows a bug when applying a function that has as one of its parameters a lambda function + // + + String mangled = + "_ZN3JSC9Structure3addILNS0_9ShouldPinE1EZNS_8JSObject35prepareToPutDirectWithoutTransitionERNS_2VMENS_12PropertyNameEjjPS0_EUlRKNS_24GCSafeConcurrentJSLockerEiiE_EEiS5_S6_jRKT0_"; + + GnuDemangler demangler = new GnuDemangler(); + demangler.canDemangle(program);// this performs initialization + + GnuDemanglerOptions options = new GnuDemanglerOptions(); + options.setDemangleOnlyKnownPatterns(false); + options = options.withDeprecatedDemangler(); + DemangledObject result = demangler.demangle(mangled, options); + assertNotNull(result); + assertEquals( + "int JSC::Structure::add<(JSC::Structure::ShouldPin)1,JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&,JSC::PropertyName,unsigned_int,unsigned_int,JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker_const&,int,int)#1}>(JSC::VM &,JSC::PropertyName,unsigned int,JSC::JSObject::prepareToPutDirectWithoutTransition(JSC::VM&,JSC::PropertyName,unsigned_int,unsigned_int,JSC::Structure*)::{lambda(JSC::GCSafeConcurrentJSLocker const&, int, int)#1} const &)", + result.getSignature(false)); + + DemanglerCmd cmd = new DemanglerCmd(addr("01001000"), mangled, options); + + // this used to trigger an exception + boolean success = applyCmd(program, cmd); + assertTrue("Demangler command failed: " + cmd.getStatusMsg(), success); + + assertNotNull(cmd.getDemangledObject()); + } + private Address addr(String address) { return program.getAddressFactory().getAddress(address); }