From 518860f0d6b80d2d3b945b873d67d3550b83c90c Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:59:49 -0400 Subject: [PATCH] GP-4825 - Gnu Demangler - Update parser for some special global constructor/destructor symbols --- .../app/util/demangler/DemangledObject.java | 43 +++++++----- .../demangler/gnu/GnuDemanglerParser.java | 65 +++++++++++++++++-- .../demangler/GnuDemanglerParserTest.java | 63 ++++++++++++++++-- 3 files changed, 143 insertions(+), 28 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java index 0534c05a6c..0173aa710d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java @@ -4,9 +4,9 @@ * 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. @@ -45,38 +45,38 @@ public abstract class DemangledObject implements Demangled { The following names probably need to be refactored. Until then, this is how the following fields are used. - mangled - + mangled - Source: The original mangled string as seen in the program Usage: Can be used to see if a program symbol has already been demangled - originalDemangled - + originalDemangled - Source: The raw demangled string returned from the demangler Usage: for display - demangledName - - Source: The name as created by the parser which may transform or even replace the + demangledName - + Source: The name as created by the parser which may transform or even replace the string returned from the demangler Usage: for display - name - - Source: This is derived from the 'demangledName' This is updated to be suitable - for use as a symbol name. This may be null while building, but is - expected to be non-null when applyTo() is called - Usage: The name that will be applied when applyTo() is called. + name - + Source: This is derived from the 'demangledName' This is updated to be suitable + for use as a symbol name. This may be null while building, but is + expected to be non-null when applyTo() is called + Usage: The name that will be applied when applyTo() is called. - Future: These variables should be refactored and renamed to be clearer and more cohesive, - something like: + Future: These variables should be refactored and renamed to be clearer and more cohesive, + something like: mangled rawDemangled escapedDemangled - symbolName + symbolName */ protected final String mangled; // original mangled string - protected final String originalDemangled; // raw demangled string + protected String originalDemangled; // raw demangled string private String demangledName; // updated demangled string private String name; // version of demangled name suitable for symbols @@ -240,6 +240,17 @@ public abstract class DemangledObject implements Demangled { return originalDemangled; } + /** + * Sets the original demangled string. This is useful for clients that reuse constructed + * demangled objects for special case constructs. + *

+ * Note: this method is not on the interface + * @param originalDemangled the new original demangled string + */ + public void setOriginalDemangled(String originalDemangled) { + this.originalDemangled = originalDemangled; + } + @Override public Demangled getNamespace() { return namespace; @@ -364,7 +375,7 @@ public abstract class DemangledObject implements Demangled { } /** - * Apply this demangled object detail to the specified program. + * Apply this demangled object detail to the specified program. *
* NOTE: An open Program transaction must be established prior to invoking this method. * 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 7fef606dc6..ae13dcb1a8 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 @@ -4,9 +4,9 @@ * 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. @@ -271,6 +271,19 @@ public class GnuDemanglerParser { private static final Pattern DESCRIPTIVE_PREFIX_PATTERN = Pattern.compile("((.+ )(for|to) )(.+)"); + /* + * Sample: global constructors keyed to cyg_libc_stdio_altout + * + * Pattern: global (constructors|destructors) keyed to text + * + * Parts: + * -global (constructors|destructors) keyed to (group 1) + * -text (group 3) + * + */ + private static final Pattern GLOBAL_CTOR_DTOR_FOR_PATTERN = + Pattern.compile("(global (constructors|destructors) keyed to )(.+)"); + /** * The c 'decltype' keyword pattern */ @@ -446,6 +459,13 @@ public class GnuDemanglerParser { return new ArrayHandler(demangled, prefix, type); } + Matcher globalCtorMatcher = GLOBAL_CTOR_DTOR_FOR_PATTERN.matcher(demangled); + if (globalCtorMatcher.matches()) { + prefix = globalCtorMatcher.group(1); + type = globalCtorMatcher.group(3); + return new GlobalCtorDtorHandler(demangled, prefix, type); + } + return new ItemInNamespaceHandler(demangled, prefix, type); } @@ -1524,13 +1544,11 @@ public class GnuDemanglerParser { ItemInNamespaceHandler(String demangled) { super(demangled); - this.demangled = demangled; this.type = demangled; } ItemInNamespaceHandler(String demangled, String prefix, String item) { super(demangled); - this.demangled = demangled; this.prefix = prefix; this.type = item; } @@ -1542,6 +1560,40 @@ public class GnuDemanglerParser { } } + private class GlobalCtorDtorHandler extends SpecialPrefixHandler { + + GlobalCtorDtorHandler(String demangled, String prefix, String item) { + super(demangled); + this.prefix = prefix; + this.type = item; + } + + @Override + DemangledObject doBuild(Demangled namespace) { + + // + // Since we are for constructors/destructors, assume each item is function + // + String functionName = type; + if (!functionName.contains("(")) { + // add parens so the type will be parsed as a function + functionName += "()"; + } + + DemangledObject demangledFunction = parseFunctionOrVariable(functionName); + demangledFunction.setOriginalDemangled(demangled); + + // e.g., global.constructors.keyed.to.functionName + String parsedFunctionName = demangledFunction.getName(); + String prefixNoSpaces = prefix.replaceAll("\s", "\\."); + String fullName = prefixNoSpaces + parsedFunctionName; + + demangledFunction.setName(fullName); + demangledFunction.setBackupPlateComment(demangled); + return demangledFunction; + } + } + private class ArrayHandler extends SpecialPrefixHandler { private String arrayType; @@ -1576,9 +1628,8 @@ public class GnuDemanglerParser { // if ("char".equals(arrayType) && type.contains("StringLiteral")) { // treat a char[] as a string - DemangledString ds = - new DemangledString(variable.getMangledString(), demangled, type, type, - -1 /*unknown length*/, false); + DemangledString ds = new DemangledString(variable.getMangledString(), demangled, + type, type, -1 /*unknown length*/, false); ds.setSpecialPrefix(prefix); return ds; } 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 4ea5c01bfa..4657cdf013 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 @@ -4,9 +4,9 @@ * 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. @@ -1025,7 +1025,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { String demangled = process.demangle(mangled); /* - typeinfo for + typeinfo for std::__ndk1::__function::__func< dummy::it::other::Namespace::function(float)::$_2::operator()(dummy::it::other::Namespace*) const::{lambda(dummy::it::other::Namespace*)#1}, std::__ndk1::allocator, @@ -2216,8 +2216,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertName(object, "__allocate_at_least[abi:v160006]>>>", - "std", - "__1"); + "std", "__1"); String signature = object.getSignature(false); assertEquals( @@ -2225,6 +2224,60 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { signature); } + @Test + public void testGlobalConstructor() throws Exception { + + // + // mangled: _GLOBAL__I_cyg_libc_stdio_altout + // + // demangled: global constructors keyed to cyg_libc_stdio_altout + // + // updated name: global.constructors.keyed.to.cyg_libc_stdio_altout + // + + String mangled = "_GLOBAL__I_cyg_libc_stdio_altout"; + String demangled = process.demangle(mangled); + + DemangledObject object = parser.parse(mangled, demangled); + assertNotNull(object); + assertType(object, DemangledFunction.class); + + assertEquals("global constructors keyed to cyg_libc_stdio_altout", + object.getOriginalDemangled()); + assertName(object, "global.constructors.keyed.to.cyg_libc_stdio_altout"); + + String signature = object.getSignature(false); + assertEquals("undefined global.constructors.keyed.to.cyg_libc_stdio_altout(void)", + signature); + } + + @Test + public void testGlobalDestructor() throws Exception { + + // + // mangled: _GLOBAL__D_cyg_libc_stdio_altout + // + // demangled: global destructors keyed to cyg_libc_stdio_altout + // + // updated name: global.destructors.keyed.to.cyg_libc_stdio_altout + // + + String mangled = "_GLOBAL__D_cyg_libc_stdio_altout"; + String demangled = process.demangle(mangled); + + DemangledObject object = parser.parse(mangled, demangled); + assertNotNull(object); + assertType(object, DemangledFunction.class); + + assertEquals("global destructors keyed to cyg_libc_stdio_altout", + object.getOriginalDemangled()); + assertName(object, "global.destructors.keyed.to.cyg_libc_stdio_altout"); + + String signature = object.getSignature(false); + assertEquals("undefined global.destructors.keyed.to.cyg_libc_stdio_altout(void)", + signature); + } + private void assertType(Demangled o, Class c) { assertTrue("Wrong demangled type. \nExpected " + c + "; \nfound " + o.getClass(), c.isInstance(o));