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 b1a3ad1791..befaddeda1 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 @@ -206,6 +206,17 @@ public class GnuDemanglerParser { private static final Pattern LAMBDA_PATTERN = Pattern.compile(".*(\\{" + LAMBDA + "\\((.*)\\)(#\\d+)\\})"); + /* + * Sample: {unnamed type#1} + * + * Pattern: [optional text] brace unnamed type#digits brace + * + * Parts: + * -full text without leading characters (capture group 1) + */ + private static final Pattern UNNAMED_TYPE_PATTERN = + Pattern.compile("(\\{unnamed type#\\d+})"); + /* * Sample: covariant return thunk to Foo::Bar::copy(Foo::CoolStructure*) const * @@ -390,7 +401,7 @@ public class GnuDemanglerParser { LambdaName lambdaName = getLambdaName(demangled); if (lambdaName != null) { String uniqueName = lambdaName.getFullText(); - String escapedLambda = removeInternalSpaces(uniqueName); + String escapedLambda = removeBadSpaces(uniqueName); simpleName = simpleName.replace("{lambda", escapedLambda); function = new DemangledLambda(mangledSource, demangled, null); function.setSignature(lambdaName.getFullText()); @@ -484,12 +495,16 @@ public class GnuDemanglerParser { } /** - * Removes spaces inside of templates and parentheses by either dropping the spaces or - * replacing them with an underscore when they are surrounded by word characters + * Removes spaces from unwanted places. For example, all spaces internal to templates and + * parameter lists will be removed. Also, other special cases may be handled, such as when + * the 'unnamed type' construct is found. + * + * @param text the text to fix + * @return the fixed text */ - private String removeInternalSpaces(String name) { - CondensedString cs = new CondensedString(name); - return cs.getCondensedString(); + private String removeBadSpaces(String text) { + CondensedString condensedString = new CondensedString(text); + return condensedString.getCondensedText(); } /** @@ -898,7 +913,6 @@ public class GnuDemanglerParser { } private void setNameAndNamespace(DemangledObject object, String name) { - SymbolPath path = new SymbolPath(name); List names = path.asList(); @@ -1011,7 +1025,7 @@ public class GnuDemanglerParser { */ - String nameString = removeInternalSpaces(demangled).trim(); + String nameString = removeBadSpaces(demangled).trim(); DemangledVariable variable = new DemangledVariable(mangledSource, demangledSource, (String) null); setNameAndNamespace(variable, nameString); @@ -1025,7 +1039,7 @@ public class GnuDemanglerParser { * and Namespace{C} will be returned. * *

This method will also escape spaces separators inside of templates - * (see {@link #removeInternalSpaces(String)}). + * (see {@link #removeBadSpaces(String)}). * * @param names the names to convert * @return the newly created type @@ -1036,13 +1050,13 @@ public class GnuDemanglerParser { } int index = names.size() - 1; String rawName = names.get(index); - String escapedName = removeInternalSpaces(rawName); + String escapedName = removeBadSpaces(rawName); DemangledType myNamespace = new DemangledType(mangledSource, demangledSource, escapedName); DemangledType namespace = myNamespace; while (--index >= 0) { rawName = names.get(index); - escapedName = removeInternalSpaces(rawName); + escapedName = removeBadSpaces(rawName); DemangledType parentNamespace = new DemangledType(mangledSource, demangledSource, escapedName); namespace.setNamespace(parentNamespace); @@ -1175,7 +1189,7 @@ public class GnuDemanglerParser { new DemangledString(mangledSource, demangledSource, "typeinfo-name", type, -1/*unknown length*/, false); demangledString.setSpecialPrefix("typeinfo name for "); - String namespaceString = removeInternalSpaces(type); + String namespaceString = removeBadSpaces(type); setNamespace(demangledString, namespaceString); return demangledString; } @@ -1269,8 +1283,8 @@ public class GnuDemanglerParser { function.setName(originalPrefix); if (!StringUtils.isBlank(templates)) { - String escapedPrefix = removeInternalSpaces(originalPrefix); - String escapedTemplates = removeInternalSpaces(templates); + String escapedPrefix = removeBadSpaces(originalPrefix); + String escapedTemplates = removeBadSpaces(templates); int templateIndex = escapedPrefix.indexOf(escapedTemplates); String operatorName = escapedPrefix.substring(0, templateIndex); DemangledTemplate demangledTemplate = parseTemplate(escapedTemplates); @@ -1516,10 +1530,7 @@ public class GnuDemanglerParser { private boolean isFunction; - private String rawReturnType; private String returnType; - - private String rawName; private String name; private List parameters; @@ -1543,16 +1554,13 @@ public class GnuDemanglerParser { String rawPrefix = signatureString.substring(0, prefixEndPos).trim(); CondensedString prefixString = new CondensedString(rawPrefix); - String prefix = prefixString.getCondensedString(); + String prefix = prefixString.getCondensedText(); int nameStart = Math.max(0, prefix.lastIndexOf(' ')); - - rawName = prefixString.substringOriginal(nameStart, prefix.length()); - name = removeInternalSpaces(rawName); + name = prefix.substring(nameStart, prefix.length()).trim(); // check for return type if (nameStart > 0) { - rawReturnType = prefixString.substringOriginal(0, nameStart); - returnType = removeInternalSpaces(rawReturnType); + returnType = prefix.substring(0, nameStart); } } @@ -1579,21 +1587,27 @@ public class GnuDemanglerParser { } /** - * An object that will change the input string (such as to remove spaces) while maintaining - * the ability to translate from the new string back to the old string + * A class to handle whitespace manipulation within demangled strings. This class will + * remove bad spaces, which is all whitespace that is not needed to separate distinct objects + * inside of a demangled string. + * + *

Generally, this class removes spaces within templates and parameter lists. It will + * remove some spaces, while converting some to underscores. */ private class CondensedString { - private String originalString; - private String condensedString; + @SuppressWarnings("unused") // used by toString() + private String sourceText; + private String condensedText; private List parts = new ArrayList<>(); CondensedString(String input) { - this.originalString = input; - this.condensedString = createFixedString(input); + String fixed = fixupUnnamedTypes(input); + this.sourceText = fixed; + this.condensedText = convertTemplateAndParameterSpaces(fixed); } - private String createFixedString(String name) { + private String convertTemplateAndParameterSpaces(String name) { int depth = 0; char last = NULL_CHAR; @@ -1638,46 +1652,25 @@ public class GnuDemanglerParser { return Character.isLetterOrDigit(last) && Character.isLetterOrDigit(next); } + private String fixupUnnamedTypes(String demangled) { + String fixed = demangled; + Matcher matcher = UNNAMED_TYPE_PATTERN.matcher(demangled); + while (matcher.find()) { + String text = matcher.group(1); + String noSpace = text.replaceFirst("\\s", "_"); + fixed = fixed.replace(text, noSpace); + } + + return fixed; + } + /** * Returns the original string value that has been 'condensed', which means to remove * internal spaces * @return the condensed string */ - String getCondensedString() { - return condensedString; - } - - /** - * Uses the given start and end from the 'condensed' string to create a substring of - * the original string - * @param condensedStart the start in the condensed string - * @param condensedEnd the end in the condensed string - * @return the substring from the original string - */ - String substringOriginal(int condensedStart, int condensedEnd) { - - int start = condensedToOriginal(condensedStart); - int end = condensedToOriginal(condensedEnd); - return originalString.substring(start, end); - } - - private int condensedToOriginal(int index) { - - int next = index + 1; - int n = 0; - for (int i = 0; i < parts.size(); i++) { - Part p = parts.get(i); - n += p.condensed.length(); - if (n == next) { - return i; - } - } - - if (n == index) { - return parts.size(); // user asked for length of the 'condensed' string - } - - throw new IndexOutOfBoundsException("Index not in condensed string: " + index); + String getCondensedText() { + return condensedText; } @Override 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 1e4efa0cc5..33e0b51fe9 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 @@ -1420,6 +1420,25 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { signature); } + @Test + public void testFunctionWithLamba_WithUnnamedType() throws Exception { + + // + // Mangled: _ZN13SoloGimbalEKFUt_C2Ev + // + // Demangled: SoloGimbalEKF::{unnamed type#1}::SoloGimbalEKF() + // + String mangled = "_ZN13SoloGimbalEKFUt_C2Ev"; + String demangled = process.demangle(mangled); + + DemangledObject object = parser.parse(mangled, demangled); + assertNotNull(object); + assertType(object, DemangledFunction.class); + + String signature = object.getSignature(false); + assertEquals("undefined SoloGimbalEKF::{unnamed_type#1}::SoloGimbalEKF(void)", signature); + } + @Test public void testFunctionWithLambda_WrappingAnotherFunctionCall() 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 e853173d5b..5de5575264 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 @@ -76,6 +76,31 @@ public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrati assertNotNull(cmd.getDemangledObject()); } + @Test + public void testParsingReturnType_UnnamedType() throws Exception { + + String mangled = "_ZN13SoloGimbalEKFUt_C2Ev"; + + 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("undefined SoloGimbalEKF::{unnamed_type#1}::SoloGimbalEKF(void)", + 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); }