diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java index 3497838956..94c1917b5d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java @@ -25,6 +25,7 @@ import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.task.TaskMonitor; +import utility.function.Dummy; public class DemangledThunk extends DemangledObject { @@ -176,8 +177,7 @@ public class DemangledThunk extends DemangledObject { } Symbol s = SymbolUtilities.getExpectedLabelOrFunctionSymbol(program, - mangled, err -> Msg.warn(this, err)); - + mangled, Dummy.consumer()); if (s == null) { Address thunkedAddr = CreateThunkFunctionCmd.getThunkedAddr(program, thunkAddress, false); @@ -185,11 +185,13 @@ public class DemangledThunk extends DemangledObject { s = program.getSymbolTable().getPrimarySymbol(thunkedAddr); } } + if (s == null || !block.contains(s.getAddress())) { + Msg.warn(this, "Unable to find or create thunk for " + mangled + " at " + thunkAddress); return null; } - Address addr = s.getAddress(); + Address addr = s.getAddress(); DemanglerOptions subOptions = new DemanglerOptions(options); subOptions.setApplySignature(true); 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 3b17ce4a7f..5d2a8e8571 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 @@ -1004,6 +1004,81 @@ public class GnuDemanglerParser { return findBalancedStart(string, templateEnd, '<', '>'); } + /** + * Walks backward from the given start position to find the next namespace separator. This + * allows clients to determine if a given position is inside of a namespace. + * + * @param text the text to search + * @param start the start position + * @param stop the stop position + * @return the start index of the namespace entry containing the current {@code start} + * index; -1 if no namespace start is found + */ + private int findNamespaceStart(String text, int start, int stop) { + + if (!text.contains(Namespace.DELIMITER)) { + return -1; + } + + int nsCount = 0; + int parenDepth = 0; + int templateDepth = 0; + int braceDepth = 0; + boolean isNested = false; + + for (int i = start; i >= stop; i--) { + + char c = text.charAt(i); + switch (c) { + case ':': { + nsCount++; + if (nsCount == 2) { + if (!isNested) { + return i + 2; + } + nsCount = 0; + } + break; + } + case ' ': { + if (!isNested) { + return -1; // a space implies a return type when not nested + } + break; + } + case '(': { + isNested = --parenDepth > 0 || templateDepth > 0 || braceDepth > 0; + break; + } + case ')': { + isNested = ++parenDepth > 0 || templateDepth > 0 || braceDepth > 0; + break; + } + case '<': { + isNested = parenDepth > 0 || --templateDepth > 0 || braceDepth > 0; + break; + } + case '>': { + isNested = parenDepth > 0 || ++templateDepth > 0 || braceDepth > 0; + break; + } + case '{': { + isNested = parenDepth > 0 || templateDepth > 0 || --braceDepth > 0; + break; + } + case '}': { + isNested = parenDepth > 0 || templateDepth > 0 || ++braceDepth > 0; + break; + } + + default: + continue; + } + } + + return -1; + } + private DemangledDataType createTypeInNamespace(String name) { List names = SymbolPathParser.parse(name, false); DemangledType namespace = null; @@ -1342,7 +1417,17 @@ public class GnuDemanglerParser { @Override boolean matches(String text) { matcher = OVERLOAD_OPERATOR_NAME_PATTERN.matcher(text); - return matcher.matches(); + if (!matcher.matches()) { + return false; + } + + int operatorStart = matcher.start(2); + int leafStart = findNamespaceStart(demangled, text.length() - 1, operatorStart); + if (leafStart > operatorStart) { + return false; // operator is inside of a non-leaf namespace entry + } + + return true; } @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 751fb13a52..50b908baff 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 @@ -842,6 +842,32 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertEquals("Magick::Coordinate const &", parameters.get(1).getSignature()); } + @Test + public void testOperatorAsNamespace() throws Exception { + + // + // Mangled: __ZN3WTF15__visitor_tableINS_7VisitorIZZN7WebCore12SubtleCrypto11generateKeyERN3JSC9ExecStateEONS_7VariantIJNS4_6StrongINS4_8JSObjectEEENS_6StringEEEEbONS_6VectorINS2_14CryptoKeyUsageELm0ENS_15CrashOnOverflowELm16ENS_10FastMallocEEEONS_3RefINS2_15DeferredPromiseENS_13DumbPtrTraitsISL_EEEEEN4$_10clEONS7_IJNS_6RefPtrINS2_9CryptoKeyENSM_ISS_EEEENS2_13CryptoKeyPairEEEEEUlRSU_E_JZZNS3_11generateKeyES6_SD_bSJ_SP_ENSQ_clESX_EUlRSV_E_EEEJSU_SV_EE12__trampolineE + // + // Demangled: WTF::__visitor_table, WTF::String>&&, bool, WTF::Vector&&, WTF::Ref >&&)::$_10::operator()(WTF::Variant >, WebCore::CryptoKeyPair>&&)::{lambda(WTF::RefPtr >&)#1}, WebCore::SubtleCrypto::generateKey(JSC::ExecState&, WTF::Variant, WTF::String>&&, bool, WTF::Vector&&, WTF::Ref >&&)::$_10::operator()(WTF::Variant >, WebCore::CryptoKeyPair>&&)::{lambda(WebCore::CryptoKeyPair&)#1}>, WTF::RefPtr >, WebCore::CryptoKeyPair>::__trampoline + // + + DemangledObject object = parser.parse( + "__ZN3WTF15__visitor_tableINS_7VisitorIZZN7WebCore12SubtleCrypto11generateKeyERN3JSC9ExecStateEONS_7VariantIJNS4_6StrongINS4_8JSObjectEEENS_6StringEEEEbONS_6VectorINS2_14CryptoKeyUsageELm0ENS_15CrashOnOverflowELm16ENS_10FastMallocEEEONS_3RefINS2_15DeferredPromiseENS_13DumbPtrTraitsISL_EEEEEN4$_10clEONS7_IJNS_6RefPtrINS2_9CryptoKeyENSM_ISS_EEEENS2_13CryptoKeyPairEEEEEUlRSU_E_JZZNS3_11generateKeyES6_SD_bSJ_SP_ENSQ_clESX_EUlRSV_E_EEEJSU_SV_EE12__trampolineE", + "WTF::__visitor_table, WTF::String>&&, bool, WTF::Vector&&, WTF::Ref >&&)::$_10::operator()(WTF::Variant >, WebCore::CryptoKeyPair>&&)::{lambda(WTF::RefPtr >&)#1}, WebCore::SubtleCrypto::generateKey(JSC::ExecState&, WTF::Variant, WTF::String>&&, bool, WTF::Vector&&, WTF::Ref >&&)::$_10::operator()(WTF::Variant >, WebCore::CryptoKeyPair>&&)::{lambda(WebCore::CryptoKeyPair&)#1}>, WTF::RefPtr >, WebCore::CryptoKeyPair>::__trampoline"); + + assertNotNull(object); + assertType(object, DemangledVariable.class); + + String name = "__trampoline"; + assertName(object, name, "WTF", + "__visitor_table,WTF::String>&&,bool,WTF::Vector&&,WTF::Ref>&&)::$_10::operator()(WTF::Variant>,WebCore::CryptoKeyPair>&&)::{lambda(WTF::RefPtr>&)#1},WebCore::SubtleCrypto::generateKey(JSC::ExecState&,WTF::Variant,WTF::String>&&,bool,WTF::Vector&&,WTF::Ref>&&)::$_10::operator()(WTF::Variant>,WebCore::CryptoKeyPair>&&)::{lambda(WebCore::CryptoKeyPair&)#1}>,WTF::RefPtr>,WebCore::CryptoKeyPair>"); + + String signature = object.getSignature(false); + assertEquals( + "WTF::__visitor_table,WTF::String>&&,bool,WTF::Vector&&,WTF::Ref>&&)::$_10::operator()(WTF::Variant>,WebCore::CryptoKeyPair>&&)::{lambda(WTF::RefPtr>&)#1},WebCore::SubtleCrypto::generateKey(JSC::ExecState&,WTF::Variant,WTF::String>&&,bool,WTF::Vector&&,WTF::Ref>&&)::$_10::operator()(WTF::Variant>,WebCore::CryptoKeyPair>&&)::{lambda(WebCore::CryptoKeyPair&)#1}>,WTF::RefPtr>,WebCore::CryptoKeyPair>::__trampoline", + signature); + } + @Test public void testOverloadedShiftOperatorTemplated_RightShift() { parser = new GnuDemanglerParser();