From 0c7c458a9f408509920705f8fd0c3b7ee2276986 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Tue, 23 Jun 2026 15:53:56 -0400 Subject: [PATCH 01/14] Avoid core-only dependencies in API demangler --- demangler/gnu3/demangle_gnu3.cpp | 2 +- demangler/msvc/demangle_msvc.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 212153376..c61f6a3a0 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -3210,7 +3210,7 @@ namespace else return false; - thread_local DemangleGNU3 demangle(platform, encoding); + thread_local ::DemangleGNU3 demangle(platform, encoding); demangle.Reset(platform, encoding); try { diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index 27d44cd39..55c7b41b8 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -16,8 +16,9 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_msvc.h" +#ifdef BINARYNINJACORE_LIBRARY #include "unicode.h" -#include "base/unicode.h" +#endif #include #include #include @@ -1559,7 +1560,10 @@ DemangledTypeNode Demangle::DemangleString(NameList& symbolName) } } symbolName.clear(); - symbolName.push_back(MakeNameSegment(fmt::bnformat("{}\"{}\"{}", literalPrefix, name, truncated ? "..." : ""))); + _STD_STRING literalName = literalPrefix + "\"" + name + "\""; + if (truncated) + literalName += "..."; + symbolName.push_back(MakeNameSegment(literalName)); return type; } From 3c68ce2aa2030f898aaf82ad5e0ff0eed771fa35 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Sat, 6 Jun 2026 00:31:32 -0400 Subject: [PATCH 02/14] Refactor template simplifier onto structured demangler AST Make template simplification demangler-controlled and expand structured template simplifier coverage --- binaryninjaapi.h | 58 +- binaryninjacore.h | 7 +- demangle.cpp | 39 +- demangler/demangled_template_simplifier.cpp | 2475 +++++++++++++++++ demangler/demangled_template_simplifier.h | 31 + demangler/demangled_type_node.cpp | 139 +- demangler/demangled_type_node.h | 11 + demangler/gnu3/CMakeLists.txt | 2 + demangler/gnu3/demangle_gnu3.cpp | 213 +- demangler/gnu3/demangle_gnu3.h | 37 +- demangler/msvc/CMakeLists.txt | 4 +- demangler/msvc/demangle_msvc.cpp | 83 +- demangler/msvc/demangle_msvc.h | 18 +- .../dwarf/dwarf_import/src/dwarfdebuginfo.rs | 38 +- plugins/dwarf/dwarf_import/src/functions.rs | 4 +- plugins/dwarf/dwarf_import/src/lib.rs | 31 +- plugins/workflow_swift/src/demangler/mod.rs | 1 + python/demangle.py | 81 +- rust/examples/demangler.rs | 1 + rust/src/demangle.rs | 17 +- rust/src/lib.rs | 1 - rust/src/template_simplifier.rs | 15 - rust/tests/demangler.rs | 22 +- 23 files changed, 3050 insertions(+), 278 deletions(-) create mode 100644 demangler/demangled_template_simplifier.cpp create mode 100644 demangler/demangled_template_simplifier.h delete mode 100644 rust/src/template_simplifier.rs diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 260bfc25a..e384f25dd 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2542,26 +2542,6 @@ namespace BinaryNinja { */ bool IsGNU3MangledString(const std::string& mangledName); - /*! - \ingroup demangle - */ - std::string SimplifyToString(const std::string& input); - - /*! - \ingroup demangle - */ - std::string SimplifyToString(const QualifiedName& input); - - /*! - \ingroup demangle - */ - QualifiedName SimplifyToQualifiedName(const std::string& input, bool simplify); - - /*! - \ingroup demangle - */ - QualifiedName SimplifyToQualifiedName(const QualifiedName& input); - /*! \ingroup mainthread */ @@ -20700,38 +20680,6 @@ namespace BinaryNinja { static int Compare(LinearViewCursor* a, LinearViewCursor* b); }; - /*! - - \ingroup simplifyname - */ - class SimplifyName - { - public: - // Use these functions to interface with the simplifier - static std::string to_string(const std::string& input); - static std::string to_string(const QualifiedName& input); - static QualifiedName to_qualified_name(const std::string& input, bool simplify); - static QualifiedName to_qualified_name(const QualifiedName& input); - - // Below is everything for the above APIs to work - enum SimplifierDest - { - str, - fqn - }; - - SimplifyName(const std::string&, const SimplifierDest, const bool); - ~SimplifyName(); - - operator std::string() const; - operator QualifiedName(); - - private: - const char* m_rust_string; - const char** m_rust_array; - uint64_t m_length; - }; - struct FindParameters { BNFindType type; @@ -22553,7 +22501,7 @@ namespace BinaryNinja { static bool IsMangledStringCallback(void* ctxt, const char* name); static bool DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view); + BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); static void FreeVarNameCallback(void* ctxt, BNQualifiedName* name); public: @@ -22614,7 +22562,7 @@ namespace BinaryNinja { \return True if demangling was successful and results were stored into out-parameters */ virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view = nullptr) = 0; + QualifiedName& outVarName, Ref view = nullptr, bool simplify = false) = 0; }; /*! @@ -22628,7 +22576,7 @@ namespace BinaryNinja { virtual bool IsMangledString(const std::string& name); virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view); + QualifiedName& outVarName, Ref view, bool simplify = false); }; namespace Unicode diff --git a/binaryninjacore.h b/binaryninjacore.h index 225cb437d..b07b9fb3e 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -3903,7 +3903,7 @@ extern "C" void* context; bool (*isMangledString)(void* ctxt, const char* name); bool (*demangle)(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view); + BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); void (*freeVarName)(void* ctxt, BNQualifiedName* name); } BNDemanglerCallbacks; @@ -8273,6 +8273,8 @@ extern "C" BINARYNINJACOREAPI void BNPromoteDemangler(BNDemangler* demangler); BINARYNINJACOREAPI bool BNIsDemanglerMangledName(BNDemangler* demangler, const char* name); + BINARYNINJACOREAPI bool BNDemanglerDemangleWithOptions(BNDemangler* demangler, BNArchitecture* arch, + const char* name, BNType** outType, BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); BINARYNINJACOREAPI bool BNDemanglerDemangle(BNDemangler* demangler, BNArchitecture* arch, const char* name, BNType** outType, BNQualifiedName* outVarName, BNBinaryView* view); BINARYNINJACOREAPI bool BNDemangleGeneric(BNArchitecture* arch, const char* name, @@ -8637,9 +8639,6 @@ extern "C" BINARYNINJACOREAPI uint32_t BNGetAddressRenderedWidth(uint64_t addr); - BINARYNINJACOREAPI BNQualifiedName BNRustSimplifyStrToFQN(const char* const, bool); - BINARYNINJACOREAPI char* BNRustSimplifyStrToStr(const char* const); - BINARYNINJACOREAPI BNDebugInfoParser* BNRegisterDebugInfoParser(const char* name, bool (*isValid)(void*, BNBinaryView*), bool (*parseInfo)(void*, BNDebugInfo*, BNBinaryView*, BNBinaryView*, BNProgressFunction, void*), diff --git a/demangle.cpp b/demangle.cpp index faf27ac21..058dc9fb5 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -110,35 +110,6 @@ namespace BinaryNinja { } - string SimplifyToString(const string& input) - { - return BNRustSimplifyStrToStr(input.c_str()); - } - - - string SimplifyToString(const QualifiedName& input) - { - return BNRustSimplifyStrToStr(input.GetString().c_str()); - } - - - QualifiedName SimplifyToQualifiedName(const string& input, bool simplify) - { - BNQualifiedName name = BNRustSimplifyStrToFQN(input.c_str(), simplify); - QualifiedName result = QualifiedName::FromAPIObject(&name); - BNFreeQualifiedName(&name); - return result; - } - - - QualifiedName SimplifyToQualifiedName(const QualifiedName& input) - { - BNQualifiedName name = BNRustSimplifyStrToFQN(input.GetString().c_str(), true); - QualifiedName result = QualifiedName::FromAPIObject(&name); - BNFreeQualifiedName(&name); - return result; - } - Demangler::Demangler(const std::string& name): m_nameForRegister(name) { } @@ -155,7 +126,7 @@ namespace BinaryNinja { } bool Demangler::DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view) + BNQualifiedName* outVarName, BNBinaryView* view, bool simplify) { Demangler* demangler = (Demangler*)ctxt; @@ -164,7 +135,7 @@ namespace BinaryNinja { Ref apiType; QualifiedName apiVarName; - bool success = demangler->Demangle(apiArch, name, apiType, apiVarName, apiView); + bool success = demangler->Demangle(apiArch, name, apiType, apiVarName, apiView, simplify); if (!success) return false; @@ -239,12 +210,12 @@ namespace BinaryNinja { } bool CoreDemangler::Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view) + QualifiedName& outVarName, Ref view, bool simplify) { BNType* apiType = nullptr; BNQualifiedName apiVarName; - bool success = BNDemanglerDemangle( - m_object, arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr); + bool success = BNDemanglerDemangleWithOptions( + m_object, arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr, simplify); if (!success) return false; diff --git a/demangler/demangled_template_simplifier.cpp b/demangler/demangled_template_simplifier.cpp new file mode 100644 index 000000000..ca17d0488 --- /dev/null +++ b/demangler/demangled_template_simplifier.cpp @@ -0,0 +1,2475 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// 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. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "demangled_template_simplifier.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef BINARYNINJACORE_LIBRARY +using namespace BinaryNinjaCore; +#else +using namespace BinaryNinja; +using namespace std; +#endif + +namespace +{ + using Param = DemangledTypeNode::Param; + using ParamList = vector; + + static std::string_view TrimSpaces(std::string_view s) + { + size_t start = 0; + while (start < s.size() && s[start] == ' ') + start++; + size_t end = s.size(); + while (end > start && s[end - 1] == ' ') + end--; + return s.substr(start, end - start); + } + + static bool StartsWith(std::string_view s, const char* prefix) + { + size_t n = strlen(prefix); + return s.compare(0, n, prefix) == 0; + } + + static std::string_view StripLeadingTypeKeyword(std::string_view s) + { + s = TrimSpaces(s); + if (StartsWith(s, "class ")) + return s.substr(6); + if (StartsWith(s, "struct ")) + return s.substr(7); + if (StartsWith(s, "union ")) + return s.substr(6); + if (StartsWith(s, "enum ")) + return s.substr(5); + return s; + } + + static std::string_view ExtractLeadingTypeKeyword(std::string_view& s) + { + s = TrimSpaces(s); + if (StartsWith(s, "class ")) + { + s = s.substr(6); + return "class "; + } + if (StartsWith(s, "struct ")) + { + s = s.substr(7); + return "struct "; + } + if (StartsWith(s, "union ")) + { + s = s.substr(6); + return "union "; + } + if (StartsWith(s, "enum ")) + { + s = s.substr(5); + return "enum "; + } + return {}; + } + + static string RemoveSpaces(std::string_view s) + { + string out; + out.reserve(s.size()); + for (char c : s) + { + if (c != ' ') + out += c; + } + return out; + } + + static bool StringEquals(const string& s, std::string_view value) + { + return s.size() == value.size() && + (value.empty() || memcmp(s.data(), value.data(), value.size()) == 0); + } + + static bool IsUnqualifiedNonPointerType(const DemangledTypeNode& type) + { + return !type.IsConst() && !type.IsVolatile() && type.GetPointerSuffixBits() == 0; + } + + static size_t ResolvedIntegerWidth(size_t width, DemangledTypeNode::WidthKind widthKind) + { + switch (widthKind) + { + case DemangledTypeNode::AddressWidth: + return 8; + case DemangledTypeNode::DefaultIntegerWidth: + return 4; + case DemangledTypeNode::FixedWidth: + default: + return width; + } + } + + static bool GetUnqualifiedIntegerTypeInfo(const DemangledTypeNode& type, size_t& width, + DemangledTypeNode::WidthKind& widthKind, bool& isSigned, std::string_view& altName) + { + return IsUnqualifiedNonPointerType(type) && type.GetIntegerTypeInfo(width, widthKind, isSigned, altName); + } + + static bool GetUnqualifiedWideCharTypeInfo(const DemangledTypeNode& type, size_t& width, + std::string_view& altName) + { + return IsUnqualifiedNonPointerType(type) && type.GetWideCharTypeInfo(width, altName); + } + + static string CanonicalTypeNoSpaces(const DemangledTypeNode& type) + { + string s = type.GetString(); + return RemoveSpaces(StripLeadingTypeKeyword(std::string_view(s.data(), s.size()))); + } + + static string CanonicalTypeNoSpaces(std::string_view type) + { + return RemoveSpaces(StripLeadingTypeKeyword(type)); + } + + static bool SameType(const DemangledTypeNode& a, const DemangledTypeNode& b) + { + if (a.IsStructurallyEqual(b)) + return true; + return CanonicalTypeNoSpaces(a) == CanonicalTypeNoSpaces(b); + } + + static bool SameType(const DemangledTypeNode& a, std::string_view b) + { + if (!a.IsConst() && !a.IsVolatile() && a.GetPointerSuffixBits() == 0 && a.GetClass() == NamedTypeReferenceClass) + { + const DemangledQualifiedName& name = a.GetName(); + if (name.size() == 1 && !name[0].HasTemplateArguments()) + { + const string& base = name[0].GetBase(); + if (StringEquals(base, b)) + return true; + } + } + return CanonicalTypeNoSpaces(a) == CanonicalTypeNoSpaces(b); + } + + static bool IsSimpleNamedType(const DemangledTypeNode& type, std::string_view name, bool cnst = false) + { + if (type.IsConst() != cnst || type.IsVolatile() || type.GetPointerSuffixBits() != 0 || + type.GetClass() != NamedTypeReferenceClass) + return false; + + const DemangledQualifiedName& typeName = type.GetName(); + if (typeName.size() != 1 || typeName[0].HasTemplateArguments()) + return false; + const string& base = typeName[0].GetBase(); + return StringEquals(base, name); + } + + static bool IsQualifiedName(const DemangledTypeNode& type, std::initializer_list expected) + { + if (!IsUnqualifiedNonPointerType(type) || type.GetClass() != NamedTypeReferenceClass) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (name.size() != expected.size()) + return false; + + size_t i = 0; + for (std::string_view expectedSegment : expected) + { + if (name[i].HasTemplateArguments() || !StringEquals(name[i].GetBase(), expectedSegment)) + return false; + i++; + } + return true; + } + + static const char* CharFamilyAliasName(const DemangledTypeNode& element, const char* charAlias, + const char* wcharAlias, const char* char8Alias = nullptr, const char* char16Alias = nullptr, + const char* char32Alias = nullptr) + { + size_t width = 0; + DemangledTypeNode::WidthKind widthKind = DemangledTypeNode::FixedWidth; + bool isSigned = false; + std::string_view altName; + if (GetUnqualifiedIntegerTypeInfo(element, width, widthKind, isSigned, altName)) + { + if (char8Alias && altName == "char8_t") + return char8Alias; + if (isSigned && (altName.empty() || altName == "char") && + ResolvedIntegerWidth(width, widthKind) == 1) + return charAlias; + } + + size_t wideWidth = 0; + std::string_view wideAltName; + if (GetUnqualifiedWideCharTypeInfo(element, wideWidth, wideAltName)) + { + if (char16Alias && wideAltName == "char16_t") + return char16Alias; + if (char32Alias && wideAltName == "char32_t") + return char32Alias; + if (wideAltName.empty() || wideAltName == "wchar_t") + return wcharAlias; + } + + if (IsSimpleNamedType(element, "char")) + return charAlias; + if (IsSimpleNamedType(element, "wchar_t")) + return wcharAlias; + if (char8Alias && IsSimpleNamedType(element, "char8_t")) + return char8Alias; + if (char16Alias && IsSimpleNamedType(element, "char16_t")) + return char16Alias; + if (char32Alias && IsSimpleNamedType(element, "char32_t")) + return char32Alias; + + const string type = CanonicalTypeNoSpaces(element); + if (type == "char") + return charAlias; + if (type == "wchar_t") + return wcharAlias; + if (char8Alias && type == "char8_t") + return char8Alias; + if (char16Alias && type == "char16_t") + return char16Alias; + if (char32Alias && type == "char32_t") + return char32Alias; + return nullptr; + } + + static bool HasOnlyOmittedPointerSuffixes(const DemangledTypeNode& type) + { + return (type.GetPointerSuffixBits() & ~(1u << Ptr64Suffix)) == 0; + } + + static bool IsConstTypeNamed(const DemangledTypeNode& type, std::string_view name) + { + if (IsSimpleNamedType(type, name, true)) + return true; + if (!type.IsConst() || type.IsVolatile()) + return false; + + DemangledTypeNode unqualified = type; + unqualified.SetConst(false); + return SameType(unqualified, name); + } + + static bool IsInlineStdNamespace(const DemangledNamePart& component) + { + if (component.HasTemplateArguments()) + return false; + const string& name = component.GetBase(); + return name == "__1" || name == "__cxx11" || name == "__detail"; + } + + static bool IsAbslInlineNamespace(const DemangledNamePart& component) + { + if (component.HasTemplateArguments()) + return false; + const string& name = component.GetBase(); + if (!StartsWith(name, "lts_")) + return false; + for (size_t i = 4; i < name.size(); i++) + { + if (!isdigit(static_cast(name[i])) && name[i] != '_') + return false; + } + return name.size() > 4; + } + + static bool IsStdScope(const DemangledQualifiedName& name, size_t* templateIndex) + { + if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "std") + return false; + + size_t i = 1; + while (i < name.size() && IsInlineStdNamespace(name[i])) + i++; + if (i >= name.size()) + return false; + *templateIndex = i; + return true; + } + + static bool IsStdScope(const DemangledTypeNode& type, size_t* templateIndex) + { + const DemangledQualifiedName& name = type.GetName(); + return IsStdScope(name, templateIndex); + } + + static bool IsAbslScope(const DemangledQualifiedName& name, size_t* templateIndex) + { + if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "absl") + return false; + + size_t i = 1; + while (i < name.size() && IsAbslInlineNamespace(name[i])) + i++; + if (i >= name.size()) + return false; + *templateIndex = i; + return true; + } + + static bool IsAbslScope(const DemangledTypeNode& type, size_t* templateIndex) + { + const DemangledQualifiedName& name = type.GetName(); + return IsAbslScope(name, templateIndex); + } + + static bool IsStdTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + size_t templateIndex = 0; + if (!IsStdScope(type, &templateIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (name.empty() || templateIndex + 1 != name.size()) + return false; + if (name[templateIndex].GetBase() != tmpl || !name[templateIndex].HasTemplateArguments()) + return false; + if (args) + *args = &name[templateIndex].GetTemplateArguments(); + return true; + } + + static bool IsAbslTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + size_t templateIndex = 0; + if (!IsAbslScope(type, &templateIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (name.empty() || templateIndex + 1 != name.size()) + return false; + if (name[templateIndex].GetBase() != tmpl || !name[templateIndex].HasTemplateArguments()) + return false; + if (args) + *args = &name[templateIndex].GetTemplateArguments(); + return true; + } + + static bool IsAbslNestedTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, + const ParamList** args = nullptr) + { + size_t scopeIndex = 0; + if (!IsAbslScope(type, &scopeIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (scopeIndex + 2 != name.size()) + return false; + if (name[scopeIndex].HasTemplateArguments() || name[scopeIndex].GetBase() != scope) + return false; + if (name[scopeIndex + 1].GetBase() != tmpl || !name[scopeIndex + 1].HasTemplateArguments()) + return false; + if (args) + *args = &name[scopeIndex + 1].GetTemplateArguments(); + return true; + } + + static bool IsAbslNestedName(const DemangledTypeNode& type, std::string_view scope, std::string_view base) + { + size_t scopeIndex = 0; + if (!IsAbslScope(type, &scopeIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + return scopeIndex + 2 == name.size() && !name[scopeIndex].HasTemplateArguments() && + name[scopeIndex].GetBase() == scope && !name[scopeIndex + 1].HasTemplateArguments() && + name[scopeIndex + 1].GetBase() == base; + } + + static bool IsStdPmrTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + const DemangledQualifiedName& name = type.GetName(); + if (name.size() < 3 || name[0].HasTemplateArguments() || name[0].GetBase() != "std") + return false; + + size_t i = 1; + while (i < name.size() && IsInlineStdNamespace(name[i])) + i++; + if (i >= name.size() || name[i].HasTemplateArguments() || name[i].GetBase() != "pmr") + return false; + i++; + if (i + 1 != name.size()) + return false; + if (name[i].GetBase() != tmpl || !name[i].HasTemplateArguments()) + return false; + if (args) + *args = &name[i].GetTemplateArguments(); + return true; + } + + static bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsStdTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + static bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, std::string_view arg) + { + const ParamList* args = nullptr; + if (!IsStdTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + static bool IsStdPmrOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsStdPmrTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + static bool IsPmrPolymorphicAllocator(const DemangledTypeNode& type, const DemangledTypeNode& valueType) + { + return IsStdPmrOneArgTemplate(type, "polymorphic_allocator", valueType); + } + + static bool IsConstTypeOf(const DemangledTypeNode& type, const DemangledTypeNode& base) + { + if (type.IsConst() && !type.IsVolatile()) + { + DemangledTypeNode unqualified = type; + unqualified.SetConst(false); + if (SameType(unqualified, base)) + return true; + } + + const string baseString = base.GetString(); + string suffixConst = baseString; + suffixConst += " const"; + string prefixConst = "const "; + prefixConst += baseString; + const string actual = CanonicalTypeNoSpaces(type); + return actual == CanonicalTypeNoSpaces(std::string_view(suffixConst.data(), suffixConst.size())) || + actual == CanonicalTypeNoSpaces(std::string_view(prefixConst.data(), prefixConst.size())); + } + + static bool IsStdPairOfConstKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* pairArgs = nullptr; + if (!IsStdTemplate(type, "pair", &pairArgs) || pairArgs->size() != 2 || !(*pairArgs)[0].type || + !(*pairArgs)[1].type) + return false; + return IsConstTypeOf(*(*pairArgs)[0].type, key) && SameType(*(*pairArgs)[1].type, value); + } + + static bool IsStdPairOfKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* pairArgs = nullptr; + if (!IsStdTemplate(type, "pair", &pairArgs) || pairArgs->size() != 2 || !(*pairArgs)[0].type || + !(*pairArgs)[1].type) + return false; + return SameType(*(*pairArgs)[0].type, key) && SameType(*(*pairArgs)[1].type, value); + } + + static bool IsDefaultPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* allocArgs = nullptr; + if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) + return false; + return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value); + } + + static bool IsDefaultPairAllocatorWithMutableKey(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* allocArgs = nullptr; + if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) + return false; + return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value) || + IsStdPairOfKeyValue(*(*allocArgs)[0].type, key, value); + } + + static bool IsPmrPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* allocArgs = nullptr; + if (!IsStdPmrTemplate(type, "polymorphic_allocator", &allocArgs) || allocArgs->size() != 1 || + !(*allocArgs)[0].type) + return false; + return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value); + } + + static bool IsAbslOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsAbslTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + static std::string_view AbiBaseName(std::string_view name); + + static bool IsStdStringLike(const DemangledTypeNode& type) + { + const DemangledQualifiedName& name = type.GetName(); + size_t templateIndex = 0; + if (!IsStdScope(name, &templateIndex)) + return false; + if (templateIndex + 1 != name.size()) + return false; + + const std::string_view base = AbiBaseName(name[templateIndex].GetBase()); + if (base == "basic_string" || base == "basic_string_view") + return true; + return !name[templateIndex].HasTemplateArguments() && + (base == "string" || base == "wstring" || base == "u8string" || base == "u16string" || + base == "u32string" || base == "string_view" || base == "wstring_view" || base == "u8string_view" || + base == "u16string_view" || base == "u32string_view"); + } + + static bool IsAbslStringViewLike(const DemangledTypeNode& type) + { + size_t templateIndex = 0; + if (!IsAbslScope(type, &templateIndex)) + return false; + const DemangledQualifiedName& name = type.GetName(); + return templateIndex + 1 == name.size() && !name[templateIndex].HasTemplateArguments() && + name[templateIndex].GetBase() == "string_view"; + } + + static bool IsAbslDefaultStringLikeKey(const DemangledTypeNode& key) + { + return IsStdStringLike(key) || IsAbslStringViewLike(key); + } + + static bool IsAbslNestedOneArgTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, + const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsAbslNestedTemplate(type, scope, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + static bool IsAbslHashDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) + { + if (IsAbslNestedOneArgTemplate(type, "container_internal", "hash_default_hash", key) || + IsAbslNestedOneArgTemplate(type, "hash_internal", "Hash", key) || IsAbslOneArgTemplate(type, "Hash", key)) + return true; + + if (IsAbslNestedName(type, "container_internal", "StringHash")) + return IsAbslDefaultStringLikeKey(key); + + const ParamList* args = nullptr; + if (IsAbslNestedTemplate(type, "container_internal", "BasicStringHash", &args) && args->size() == 1 && + (*args)[0].type) + { + const DemangledTypeNode& charType = *(*args)[0].type; + return CharFamilyAliasName(charType, "", "", nullptr, "", "") || IsAbslDefaultStringLikeKey(key); + } + return false; + } + + static bool IsAbslEqDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) + { + if (IsAbslNestedOneArgTemplate(type, "container_internal", "hash_default_eq", key) || + IsStdOneArgTemplate(type, "equal_to", key)) + return true; + + const ParamList* args = nullptr; + if (IsStdTemplate(type, "equal_to", &args) && args->size() == 1 && (*args)[0].type && + SameType(*(*args)[0].type, "void")) + return true; + + if (IsAbslNestedName(type, "container_internal", "StringEq")) + return IsAbslDefaultStringLikeKey(key); + + if (IsAbslNestedTemplate(type, "container_internal", "BasicStringEq", &args) && args->size() == 1 && + (*args)[0].type) + { + const DemangledTypeNode& charType = *(*args)[0].type; + return CharFamilyAliasName(charType, "", "", nullptr, "", "") || IsAbslDefaultStringLikeKey(key); + } + return false; + } + + static bool IsBoostScope(const DemangledQualifiedName& name, size_t* templateIndex) + { + if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "boost") + return false; + if (name.size() < 2) + return false; + *templateIndex = 1; + return true; + } + + static bool IsBoostScope(const DemangledTypeNode& type, size_t* templateIndex) + { + const DemangledQualifiedName& name = type.GetName(); + return IsBoostScope(name, templateIndex); + } + + static bool IsBoostTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + size_t templateIndex = 0; + if (!IsBoostScope(type, &templateIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (templateIndex + 1 != name.size()) + return false; + if (name[templateIndex].GetBase() != tmpl || !name[templateIndex].HasTemplateArguments()) + return false; + if (args) + *args = &name[templateIndex].GetTemplateArguments(); + return true; + } + + static bool IsBoostOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsBoostTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + static bool GetSimpleLiteralName(const DemangledTypeNode& arg, std::string_view& literal) + { + if (arg.IsConst() || arg.IsVolatile() || arg.GetPointerSuffixBits() != 0 || + arg.GetClass() != NamedTypeReferenceClass) + return false; + + const DemangledQualifiedName& name = arg.GetName(); + if (name.size() != 1 || name[0].HasTemplateArguments()) + return false; + literal = name[0].GetBase(); + return true; + } + + static bool IsIntegerSuffix(char c) + { + return c == 'u' || c == 'U' || c == 'l' || c == 'L'; + } + + static bool MatchesNonTypeValue(std::string_view raw, std::string_view value) + { + raw = TrimSpaces(raw); + size_t castClose = raw.rfind(')'); + if (!raw.empty() && raw[0] == '(' && castClose != std::string_view::npos && castClose + 1 < raw.size()) + raw = TrimSpaces(raw.substr(castClose + 1)); + + size_t rawIndex = 0; + for (char expected: value) + { + while (rawIndex < raw.size() && raw[rawIndex] == ' ') + rawIndex++; + if (rawIndex >= raw.size() || raw[rawIndex] != expected) + return false; + rawIndex++; + } + + while (rawIndex < raw.size() && raw[rawIndex] == ' ') + rawIndex++; + while (rawIndex < raw.size() && IsIntegerSuffix(raw[rawIndex])) + rawIndex++; + while (rawIndex < raw.size() && raw[rawIndex] == ' ') + rawIndex++; + return rawIndex == raw.size(); + } + + static bool IsNonTypeValue(const DemangledTypeNode& arg, std::string_view value) + { + std::string_view literal; + return GetSimpleLiteralName(arg, literal) && MatchesNonTypeValue(literal, value); + } + + static bool CanonicalIntegerLiteral(std::string_view raw, string& out) + { + raw = TrimSpaces(raw); + bool hadCast = false; + size_t castClose = raw.rfind(')'); + if (!raw.empty() && raw[0] == '(' && castClose != std::string_view::npos && castClose + 1 < raw.size()) + { + raw = TrimSpaces(raw.substr(castClose + 1)); + hadCast = true; + } + + string s = RemoveSpaces(raw); + size_t i = 0; + if (i < s.size() && (s[i] == '-' || s[i] == '+')) + i++; + const size_t digitsStart = i; + while (i < s.size() && isdigit(static_cast(s[i]))) + i++; + if (i == digitsStart) + return false; + + const size_t suffixStart = i; + while (i < s.size()) + { + char c = s[i]; + if (c != 'u' && c != 'U' && c != 'l' && c != 'L') + return false; + i++; + } + if (suffixStart == s.size() && !hadCast) + return false; + + out = s.substr(0, suffixStart); + return !out.empty() && out != s; + } + + static bool CanonicalizeNonTypeIntegerLiteral(DemangledTypeNode& type) + { + std::string_view literal; + if (!GetSimpleLiteralName(type, literal)) + return false; + + string canonical; + if (!CanonicalIntegerLiteral(literal, canonical)) + return false; + type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{canonical}); + return true; + } + + static bool IsVoidType(const DemangledTypeNode& arg) + { + return SameType(arg, "void"); + } + + static bool IsPtrdiffType(const DemangledTypeNode& arg) + { + if (IsSimpleNamedType(arg, "long") || IsSimpleNamedType(arg, "long int") || + IsSimpleNamedType(arg, "long long") || IsSimpleNamedType(arg, "long long int") || + IsSimpleNamedType(arg, "__int64") || IsSimpleNamedType(arg, "int64_t") || + IsQualifiedName(arg, {"std", "ptrdiff_t"})) + return true; + + size_t width = 0; + DemangledTypeNode::WidthKind widthKind = DemangledTypeNode::FixedWidth; + bool isSigned = false; + std::string_view altName; + if (GetUnqualifiedIntegerTypeInfo(arg, width, widthKind, isSigned, altName)) + { + if (altName == "long" || altName == "long int" || altName == "long long" || + altName == "long long int" || altName == "__int64" || altName == "int64_t") + return true; + return isSigned && altName.empty() && ResolvedIntegerWidth(width, widthKind) == 8; + } + + string t = CanonicalTypeNoSpaces(arg); + return t == "long" || t == "longint" || t == "longlong" || t == "longlongint" || + t == "__int64" || t == "int64_t" || t == "std::ptrdiff_t"; + } + + static bool IsPointerTo(const DemangledTypeNode& type, const DemangledTypeNode& pointee) + { + const DemangledTypeNode* child = nullptr; + BNReferenceType referenceType = PointerReferenceType; + return !type.IsConst() && !type.IsVolatile() && HasOnlyOmittedPointerSuffixes(type) && + type.GetPointerChildType(child, referenceType) && referenceType == PointerReferenceType && child && + SameType(*child, pointee); + } + + static bool IsReferenceTo(const DemangledTypeNode& type, const DemangledTypeNode& referenced) + { + const DemangledTypeNode* child = nullptr; + BNReferenceType referenceType = PointerReferenceType; + return !type.IsConst() && !type.IsVolatile() && HasOnlyOmittedPointerSuffixes(type) && + type.GetPointerChildType(child, referenceType) && referenceType == ReferenceReferenceType && child && + SameType(*child, referenced); + } + + static bool IsConstPointerTo(const DemangledTypeNode& type, std::string_view pointee) + { + const DemangledTypeNode* child = nullptr; + BNReferenceType referenceType = PointerReferenceType; + return !type.IsConst() && !type.IsVolatile() && HasOnlyOmittedPointerSuffixes(type) && + type.GetPointerChildType(child, referenceType) && referenceType == PointerReferenceType && child && + IsConstTypeNamed(*child, pointee); + } + + static bool IsStringConstIterator(const DemangledTypeNode& type, bool wide, bool pmr) + { + const ParamList* wrapIterArgs = nullptr; + if (IsStdTemplate(type, "__wrap_iter", &wrapIterArgs) && wrapIterArgs->size() == 1 && + (*wrapIterArgs)[0].type) + { + return IsConstPointerTo(*(*wrapIterArgs)[0].type, wide ? "wchar_t" : "char"); + } + + const char* stringName = wide ? "wstring" : "string"; + if (pmr) + { + if (IsQualifiedName(type, {"std", "pmr", stringName, "const_iterator"})) + return true; + } + return IsQualifiedName(type, {"std", stringName, "const_iterator"}); + } + + static const char* RegexIteratorAliasName(const DemangledTypeNode& iterator, const char* charPointerAlias, + const char* wcharPointerAlias, const char* stringAlias, const char* wstringAlias, bool pmr = false) + { + if (IsConstPointerTo(iterator, "char")) + return charPointerAlias; + if (IsConstPointerTo(iterator, "wchar_t")) + return wcharPointerAlias; + if (IsStringConstIterator(iterator, false, pmr)) + return stringAlias; + if (IsStringConstIterator(iterator, true, pmr)) + return wstringAlias; + return nullptr; + } + + static bool IsSubMatchOfIterator(const DemangledTypeNode& type, const DemangledTypeNode& iterator, bool pmr = false) + { + const ParamList* subMatchArgs = nullptr; + if (IsStdTemplate(type, "sub_match", &subMatchArgs) && subMatchArgs->size() == 1 && (*subMatchArgs)[0].type) + return SameType(*(*subMatchArgs)[0].type, iterator); + + size_t templateIndex = 0; + if (!IsStdScope(type, &templateIndex)) + return false; + const DemangledQualifiedName& name = type.GetName(); + if (name.empty() || templateIndex + 1 != name.size() || name[templateIndex].HasTemplateArguments()) + return false; + + const char* expected = RegexIteratorAliasName(iterator, "csub_match", "wcsub_match", "ssub_match", + "wssub_match", pmr); + return expected && name[templateIndex].GetBase() == expected; + } + + static bool IsDefaultSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) + { + const ParamList* allocArgs = nullptr; + if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) + return false; + return IsSubMatchOfIterator(*(*allocArgs)[0].type, iterator); + } + + static bool IsPmrSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) + { + const ParamList* allocArgs = nullptr; + if (!IsStdPmrTemplate(type, "polymorphic_allocator", &allocArgs) || allocArgs->size() != 1 || + !(*allocArgs)[0].type) + return false; + return IsSubMatchOfIterator(*(*allocArgs)[0].type, iterator, true); + } + + static const char* WideStringName(std::string_view name) + { + if (name == "basic_string") return "wstring"; + if (name == "basic_stringbuf") return "wstringbuf"; + if (name == "basic_istringstream") return "wistringstream"; + if (name == "basic_ostringstream") return "wostringstream"; + if (name == "basic_stringstream") return "wstringstream"; + if (name == "basic_ios") return "wios"; + if (name == "basic_streambuf") return "wstreambuf"; + if (name == "basic_istream") return "wistream"; + if (name == "basic_ostream") return "wostream"; + if (name == "basic_iostream") return "wiostream"; + if (name == "basic_filebuf") return "wfilebuf"; + if (name == "basic_ifstream") return "wifstream"; + if (name == "basic_ofstream") return "wofstream"; + if (name == "basic_fstream") return "wfstream"; + return nullptr; + } + + static const char* AtomicAliasForName(std::string_view t) + { + if (t == "bool") return "atomic_bool"; + if (t == "char") return "atomic_char"; + if (t == "signed char") return "atomic_schar"; + if (t == "unsigned char") return "atomic_uchar"; + if (t == "short" || t == "short int") return "atomic_short"; + if (t == "unsigned short" || t == "unsigned short int") return "atomic_ushort"; + if (t == "int") return "atomic_int"; + if (t == "unsigned" || t == "unsigned int") return "atomic_uint"; + if (t == "long" || t == "long int") return "atomic_long"; + if (t == "unsigned long" || t == "unsigned long int") return "atomic_ulong"; + if (t == "long long" || t == "long long int") return "atomic_llong"; + if (t == "unsigned long long" || t == "unsigned long long int") return "atomic_ullong"; + if (t == "char8_t") return "atomic_char8_t"; + if (t == "char16_t") return "atomic_char16_t"; + if (t == "char32_t") return "atomic_char32_t"; + if (t == "wchar_t") return "atomic_wchar_t"; + if (t == "int8_t" || t == "__int8") return "atomic_int8_t"; + if (t == "uint8_t" || t == "unsigned __int8") return "atomic_uint8_t"; + if (t == "int16_t" || t == "__int16") return "atomic_int16_t"; + if (t == "uint16_t" || t == "unsigned __int16") return "atomic_uint16_t"; + if (t == "int32_t" || t == "__int32") return "atomic_int32_t"; + if (t == "uint32_t" || t == "unsigned __int32") return "atomic_uint32_t"; + if (t == "int64_t" || t == "__int64") return "atomic_int64_t"; + if (t == "uint64_t" || t == "unsigned __int64") return "atomic_uint64_t"; + if (t == "int_least8_t") return "atomic_int_least8_t"; + if (t == "uint_least8_t") return "atomic_uint_least8_t"; + if (t == "int_least16_t") return "atomic_int_least16_t"; + if (t == "uint_least16_t") return "atomic_uint_least16_t"; + if (t == "int_least32_t") return "atomic_int_least32_t"; + if (t == "uint_least32_t") return "atomic_uint_least32_t"; + if (t == "int_least64_t") return "atomic_int_least64_t"; + if (t == "uint_least64_t") return "atomic_uint_least64_t"; + if (t == "int_fast8_t") return "atomic_int_fast8_t"; + if (t == "uint_fast8_t") return "atomic_uint_fast8_t"; + if (t == "int_fast16_t") return "atomic_int_fast16_t"; + if (t == "uint_fast16_t") return "atomic_uint_fast16_t"; + if (t == "int_fast32_t") return "atomic_int_fast32_t"; + if (t == "uint_fast32_t") return "atomic_uint_fast32_t"; + if (t == "int_fast64_t") return "atomic_int_fast64_t"; + if (t == "uint_fast64_t") return "atomic_uint_fast64_t"; + if (t == "intptr_t") return "atomic_intptr_t"; + if (t == "uintptr_t") return "atomic_uintptr_t"; + if (t == "size_t") return "atomic_size_t"; + if (t == "ptrdiff_t") return "atomic_ptrdiff_t"; + if (t == "intmax_t") return "atomic_intmax_t"; + if (t == "uintmax_t") return "atomic_uintmax_t"; + return nullptr; + } + + static const char* AtomicAliasForPrimitive(const DemangledTypeNode& type) + { + if (!IsUnqualifiedNonPointerType(type)) + return nullptr; + + if (type.GetClass() == BoolTypeClass) + return "atomic_bool"; + + size_t width = 0; + DemangledTypeNode::WidthKind widthKind = DemangledTypeNode::FixedWidth; + bool isSigned = false; + std::string_view altName; + if (GetUnqualifiedIntegerTypeInfo(type, width, widthKind, isSigned, altName)) + { + if (!altName.empty()) + { + if (const char* alias = AtomicAliasForName(altName)) + return alias; + } + + switch (ResolvedIntegerWidth(width, widthKind)) + { + case 1: + return isSigned ? "atomic_char" : "atomic_uint8_t"; + case 2: + return isSigned ? "atomic_int16_t" : "atomic_uint16_t"; + case 4: + return isSigned ? "atomic_int32_t" : "atomic_uint32_t"; + case 8: + return isSigned ? "atomic_int64_t" : "atomic_uint64_t"; + default: + return nullptr; + } + } + + size_t wideWidth = 0; + std::string_view wideAltName; + if (GetUnqualifiedWideCharTypeInfo(type, wideWidth, wideAltName)) + { + if (!wideAltName.empty()) + { + if (const char* alias = AtomicAliasForName(wideAltName)) + return alias; + } + return "atomic_wchar_t"; + } + + return nullptr; + } + + static const char* AtomicAliasForNamedType(const DemangledTypeNode& type) + { + if (!IsUnqualifiedNonPointerType(type) || type.GetClass() != NamedTypeReferenceClass) + return nullptr; + + const DemangledQualifiedName& name = type.GetName(); + if (name.size() == 1 && !name[0].HasTemplateArguments()) + return AtomicAliasForName(name[0].GetBase()); + + if (name.size() == 2 && !name[0].HasTemplateArguments() && !name[1].HasTemplateArguments() && + StringEquals(name[0].GetBase(), "std")) + return AtomicAliasForName(name[1].GetBase()); + + return nullptr; + } + + static const char* AtomicAliasName(const DemangledTypeNode& type) + { + if (const char* alias = AtomicAliasForPrimitive(type)) + return alias; + if (const char* alias = AtomicAliasForNamedType(type)) + return alias; + + const string t = CanonicalTypeNoSpaces(type); + if (t == "bool") return "atomic_bool"; + if (t == "char") return "atomic_char"; + if (t == "signedchar") return "atomic_schar"; + if (t == "unsignedchar") return "atomic_uchar"; + if (t == "short" || t == "shortint") return "atomic_short"; + if (t == "unsignedshort" || t == "unsignedshortint") return "atomic_ushort"; + if (t == "int") return "atomic_int"; + if (t == "unsigned" || t == "unsignedint") return "atomic_uint"; + if (t == "long" || t == "longint") return "atomic_long"; + if (t == "unsignedlong" || t == "unsignedlongint") return "atomic_ulong"; + if (t == "longlong" || t == "longlongint") return "atomic_llong"; + if (t == "unsignedlonglong" || t == "unsignedlonglongint") return "atomic_ullong"; + if (t == "char8_t") return "atomic_char8_t"; + if (t == "char16_t") return "atomic_char16_t"; + if (t == "char32_t") return "atomic_char32_t"; + if (t == "wchar_t") return "atomic_wchar_t"; + if (t == "int8_t" || t == "std::int8_t" || t == "__int8") return "atomic_int8_t"; + if (t == "uint8_t" || t == "std::uint8_t" || t == "unsigned__int8") return "atomic_uint8_t"; + if (t == "int16_t" || t == "std::int16_t" || t == "__int16") return "atomic_int16_t"; + if (t == "uint16_t" || t == "std::uint16_t" || t == "unsigned__int16") return "atomic_uint16_t"; + if (t == "int32_t" || t == "std::int32_t" || t == "__int32") return "atomic_int32_t"; + if (t == "uint32_t" || t == "std::uint32_t" || t == "unsigned__int32") return "atomic_uint32_t"; + if (t == "int64_t" || t == "std::int64_t" || t == "__int64") return "atomic_int64_t"; + if (t == "uint64_t" || t == "std::uint64_t" || t == "unsigned__int64") return "atomic_uint64_t"; + if (t == "int_least8_t" || t == "std::int_least8_t") return "atomic_int_least8_t"; + if (t == "uint_least8_t" || t == "std::uint_least8_t") return "atomic_uint_least8_t"; + if (t == "int_least16_t" || t == "std::int_least16_t") return "atomic_int_least16_t"; + if (t == "uint_least16_t" || t == "std::uint_least16_t") return "atomic_uint_least16_t"; + if (t == "int_least32_t" || t == "std::int_least32_t") return "atomic_int_least32_t"; + if (t == "uint_least32_t" || t == "std::uint_least32_t") return "atomic_uint_least32_t"; + if (t == "int_least64_t" || t == "std::int_least64_t") return "atomic_int_least64_t"; + if (t == "uint_least64_t" || t == "std::uint_least64_t") return "atomic_uint_least64_t"; + if (t == "int_fast8_t" || t == "std::int_fast8_t") return "atomic_int_fast8_t"; + if (t == "uint_fast8_t" || t == "std::uint_fast8_t") return "atomic_uint_fast8_t"; + if (t == "int_fast16_t" || t == "std::int_fast16_t") return "atomic_int_fast16_t"; + if (t == "uint_fast16_t" || t == "std::uint_fast16_t") return "atomic_uint_fast16_t"; + if (t == "int_fast32_t" || t == "std::int_fast32_t") return "atomic_int_fast32_t"; + if (t == "uint_fast32_t" || t == "std::uint_fast32_t") return "atomic_uint_fast32_t"; + if (t == "int_fast64_t" || t == "std::int_fast64_t") return "atomic_int_fast64_t"; + if (t == "uint_fast64_t" || t == "std::uint_fast64_t") return "atomic_uint_fast64_t"; + if (t == "intptr_t" || t == "std::intptr_t") return "atomic_intptr_t"; + if (t == "uintptr_t" || t == "std::uintptr_t") return "atomic_uintptr_t"; + if (t == "size_t" || t == "std::size_t") return "atomic_size_t"; + if (t == "ptrdiff_t" || t == "std::ptrdiff_t") return "atomic_ptrdiff_t"; + if (t == "intmax_t" || t == "std::intmax_t") return "atomic_intmax_t"; + if (t == "uintmax_t" || t == "std::uintmax_t") return "atomic_uintmax_t"; + return nullptr; + } + + static void ResizeTemplateArgs(DemangledNamePart& c, size_t n) + { + c.GetMutableTemplateArguments().resize(n); + } + + static std::string_view AbiBaseName(std::string_view name) + { + size_t suffixStart = name.size(); + while (suffixStart > 0 && name[suffixStart - 1] == ']') + { + std::string_view prefix = name.substr(0, suffixStart); + size_t tagStart = prefix.rfind("[abi:"); + if (tagStart == std::string_view::npos) + break; + suffixStart = tagStart; + } + return name.substr(0, suffixStart); + } + + static std::string_view AbiTagSuffix(std::string_view name) + { + size_t suffixStart = name.size(); + while (suffixStart > 0 && name[suffixStart - 1] == ']') + { + std::string_view prefix = name.substr(0, suffixStart); + size_t tagStart = prefix.rfind("[abi:"); + if (tagStart == std::string_view::npos) + break; + suffixStart = tagStart; + } + return name.substr(suffixStart); + } + + static void ReplaceWithAlias(DemangledNamePart& c, std::string_view alias) + { + string replacement(alias.data(), alias.size()); + const std::string_view abiSuffix = AbiTagSuffix(c.GetBase()); + replacement.append(abiSuffix.data(), abiSuffix.size()); + c.SetBase(std::move(replacement)); + c.ClearTemplateArguments(); + } + + static bool ApplyStringRule(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.size() != 3 || (name != "basic_string" && name != "basic_stringbuf" && + name != "basic_istringstream" && name != "basic_ostringstream" && name != "basic_stringstream")) + return false; + if (!args[0].type || !args[1].type || !args[2].type) + return false; + + const DemangledTypeNode& element = *args[0].type; + if (!IsStdOneArgTemplate(*args[1].type, "char_traits", element) || + !IsStdOneArgTemplate(*args[2].type, "allocator", element)) + return false; + + if (SameType(element, "char")) + { + if (name == "basic_string") + ReplaceWithAlias(c, "string"); + else + ReplaceWithAlias(c, name.substr(6)); + return true; + } + if (SameType(element, "wchar_t")) + { + if (const char* wide = WideStringName(name)) + ReplaceWithAlias(c, wide); + else + return false; + return true; + } + if (name == "basic_string") + { + if (const char* alias = CharFamilyAliasName(element, "string", "wstring", "u8string", "u16string", + "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + + if (const char* alias = CharFamilyAliasName(element, nullptr, nullptr, name == "basic_string" ? "u8string" : nullptr, + name == "basic_string" ? "u16string" : nullptr, name == "basic_string" ? "u32string" : nullptr)) + { + ReplaceWithAlias(c, alias); + return true; + } + + ResizeTemplateArgs(c, 1); + return true; + } + + static bool ApplyIostreamRule(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.size() != 2 || !StartsWith(name, "basic_")) + return false; + if (name != "basic_ios" && name != "basic_streambuf" && name != "basic_istream" && + name != "basic_ostream" && name != "basic_iostream" && name != "basic_filebuf" && + name != "basic_ifstream" && name != "basic_ofstream" && name != "basic_fstream") + return false; + if (!args[0].type || !args[1].type) + return false; + + const DemangledTypeNode& element = *args[0].type; + if (!IsStdOneArgTemplate(*args[1].type, "char_traits", element)) + return false; + + if (SameType(element, "char")) + { + ReplaceWithAlias(c, name.substr(6)); + return true; + } + if (SameType(element, "wchar_t")) + { + if (const char* wide = WideStringName(name)) + { + ReplaceWithAlias(c, wide); + return true; + } + } + + ResizeTemplateArgs(c, 1); + return true; + } + + static bool ApplyContainerRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "vector" && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if (name == "vector" && args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "lessthan", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "deque" || name == "forward_list" || name == "list") && args.size() == 2 && + args[0].type && args[1].type && IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "stack" || name == "queue") && args.size() == 2 && args[0].type && args[1].type) + { + const ParamList* dequeArgs = nullptr; + if (IsStdTemplate(*args[1].type, "deque", &dequeArgs) && dequeArgs->size() == 1 && + (*dequeArgs)[0].type && SameType(*(*dequeArgs)[0].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + } + + if (name == "priority_queue" && args.size() == 3 && args[0].type && args[1].type && args[2].type) + { + const ParamList* vectorArgs = nullptr; + if (IsStdTemplate(*args[1].type, "vector", &vectorArgs) && vectorArgs->size() == 1 && + (*vectorArgs)[0].type && SameType(*(*vectorArgs)[0].type, *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + } + + if ((name == "set" || name == "multiset") && args.size() == 3 && args[0].type && args[1].type && + args[2].type && IsStdOneArgTemplate(*args[1].type, "less", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "map" || name == "multimap") && args.size() == 4 && args[0].type && args[1].type && + args[2].type && args[3].type && IsStdOneArgTemplate(*args[2].type, "less", *args[0].type) && + IsDefaultPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "unordered_set" || name == "unordered_multiset") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[1].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "unordered_map" || name == "unordered_multimap") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsStdOneArgTemplate(*args[2].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type) && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + return false; + } + + static bool ApplyDiscoveredStdRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "basic_regex" && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "regex_traits", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "unique_ptr" && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "default_delete", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "match_results" && args.size() == 2 && args[0].type && args[1].type && + IsDefaultSubMatchAllocator(*args[1].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "ratio" && args.size() == 2 && args[1].type && IsNonTypeValue(*args[1].type, "1")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "extent" && args.size() == 2 && args[1].type && IsNonTypeValue(*args[1].type, "0")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "enable_if" && args.size() == 2 && args[1].type && IsVoidType(*args[1].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "compare_three_way_result" && args.size() == 2 && args[0].type && args[1].type && + SameType(*args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "formatter" && args.size() == 2 && args[1].type && SameType(*args[1].type, "char")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if ((name == "moneypunct" || name == "moneypunct_byname") && args.size() == 2 && args[1].type && + (IsNonTypeValue(*args[1].type, "false") || IsNonTypeValue(*args[1].type, "0"))) + { + ResizeTemplateArgs(c, 1); + return true; + } + if ((name == "codecvt_utf8" || name == "codecvt_utf16" || name == "codecvt_utf8_utf16") && + args.size() == 3 && args[1].type && args[2].type && IsNonTypeValue(*args[1].type, "1114111") && + IsNonTypeValue(*args[2].type, "0")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "istream_iterator" && args.size() == 4 && args[1].type && args[2].type && args[3].type && + SameType(*args[1].type, "char") && IsStdOneArgTemplate(*args[2].type, "char_traits", "char") && + IsPtrdiffType(*args[3].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "ostream_iterator" && args.size() == 3 && args[1].type && args[2].type && + SameType(*args[1].type, "char") && IsStdOneArgTemplate(*args[2].type, "char_traits", "char")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "iterator" && args.size() == 5 && args[1].type && args[2].type && args[3].type && + args[4].type && IsPtrdiffType(*args[2].type) && IsPointerTo(*args[3].type, *args[1].type) && + IsReferenceTo(*args[4].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + if ((name == "default_searcher" || name == "boyer_moore_searcher" || + name == "boyer_moore_horspool_searcher") && args.size() == 2 && args[1].type) + { + const ParamList* equalArgs = nullptr; + if (IsStdTemplate(*args[1].type, "equal_to", &equalArgs) && equalArgs->size() == 1 && + (*equalArgs)[0].type && IsVoidType(*(*equalArgs)[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + } + if ((name == "boyer_moore_searcher" || name == "boyer_moore_horspool_searcher") && args.size() == 3 && + args[2].type && IsStdOneArgTemplate(*args[2].type, "equal_to", "void")) + { + ResizeTemplateArgs(c, 2); + return true; + } + return false; + } + + static bool ApplyChronoRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + if (c.GetBase() != "duration" || args.size() != 2 || !args[1].type) + return false; + + const ParamList* ratioArgs = nullptr; + if (IsStdTemplate(*args[1].type, "ratio", &ratioArgs) && ratioArgs->size() == 1 && + (*ratioArgs)[0].type && IsNonTypeValue(*(*ratioArgs)[0].type, "1")) + { + ResizeTemplateArgs(c, 1); + return true; + } + return false; + } + + static bool ApplyGenericStdDefaultRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + if (args.size() == 2 && args[0].type && args[1].type && + (IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) || + IsStdOneArgTemplate(*args[1].type, "istreambuf_iterator", *args[0].type) || + IsStdOneArgTemplate(*args[1].type, "ostreambuf_iterator", *args[0].type))) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if (args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + return false; + } + + static bool ApplyStdFunctionTemplateRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if ((name == "operator<<" || name == "operator+") && args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + if (name == "operator<<") + c.ClearTemplateArguments(); + else + ResizeTemplateArgs(c, 1); + return true; + } + + if (name != "operator==" && name != "operator!=" && name != "operator<" && name != "operator<=" && + name != "operator>" && name != "operator>=" && name != "operator<=>") + return false; + + if (args.size() == 5 && args[0].type && args[1].type && args[2].type && args[3].type && args[4].type && + IsStdOneArgTemplate(*args[2].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type) && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + if (args.size() == 4 && args[0].type && args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[1].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (args.size() == 4 && args[0].type && args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[2].type, "less", *args[0].type) && + IsDefaultPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + if (args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "less", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + return false; + } + + static bool ApplyStdTemplateRules(DemangledNamePart& c) + { + if (ApplyStringRule(c)) + return true; + if (ApplyIostreamRule(c)) + return true; + if (ApplyContainerRules(c)) + return true; + if (ApplyDiscoveredStdRules(c)) + return true; + if (ApplyStdFunctionTemplateRules(c)) + return true; + return ApplyGenericStdDefaultRules(c); + } + + static bool ApplyAbslTemplateRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if ((name == "flat_hash_set" || name == "node_hash_set") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + const bool defaultHash = IsAbslHashDefault(*args[1].type, *args[0].type); + const bool defaultEq = IsAbslEqDefault(*args[2].type, *args[0].type); + if (defaultHash && defaultEq) + ResizeTemplateArgs(c, 1); + else if (defaultEq) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "flat_hash_map" || name == "node_hash_map") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + const bool defaultHash = IsAbslHashDefault(*args[2].type, *args[0].type); + const bool defaultEq = IsAbslEqDefault(*args[3].type, *args[0].type); + if (defaultHash && defaultEq) + ResizeTemplateArgs(c, 2); + else if (defaultEq) + ResizeTemplateArgs(c, 3); + else + ResizeTemplateArgs(c, 4); + return true; + } + + if ((name == "btree_set" || name == "btree_multiset") && args.size() == 3 && args[0].type && + args[1].type && args[2].type && IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + if (IsStdOneArgTemplate(*args[1].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 1); + else + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "btree_map" || name == "btree_multimap") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsDefaultPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + if (IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if (name == "InlinedVector" && args.size() == 3 && args[0].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + if (name == "FixedArray" && args.size() == 3 && args[0].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + return false; + } + + static bool ApplyBoostContainerStringRule(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (name != "basic_string" || args.size() != 3 || !args[0].type || !args[1].type || !args[2].type) + return false; + + const DemangledTypeNode& element = *args[0].type; + if (!IsStdOneArgTemplate(*args[1].type, "char_traits", element) || + !IsStdOneArgTemplate(*args[2].type, "allocator", element)) + return false; + + if (const char* alias = CharFamilyAliasName(element, "string", "wstring", "u8string", "u16string", + "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + + ResizeTemplateArgs(c, 1); + return true; + } + + static bool ApplyBoostContainerTemplateRules(DemangledNamePart& c) + { + if (ApplyBoostContainerStringRule(c)) + return true; + + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if ((name == "vector" || name == "deque" || name == "list" || name == "slist" || + name == "stable_vector") && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if (name == "small_vector" && args.size() == 3 && args[0].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "set" || name == "multiset" || name == "flat_set" || name == "flat_multiset") && + args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + if (IsStdOneArgTemplate(*args[1].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 1); + else + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "map" || name == "multimap" || name == "flat_map" || name == "flat_multimap") && + args.size() == 4 && args[0].type && args[1].type && args[2].type && args[3].type && + IsDefaultPairAllocatorWithMutableKey(*args[3].type, *args[0].type, *args[1].type)) + { + if (IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + return false; + } + + static bool ApplyBoostUnorderedTemplateRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if ((name == "unordered_set" || name == "unordered_multiset") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + const bool defaultHash = IsBoostOneArgTemplate(*args[1].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 1); + else if (defaultEqual) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "unordered_map" || name == "unordered_multimap") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + const bool defaultHash = IsBoostOneArgTemplate(*args[2].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 2); + else if (defaultEqual) + ResizeTemplateArgs(c, 3); + else + ResizeTemplateArgs(c, 4); + return true; + } + + return false; + } + + static bool ApplyPmrAliasToStdTemplate(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "basic_string" && args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) && + IsPmrPolymorphicAllocator(*args[2].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "vector" || name == "deque" || name == "forward_list" || name == "list") && + args.size() == 2 && args[0].type && args[1].type && + IsPmrPolymorphicAllocator(*args[1].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "set" || name == "multiset") && args.size() == 3 && args[0].type && args[1].type && + args[2].type && IsPmrPolymorphicAllocator(*args[2].type, *args[0].type)) + { + if (IsStdOneArgTemplate(*args[1].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 1); + else + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "map" || name == "multimap") && args.size() == 4 && args[0].type && args[1].type && + args[2].type && args[3].type && IsPmrPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + if (IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "unordered_set" || name == "unordered_multiset") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsPmrPolymorphicAllocator(*args[3].type, *args[0].type)) + { + const bool defaultHash = IsStdOneArgTemplate(*args[1].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 1); + else if (defaultEqual) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "unordered_map" || name == "unordered_multimap") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsPmrPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + const bool defaultHash = IsStdOneArgTemplate(*args[2].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 2); + else if (defaultEqual) + ResizeTemplateArgs(c, 3); + else + ResizeTemplateArgs(c, 4); + return true; + } + + if (name == "match_results" && args.size() == 2 && args[0].type && args[1].type && + IsPmrSubMatchAllocator(*args[1].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + return false; + } + + static bool ApplyConcreteStdAliasRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "atomic" && args.size() == 1 && args[0].type) + { + if (const char* alias = AtomicAliasName(*args[0].type)) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "basic_string" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "string", "wstring", "u8string", + "u16string", "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "basic_string_view" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "string_view", "wstring_view", + "u8string_view", "u16string_view", "u32string_view")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "basic_regex" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "regex", "wregex")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "sub_match" && args.size() == 1 && args[0].type) + { + if (const char* alias = RegexIteratorAliasName(*args[0].type, "csub_match", "wcsub_match", + "ssub_match", "wssub_match")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "match_results" && args.size() == 1 && args[0].type) + { + if (const char* alias = RegexIteratorAliasName(*args[0].type, "cmatch", "wcmatch", "smatch", + "wsmatch")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if ((name == "regex_iterator" || name == "regex_token_iterator") && (args.size() == 1 || args.size() == 3) && + args[0].type) + { + if (args.size() == 3) + { + if (!args[1].type || !args[2].type) + return false; + const bool charIterator = IsConstPointerTo(*args[0].type, "char") || + IsStringConstIterator(*args[0].type, false, false); + const bool wcharIterator = IsConstPointerTo(*args[0].type, "wchar_t") || + IsStringConstIterator(*args[0].type, true, false); + if (!((charIterator && SameType(*args[1].type, "char") && + IsStdOneArgTemplate(*args[2].type, "regex_traits", "char")) || + (wcharIterator && SameType(*args[1].type, "wchar_t") && + IsStdOneArgTemplate(*args[2].type, "regex_traits", "wchar_t")))) + return false; + } + + const bool token = name == "regex_token_iterator"; + const char* alias = RegexIteratorAliasName(*args[0].type, + token ? "cregex_token_iterator" : "cregex_iterator", + token ? "wcregex_token_iterator" : "wcregex_iterator", + token ? "sregex_token_iterator" : "sregex_iterator", + token ? "wsregex_token_iterator" : "wsregex_iterator"); + if (alias) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if ((name == "basic_syncbuf" || name == "basic_osyncstream" || name == "basic_spanbuf" || + name == "basic_ispanstream" || name == "basic_ospanstream" || name == "basic_spanstream") && + args.size() == 1 && args[0].type) + { + if (name == "basic_syncbuf") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "syncbuf", "wsyncbuf")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_osyncstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "osyncstream", "wosyncstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_spanbuf") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "spanbuf", "wspanbuf")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_ispanstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "ispanstream", "wispanstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_ospanstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "ospanstream", "wospanstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_spanstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "spanstream", "wspanstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + } + return false; + } + + static bool ApplyPmrConcreteAliasRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (name == "basic_string" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "string", "wstring", "u8string", + "u16string", "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "match_results" && args.size() == 1 && args[0].type) + { + if (const char* alias = RegexIteratorAliasName(*args[0].type, "cmatch", "wcmatch", "smatch", + "wsmatch", true)) + { + ReplaceWithAlias(c, alias); + return true; + } + } + return false; + } + + static void SimplifyTemplateArguments(DemangledNamePart& c, bool& changed) + { + for (auto& arg: c.GetMutableTemplateArguments()) + { + if (!arg.type) + continue; + DemangledTypeNode mutableType = *arg.type; + bool argChanged = CanonicalizeNonTypeIntegerLiteral(mutableType); + auto nested = DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(mutableType); + if (argChanged || nested.changed) + { + arg.type = DemangledTypeNode::CreateShared(std::move(mutableType)); + changed = true; + } + } + } + + static bool ApplyNamespaceRules(DemangledQualifiedName& name) + { + bool changed = false; + if (!name.empty() && !name[0].HasTemplateArguments() && name[0].GetBase() == "__gnu_cxx") + { + name.erase(name.begin()); + changed = true; + } + + for (size_t i = 1; i < name.size();) + { + if (!name[i - 1].HasTemplateArguments() && name[i - 1].GetBase() == "std" && + IsInlineStdNamespace(name[i])) + { + name.erase(name.begin() + i); + changed = true; + continue; + } + i++; + } + for (size_t i = 1; i < name.size();) + { + if (!name[i - 1].HasTemplateArguments() && name[i - 1].GetBase() == "absl" && + IsAbslInlineNamespace(name[i])) + { + name.erase(name.begin() + i); + changed = true; + continue; + } + i++; + } + return changed; + } + + static bool ApplyStdRules(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") + continue; + if (ApplyStdTemplateRules(name[i])) + changed = true; + } + + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") + continue; + if (ApplyPmrAliasToStdTemplate(name[i])) + { + name.insert(name.begin() + i, DemangledNamePart("pmr")); + changed = true; + i++; + } + } + + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") + continue; + if (ApplyConcreteStdAliasRules(name[i])) + changed = true; + } + + for (size_t i = 2; i < name.size(); i++) + { + if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "std" || + name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "pmr") + continue; + if (ApplyPmrConcreteAliasRules(name[i])) + changed = true; + } + + for (size_t i = 2; i < name.size(); i++) + { + if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "std" || + name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "chrono") + continue; + if (ApplyChronoRules(name[i])) + changed = true; + } + return changed; + } + + static bool ApplyAbslRules(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "absl") + continue; + if (ApplyAbslTemplateRules(name[i])) + changed = true; + } + return changed; + } + + static bool ApplyBoostRules(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "boost") + continue; + if (ApplyBoostUnorderedTemplateRules(name[i])) + changed = true; + } + + for (size_t i = 2; i < name.size(); i++) + { + if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "boost" || + name[i - 1].HasTemplateArguments()) + continue; + + if (name[i - 1].GetBase() == "container") + { + if (ApplyBoostContainerTemplateRules(name[i])) + changed = true; + } + else if (name[i - 1].GetBase() == "unordered") + { + if (ApplyBoostUnorderedTemplateRules(name[i])) + changed = true; + } + } + return changed; + } + + struct AbiTaggedName + { + string base; + string tags; + }; + + static AbiTaggedName SplitAbiTagSuffix(std::string_view name) + { + size_t suffixStart = name.size(); + while (suffixStart > 0 && name[suffixStart - 1] == ']') + { + std::string_view prefix = name.substr(0, suffixStart); + size_t tagStart = prefix.rfind("[abi:"); + if (tagStart == std::string_view::npos) + break; + suffixStart = tagStart; + } + return { + string(name.substr(0, suffixStart)), + string(name.substr(suffixStart)) + }; + } + + static string AddAbiTags(const string& base, const string& tags) + { + if (tags.empty() || (base.size() >= tags.size() && base.compare(base.size() - tags.size(), tags.size(), tags) == 0)) + return base; + return base + tags; + } + + static bool ConstructorNameMatches(const string& previousBase, const string& currentBase) + { + if (previousBase == currentBase) + return true; + if (currentBase.size() > previousBase.size() + 1 && + currentBase.compare(0, previousBase.size(), previousBase) == 0 && + currentBase[previousBase.size()] == '<') + return true; + if (StartsWith(std::string_view(currentBase.data(), currentBase.size()), "basic_") && + previousBase == currentBase.substr(6)) + return true; + if (const char* wideName = WideStringName(currentBase)) + return previousBase == wideName; + return false; + } + + static bool RewriteSimplifiedConstructorSegments(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + std::string_view currentName = name[i].GetBase(); + bool destructor = false; + if (!currentName.empty() && currentName[0] == '~') + { + destructor = true; + currentName.remove_prefix(1); + } + + AbiTaggedName current = SplitAbiTagSuffix(currentName); + AbiTaggedName previous = SplitAbiTagSuffix(name[i - 1].GetBase()); + if (current.base.empty() || previous.base.empty() || !ConstructorNameMatches(previous.base, current.base)) + continue; + + string replacement = AddAbiTags(previous.base, current.tags); + if (destructor) + replacement = "~" + replacement; + + bool argsChanged = false; + DemangledNamePart simplifiedCurrent = name[i]; + if (name[i].HasTemplateArguments() && !destructor) + { + simplifiedCurrent.SetBase(current.base); + if (ApplyStdTemplateRules(simplifiedCurrent) || ApplyConcreteStdAliasRules(simplifiedCurrent) || + ApplyAbslTemplateRules(simplifiedCurrent) || + ApplyBoostContainerTemplateRules(simplifiedCurrent) || + ApplyBoostUnorderedTemplateRules(simplifiedCurrent)) + { + const std::string_view simplifiedBase = AbiBaseName(simplifiedCurrent.GetBase()); + if (simplifiedBase == previous.base || simplifiedBase == replacement) + { + argsChanged = true; + } + } + } + + if (replacement != name[i].GetBase()) + { + name[i].SetBase(replacement); + changed = true; + } + if (argsChanged) + { + name[i].SetTemplateArguments(simplifiedCurrent.GetTemplateArguments(), true); + if (!simplifiedCurrent.HasTemplateArguments()) + name[i].ClearTemplateArguments(); + changed = true; + } + } + return changed; + } + + static bool IsAliasName(std::string_view alias, const unordered_set& aliases) + { + return aliases.find(alias) != aliases.end(); + } + + static bool IsTopLevelSimplifiedAlias(const DemangledQualifiedName& name) + { + if (name.empty() || name[0].HasTemplateArguments()) + return false; + + if (name[0].GetBase() == "std") + { + static const unordered_set stdAliases = { + "string", "wstring", "u8string", "u16string", "u32string", + "string_view", "wstring_view", "u8string_view", "u16string_view", "u32string_view", + "stringbuf", "wstringbuf", "istringstream", "wistringstream", + "ostringstream", "wostringstream", "stringstream", "wstringstream", + "ios", "wios", "streambuf", "wstreambuf", "istream", "wistream", + "ostream", "wostream", "iostream", "wiostream", "filebuf", "wfilebuf", + "ifstream", "wifstream", "ofstream", "wofstream", "fstream", "wfstream", + "syncbuf", "wsyncbuf", "osyncstream", "wosyncstream", "spanbuf", "wspanbuf", + "ispanstream", "wispanstream", "ospanstream", "wospanstream", "spanstream", "wspanstream", + "regex", "wregex", "csub_match", "wcsub_match", "ssub_match", "wssub_match", + "cmatch", "wcmatch", "smatch", "wsmatch", "cregex_iterator", "wcregex_iterator", + "sregex_iterator", "wsregex_iterator", "cregex_token_iterator", "wcregex_token_iterator", + "sregex_token_iterator", "wsregex_token_iterator", + "atomic_bool", "atomic_char", "atomic_schar", "atomic_uchar", + "atomic_short", "atomic_ushort", "atomic_int", "atomic_uint", + "atomic_long", "atomic_ulong", "atomic_llong", "atomic_ullong", + "atomic_char8_t", "atomic_char16_t", "atomic_char32_t", "atomic_wchar_t", + "atomic_int8_t", "atomic_uint8_t", "atomic_int16_t", "atomic_uint16_t", + "atomic_int32_t", "atomic_uint32_t", "atomic_int64_t", "atomic_uint64_t", + "atomic_int_least8_t", "atomic_uint_least8_t", "atomic_int_least16_t", + "atomic_uint_least16_t", "atomic_int_least32_t", "atomic_uint_least32_t", + "atomic_int_least64_t", "atomic_uint_least64_t", "atomic_int_fast8_t", + "atomic_uint_fast8_t", "atomic_int_fast16_t", "atomic_uint_fast16_t", + "atomic_int_fast32_t", "atomic_uint_fast32_t", "atomic_int_fast64_t", + "atomic_uint_fast64_t", "atomic_intptr_t", "atomic_uintptr_t", + "atomic_size_t", "atomic_ptrdiff_t", "atomic_intmax_t", "atomic_uintmax_t", + "atomic_signed_lock_free", "atomic_unsigned_lock_free", "streampos" + }; + + if (name.size() == 2 && !name[1].HasTemplateArguments()) + return IsAliasName(name[1].GetBase(), stdAliases); + + if (name.size() != 3 || name[1].HasTemplateArguments() || name[1].GetBase() != "pmr" || + name[2].HasTemplateArguments()) + return false; + + static const unordered_set pmrAliases = { + "string", "wstring", "u8string", "u16string", "u32string", + "vector", "deque", "list", "forward_list", + "map", "multimap", "set", "multiset", + "unordered_map", "unordered_multimap", "unordered_set", "unordered_multiset", + "match_results", "cmatch", "wcmatch", "smatch", "wsmatch" + }; + return IsAliasName(name[2].GetBase(), pmrAliases); + } + + if (name[0].GetBase() == "boost" && name.size() == 3 && !name[1].HasTemplateArguments() && + name[1].GetBase() == "container" && !name[2].HasTemplateArguments()) + { + static const unordered_set boostContainerAliases = { + "string", "wstring", "u8string", "u16string", "u32string" + }; + return IsAliasName(name[2].GetBase(), boostContainerAliases); + } + + return false; + } + + static bool HasTemplateArgs(const DemangledQualifiedName& name) + { + for (const auto& component: name) + if (component.HasTemplateArguments()) + return true; + return false; + } + + // Compatibility parser for qualified names that do not come from a demangler AST. + // Native GNU3/MSVC paths use the structured DemangledTypeNode overloads. + static size_t FindTemplateOpen(std::string_view component) + { + component = TrimSpaces(component); + if (StartsWith(component, "operator")) + { + static constexpr std::string_view operators[] = { + "operator<=>", "operator<<=", "operator>>=", "operator->*", "operator++", "operator--", + "operator<<", "operator>>", "operator<=", "operator>=", "operator==", "operator!=", + "operator+=", "operator-=", "operator*=", "operator/=", "operator%=", "operator&=", + "operator|=", "operator^=", "operator&&", "operator||", "operator()", "operator[]", + "operator->", "operator<", "operator>", "operator+", "operator-", "operator*", + "operator/", "operator%", "operator&", "operator|", "operator^", "operator~", + "operator!", "operator=", "operator," + }; + for (std::string_view op: operators) + { + if (component.size() > op.size() && component.compare(0, op.size(), op) == 0 && + component[op.size()] == '<') + return op.size(); + } + return std::string_view::npos; + } + int depth = 0; + for (size_t i = 0; i < component.size(); i++) + { + if (component[i] == '<') + { + if (depth == 0) + return i; + depth++; + } + else if (component[i] == '>' && depth > 0) + { + depth--; + } + } + return std::string_view::npos; + } + + static vector SplitTopLevel(std::string_view s, char delimiter) + { + vector out; + int angleDepth = 0, parenDepth = 0, bracketDepth = 0; + size_t start = 0; + for (size_t i = 0; i < s.size(); i++) + { + char c = s[i]; + if (delimiter == ':' && i + 1 < s.size() && c == ':' && s[i + 1] == ':' && + angleDepth == 0 && parenDepth == 0 && bracketDepth == 0) + { + out.push_back(TrimSpaces(s.substr(start, i - start))); + i++; + start = i + 1; + continue; + } + if (delimiter != ':' && c == delimiter && angleDepth == 0 && parenDepth == 0 && bracketDepth == 0) + { + out.push_back(TrimSpaces(s.substr(start, i - start))); + start = i + 1; + continue; + } + switch (c) + { + case '<': angleDepth++; break; + case '>': if (angleDepth > 0) angleDepth--; break; + case '(': parenDepth++; break; + case ')': if (parenDepth > 0) parenDepth--; break; + case '[': bracketDepth++; break; + case ']': if (bracketDepth > 0) bracketDepth--; break; + default: break; + } + } + out.push_back(TrimSpaces(s.substr(start))); + return out; + } + + static DemangledTypeNode ParseCompatibilityType(std::string_view s); + + static ParamList ParseCompatibilityArgs(std::string_view s) + { + ParamList args; + for (std::string_view arg: SplitTopLevel(s, ',')) + { + DemangledTypeNode node = ParseCompatibilityType(arg); + args.push_back({"", DemangledTypeNode::CreateShared(std::move(node))}); + } + return args; + } + + static DemangledQualifiedName ParseCompatibilityName(std::string_view s) + { + DemangledQualifiedName out; + for (std::string_view part: SplitTopLevel(s, ':')) + { + part = TrimSpaces(part); + size_t open = FindTemplateOpen(part); + if (open == std::string_view::npos) + { + out.emplace_back(part); + continue; + } + size_t close = part.rfind('>'); + if (close == std::string_view::npos || close < open) + { + out.emplace_back(part); + continue; + } + + std::string_view base = TrimSpaces(part.substr(0, open)); + DemangledNamePart segment{base}; + segment.SetTemplateArguments(ParseCompatibilityArgs(part.substr(open + 1, close - open - 1)), true); + out.push_back(std::move(segment)); + } + return out; + } + + static DemangledTypeNode ParseCompatibilityType(std::string_view s) + { + s = StripLeadingTypeKeyword(s); + if (s.empty()) + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{""}); + if (s.find("::") == std::string_view::npos && FindTemplateOpen(s) == std::string_view::npos) + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{string(s)}); + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, ParseCompatibilityName(s)); + } + + static StringList RenderSegments(const DemangledQualifiedName& name) + { + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, name).RenderTypeNameSegments(); + } +} + +DemangledTemplateSimplifier::SimplifyNameResult DemangledTemplateSimplifier::SimplifyNameSegmentsInPlace( + DemangledQualifiedName& name) +{ + SimplifyNameResult result; + if (name.empty()) + return result; + + bool changed = false; + for (auto& component: name) + SimplifyTemplateArguments(component, changed); + changed |= ApplyNamespaceRules(name); + changed |= ApplyStdRules(name); + changed |= ApplyAbslRules(name); + changed |= ApplyBoostRules(name); + changed |= RewriteSimplifiedConstructorSegments(name); + + result.changed = changed; + result.topLevelIsAlias = IsTopLevelSimplifiedAlias(name); + return result; +} + +DemangledTemplateSimplifier::SimplifyNameResult DemangledTemplateSimplifier::SimplifyTypeNodeInPlace( + DemangledTypeNode& type) +{ + SimplifyNameResult result; + bool topLevelNameIsAlias = false; + result.changed = type.MutateQualifiedNames([&](DemangledQualifiedName& name) { + auto nameResult = SimplifyNameSegmentsInPlace(name); + if (nameResult.topLevelIsAlias) + { + topLevelNameIsAlias = true; + result.topLevelIsAlias = true; + } + return nameResult.changed; + }); + if (topLevelNameIsAlias && type.GetClass() == NamedTypeReferenceClass && type.GetNTRClass() != UnknownNamedTypeClass) + { + type.SetNTRType(UnknownNamedTypeClass); + result.changed = true; + } + if (type.MutateChildTypes([&](DemangledTypeNode& child) { + auto childResult = SimplifyTypeNodeInPlace(child); + if (childResult.topLevelIsAlias) + result.topLevelIsAlias = true; + return childResult.changed; + })) + result.changed = true; + return result; +} + +BN::QualifiedName DemangledTemplateSimplifier::SimplifyQualifiedName(const BN::QualifiedName& name) +{ + string rendered = name.GetString(); + std::string_view stripped(rendered.data(), rendered.size()); + (void)ExtractLeadingTypeKeyword(stripped); + DemangledQualifiedName parsed = ParseCompatibilityName(stripped); + SimplifyNameSegmentsInPlace(parsed); + return BN::QualifiedName(RenderSegments(parsed)); +} + +bool DemangledTemplateSimplifier::NameSegmentsHaveTemplateArguments(const DemangledQualifiedName& name) +{ + return HasTemplateArgs(name); +} diff --git a/demangler/demangled_template_simplifier.h b/demangler/demangled_template_simplifier.h new file mode 100644 index 000000000..b57c4f281 --- /dev/null +++ b/demangler/demangled_template_simplifier.h @@ -0,0 +1,31 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// 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. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "demangled_type_node.h" + +namespace DemangledTemplateSimplifier +{ + struct SimplifyNameResult + { + bool changed = false; + bool topLevelIsAlias = false; + }; + + BN::QualifiedName SimplifyQualifiedName(const BN::QualifiedName& name); + SimplifyNameResult SimplifyNameSegmentsInPlace(DemangledQualifiedName& name); + SimplifyNameResult SimplifyTypeNodeInPlace(DemangledTypeNode& type); + bool NameSegmentsHaveTemplateArguments(const DemangledQualifiedName& name); +} diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index afbebf457..ce511a291 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -16,8 +16,8 @@ #ifdef BINARYNINJACORE_LIBRARY #include "binaryview.h" #endif +#include "base/assertions.h" #include -#include #include #include @@ -231,6 +231,18 @@ DemangledNamePart::DemangledNamePart(string base): } +DemangledNamePart::DemangledNamePart(const char* base): + m_base(base), m_hasTemplateArgs(false), m_spaceAfterTemplateComma(false) +{ +} + + +DemangledNamePart::DemangledNamePart(std::string_view base): + m_base(base), m_hasTemplateArgs(false), m_spaceAfterTemplateComma(false) +{ +} + + DemangledNamePart::DemangledNamePart(string base, std::shared_ptr baseTypeSuffix): m_base(std::move(base)), m_baseTypeSuffix(std::move(baseTypeSuffix)), m_hasTemplateArgs(false), m_spaceAfterTemplateComma(false) @@ -254,6 +266,14 @@ void DemangledNamePart::SetTemplateArguments(vector args } +void DemangledNamePart::ClearTemplateArguments() +{ + m_templateArgs.clear(); + m_hasTemplateArgs = false; + m_spaceAfterTemplateComma = false; +} + + void DemangledNamePart::AppendString(string& out, Platform* platform) const { out += m_base; @@ -491,7 +511,7 @@ void DemangledTypeNode::SetImplicitThisParameter(DemangledTypeNode type) payload->implicitThisParameterType = CreateShared(std::move(type)); return; } - assert(false && "SetImplicitThisParameter called for non-function demangled type"); + BN_ASSERT(false && "SetImplicitThisParameter called for non-function demangled type"); } @@ -615,6 +635,52 @@ DemangledTypeNode::NodeRef DemangledTypeNode::GetPrimaryChild() const } +bool DemangledTypeNode::MutateChildTypes(const std::function& mutator) +{ + bool changed = false; + auto mutateRef = [&](NodeRef& typeRef) { + if (!typeRef) + return; + DemangledTypeNode mutableType = *typeRef; + if (mutator(mutableType)) + { + typeRef = CreateShared(std::move(mutableType)); + changed = true; + } + }; + + if (auto payload = std::get_if(&m_payload)) + mutateRef(payload->childType); + else if (auto payload = std::get_if(&m_payload)) + mutateRef(payload->childType); + else if (auto payload = std::get_if(&m_payload)) + mutateRef(payload->childType); + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->returnType); + mutateRef(payload->implicitThisParameterType); + for (auto& param: payload->params) + mutateRef(param.type); + } + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->childType); + mutateRef(payload->suffixType); + } + return changed; +} + + +bool DemangledTypeNode::MutateQualifiedNames(const std::function& mutator) +{ + if (auto payload = std::get_if(&m_payload)) + return mutator(payload->ownerName); + else if (auto payload = std::get_if(&m_payload)) + return mutator(payload->name); + return false; +} + + bool DemangledTypeNode::AddQualifiersToPointerChild(bool cnst, bool vltl) { NodeRef* childType = nullptr; @@ -649,7 +715,7 @@ DemangledQualifiedName& DemangledTypeNode::GetMutableName() { if (auto payload = std::get_if(&m_payload)) return payload->name; - assert(false && "GetMutableName called for non-named demangled type"); + BN_ASSERT(false && "GetMutableName called for non-named demangled type"); static thread_local DemangledQualifiedName empty; empty.clear(); return empty; @@ -663,7 +729,7 @@ void DemangledTypeNode::SetName(DemangledQualifiedName name) payload->name = std::move(name); return; } - assert(false && "SetName called for non-named demangled type"); + BN_ASSERT(false && "SetName called for non-named demangled type"); } @@ -675,6 +741,53 @@ BNNamedTypeReferenceClass DemangledTypeNode::GetNTRClass() const } +bool DemangledTypeNode::GetIntegerTypeInfo(size_t& width, WidthKind& widthKind, bool& isSigned, + std::string_view& altName) const +{ + width = 0; + widthKind = FixedWidth; + isSigned = false; + altName = {}; + if (auto payload = std::get_if(&m_payload)) + { + width = payload->width; + widthKind = payload->widthKind; + isSigned = payload->isSigned; + altName = std::string_view(payload->altName.data(), payload->altName.size()); + return true; + } + return false; +} + + +bool DemangledTypeNode::GetWideCharTypeInfo(size_t& width, std::string_view& altName) const +{ + width = 0; + altName = {}; + if (auto payload = std::get_if(&m_payload)) + { + width = payload->width; + altName = std::string_view(payload->altName.data(), payload->altName.size()); + return true; + } + return false; +} + + +bool DemangledTypeNode::GetPointerChildType(const DemangledTypeNode*& childType, BNReferenceType& referenceType) const +{ + childType = nullptr; + referenceType = PointerReferenceType; + if (auto payload = std::get_if(&m_payload)) + { + childType = payload->childType.get(); + referenceType = payload->referenceType; + return true; + } + return false; +} + + void DemangledTypeNode::SetNTRType(BNNamedTypeReferenceClass cls) { if (auto payload = std::get_if(&m_payload)) @@ -682,7 +795,7 @@ void DemangledTypeNode::SetNTRType(BNNamedTypeReferenceClass cls) payload->ntrClass = cls; return; } - assert(false && "SetNTRType called for non-named demangled type"); + BN_ASSERT(false && "SetNTRType called for non-named demangled type"); } @@ -693,7 +806,7 @@ void DemangledTypeNode::SetParenthesizedMemberPointer(bool parenthesized) payload->parenthesized = parenthesized; return; } - assert(false && "SetParenthesizedMemberPointer called for non-member-pointer demangled type"); + BN_ASSERT(false && "SetParenthesizedMemberPointer called for non-member-pointer demangled type"); } @@ -704,7 +817,7 @@ void DemangledTypeNode::SetCallingConventionName(BNCallingConventionName cc) payload->callingConventionName = cc; return; } - assert(false && "SetCallingConventionName called for non-function demangled type"); + BN_ASSERT(false && "SetCallingConventionName called for non-function demangled type"); } @@ -758,12 +871,12 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons return true; }; - if (auto payload = std::get_if(&m_payload)) - return payload && std::get_if(&other.m_payload); - if (auto payload = std::get_if(&m_payload)) - return payload && std::get_if(&other.m_payload); - if (auto payload = std::get_if(&m_payload)) - return payload && std::get_if(&other.m_payload); + if (std::get_if(&m_payload)) + return true; + if (std::get_if(&m_payload)) + return true; + if (std::get_if(&m_payload)) + return true; if (auto payload = std::get_if(&m_payload)) { auto otherPayload = std::get_if(&other.m_payload); diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index 39573d94a..77f3c2b1a 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -41,7 +41,9 @@ #endif #include +#include #include +#include #include #ifdef BINARYNINJACORE_LIBRARY @@ -67,6 +69,8 @@ class DemangledNamePart DemangledNamePart(); explicit DemangledNamePart(_STD_STRING base); + explicit DemangledNamePart(const char* base); + explicit DemangledNamePart(std::string_view base); DemangledNamePart(_STD_STRING base, std::shared_ptr baseTypeSuffix); DemangledNamePart(_STD_STRING base, _STD_VECTOR templateArgs, bool spaceAfterComma = false); @@ -75,8 +79,10 @@ class DemangledNamePart void SetBase(_STD_STRING base) { m_base = std::move(base); } void AppendBase(const _STD_STRING& suffix) { m_base += suffix; } bool HasTemplateArguments() const { return m_hasTemplateArgs || !m_templateArgs.empty(); } + const _STD_VECTOR& GetTemplateArguments() const { return m_templateArgs; } _STD_VECTOR& GetMutableTemplateArguments() { return m_templateArgs; } void SetTemplateArguments(_STD_VECTOR args, bool spaceAfterComma = false); + void ClearTemplateArguments(); void AppendString(_STD_STRING& out, BN::Platform* platform) const; _STD_STRING GetString(BN::Platform* platform = nullptr) const; @@ -158,9 +164,14 @@ class DemangledTypeNode bool HasTemplateArguments() const; uint8_t GetPointerSuffixBits() const { return m_pointerSuffixBits; } BNNamedTypeReferenceClass GetNTRClass() const; + bool GetIntegerTypeInfo(size_t& width, WidthKind& widthKind, bool& isSigned, std::string_view& altName) const; + bool GetWideCharTypeInfo(size_t& width, std::string_view& altName) const; + bool GetPointerChildType(const DemangledTypeNode*& childType, BNReferenceType& referenceType) const; void SetParenthesizedMemberPointer(bool parenthesized); StringList RenderTypeNameSegments(BN::Platform* platform = nullptr) const; bool IsStructurallyEqual(const DemangledTypeNode& other) const; + bool MutateChildTypes(const std::function& mutator); + bool MutateQualifiedNames(const std::function& mutator); void SetName(DemangledQualifiedName name); void SetConst(bool c) { m_const = c; } diff --git a/demangler/gnu3/CMakeLists.txt b/demangler/gnu3/CMakeLists.txt index 02abad58a..9c1ea197c 100644 --- a/demangler/gnu3/CMakeLists.txt +++ b/demangler/gnu3/CMakeLists.txt @@ -6,6 +6,8 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.cpp *.c *.h + ../demangled_template_simplifier.cpp + ../demangled_template_simplifier.h ../demangled_type_node.cpp ../demangled_type_node.h) diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index c61f6a3a0..b85c3c2c5 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -16,6 +16,7 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_gnu3.h" +#include "demangler/demangled_template_simplifier.h" #include #include #include @@ -501,7 +502,7 @@ bool DemangleGNU3::LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNo DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(NodeRef type) { if (type) - m_templateSubstitute.push_back(std::move(type)); + m_templateSubstitute.push_back(type); return type; } @@ -522,12 +523,18 @@ DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(DemangledTypeNode&& type) } +void DemangleGNU3::PushEmptyTemplateParamSubstitution() +{ + m_templateSubstitute.push_back(NodeRef::EmptyTemplatePack()); +} + + #ifdef GNUDEMANGLE_DEBUG const DemangledTypeNode& DemangleGNU3::GetTemplateType(size_t ref) { if (ref >= m_templateSubstitute.size()) throw DemangleException(); - if (!m_templateSubstitute[ref]) + if (m_templateSubstitute[ref].emptyTemplatePack || !m_templateSubstitute[ref]) throw DemangleException(); return *m_templateSubstitute[ref]; } @@ -537,7 +544,7 @@ const DemangledTypeNode& DemangleGNU3::GetTemplateType(size_t ref) DemangleGNU3::NodeRef DemangleGNU3::PushType(NodeRef type) { if (type) - m_substitute.push_back(std::move(type)); + m_substitute.push_back(type); return type; } @@ -558,11 +565,17 @@ DemangleGNU3::NodeRef DemangleGNU3::PushType(DemangledTypeNode&& type) } +void DemangleGNU3::PushEmptyTypeSubstitution() +{ + m_substitute.push_back(NodeRef::EmptyTemplatePack()); +} + + DemangleGNU3::NodeRef DemangleGNU3::GetTypeRef(size_t ref) { if (ref >= m_substitute.size()) throw DemangleException(); - if (!m_substitute[ref]) + if (m_substitute[ref].emptyTemplatePack || !m_substitute[ref]) throw DemangleException(); return m_substitute[ref]; } @@ -580,13 +593,19 @@ void DemangleGNU3::PrintTables() LogDebug("Substitution Table\n"); for (int i = 0; (size_t)i < m_substitute.size(); i++) { - LogDebug("[%d] %s\n", i-1, GetType(i).GetString().c_str()); + if (m_substitute[i].emptyTemplatePack) + LogDebug("[%d] \n", i - 1); + else + LogDebug("[%d] %s\n", i - 1, GetType(i).GetString().c_str()); } LogDebug("Template Table\n"); for (int i = 0; (size_t)i < m_templateSubstitute.size(); i++) { - LogDebug("[%d] %s\n", i-1, GetTemplateType(i).GetString().c_str()); + if (m_templateSubstitute[i].emptyTemplatePack) + LogDebug("[%d] \n", i - 1); + else + LogDebug("[%d] %s\n", i - 1, GetTemplateType(i).GetString().c_str()); } } #endif @@ -687,7 +706,7 @@ void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode&, const ParamLis } -DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef) +DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef) { indent(); MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); @@ -724,11 +743,14 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef if (number < m_templateSubstitute.size()) { - if (!m_templateSubstitute[number]) + const auto& entry = m_templateSubstitute[number]; + if (entry.emptyTemplatePack) + return entry; + if (!entry) throw DemangleException(); if (outTypeRef) - *outTypeRef = m_templateSubstitute[number]; - return *m_templateSubstitute[number]; + *outTypeRef = entry; + return entry; } // If forward template references are permitted (e.g. inside a cv conversion @@ -740,7 +762,7 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef m_pendingForwardRefs.push_back({number, typeRef}); if (outTypeRef) *outTypeRef = typeRef; - return *typeRef; + return typeRef; } if (m_parsingLambdaParams && number >= m_lambdaTemplateParamBase) @@ -748,13 +770,22 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef auto typeRef = DemangledTypeNode::CreateShared(CreateUnknownType("auto")); if (outTypeRef) *outTypeRef = typeRef; - return *typeRef; + return typeRef; } throw DemangleException(); } +DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef) +{ + NodeRef entry = DemangleTemplateSubstitutionEntry(outTypeRef); + if (entry.emptyTemplatePack || !entry) + throw DemangleException(); + return *entry; +} + + DemangledTypeNode DemangleGNU3::DemangleType() { NestingGuard nestingGuard(*this); @@ -923,7 +954,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() extName.SetTemplateArguments(std::move(targs), true); } DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(inner)); + NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); auto extType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, DemangledQualifiedName{std::move(extName)}); NodeRef extNameRef = DemangledTypeNode::CreateShared(std::move(extType)); type = DemangledTypeNode::PostfixType(innerRef, " ", extNameRef); @@ -971,9 +1002,9 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'M': // TODO: Make into pointer to function member { DemangledTypeNode memberName = DemangleType(); - NodeRef memberNameRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(memberName)); + NodeRef memberNameRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(memberName))); DemangledTypeNode member = DemangleType(); - NodeRef memberRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(member)); + NodeRef memberRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(member))); type = DemangledTypeNode::MemberPointerType(memberRef, CopyQualifiedName(*memberNameRef), cnst, vltl); type.SetParenthesizedMemberPointer(true); substitute = true; @@ -987,6 +1018,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'e': type = DemangledTypeNode::FloatType(16, "decimal128"); break; case 'f': type = DemangledTypeNode::FloatType(4, "decimal32"); break; case 'h': type = DemangledTypeNode::FloatType(2); break; + case 'u': type = DemangledTypeNode::IntegerType(1, false, "char8_t"); break; case 'i': type = DemangledTypeNode::WideCharType(4, "char32_t"); break; case 's': type = DemangledTypeNode::WideCharType(2, "char16_t"); break; case 'a': type = CreateUnknownType("auto"); break; //auto type @@ -1000,7 +1032,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'p': { DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(inner)); + NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); type = DemangledTypeNode::PostfixType(innerRef, "..."); break; } @@ -1061,7 +1093,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() throw DemangleException(); DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(inner)); + NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); type = DemangledTypeNode::PostfixType(innerRef, std::move(dimension)); } substitute = true; @@ -1163,7 +1195,25 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) } dedent(); - auto ref = GetTypeRef(number); + NodeRef ref; + if (number < m_substitute.size()) + { + ref = GetTypeRef(number); + } + else if (number == m_substitute.size() && m_reader.Peek() == 'I' && !m_substitute.empty()) + { + // GNU/libstdc++ pack expansions can elide an empty template-id that + // LLVM still effectively treats as occupying this substitution slot. + // When the next production immediately replaces template arguments, + // use the previous template-id as the prefix. + if (m_substitute.back().emptyTemplatePack || !m_substitute.back()) + throw DemangleException(); + ref = m_substitute.back(); + } + else + { + ref = GetTypeRef(number); + } const DemangledTypeNode& resolved = *ref; string lastName = LastTypeNameSegmentBase(resolved); if (!lastName.empty()) @@ -1565,7 +1615,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() m_pendingForwardRefs.clear(); m_permitForwardTemplateRefs = true; DemangledTypeNode cvType = DemangleType(); - NodeRef cvTypeRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(cvType)); + NodeRef cvTypeRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(cvType))); m_permitForwardTemplateRefs = savedPermit; outType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, DemangledQualifiedName{DemangledNamePart("operator ", std::move(cvTypeRef))}); @@ -1815,7 +1865,9 @@ string DemangleGNU3::DemangleExpression() return "sizeof...(" + NameSegmentWithTemplateArgs("", std::move(args)).GetString() + ")..."; } case hash('s','p'): + { return "(" + DemangleExpression() + ")..."; + } case hash('t','w'): return GetOperator(elm1, elm2) + DemangleExpression(); case hash('t','r'): @@ -2144,6 +2196,48 @@ string DemangleGNU3::DemangleExpression() } +bool DemangleGNU3::TryDemangleTemplateParamPackExpansion(DemangledTypeNode& type, bool& emptyPack) +{ + if (m_reader.Length() < 3 || m_reader.PeekString(3) != "DpT") + return false; + + m_reader.Consume(3); // "DpT" + NodeRef entry = DemangleTemplateSubstitutionEntry(); + if (entry.emptyTemplatePack) + { + emptyPack = true; + PushEmptyTypeSubstitution(); + return true; + } + if (!entry) + throw DemangleException(); + + type = *entry; + PushType(type); + return true; +} + + +bool DemangleGNU3::TryDemangleTemplateParamExpressionPackExpansion(string& expr, bool& emptyPack) +{ + if (m_reader.Length() < 3 || m_reader.PeekString(3) != "spT") + return false; + + m_reader.Consume(3); // "spT" + NodeRef entry = DemangleTemplateSubstitutionEntry(); + if (entry.emptyTemplatePack) + { + emptyPack = true; + return true; + } + if (!entry) + throw DemangleException(); + + expr = entry->GetString(); + return true; +} + + bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) { DemangledTypeNode tmp; @@ -2163,8 +2257,15 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) break; case 'X': { - DemangledTypeNode exprNode = CreateUnknownType(DemangleExpression()); - args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); + string expr; + bool emptyPack = false; + if (!TryDemangleTemplateParamExpressionPackExpansion(expr, emptyPack)) + expr = DemangleExpression(); + if (!emptyPack) + { + DemangledTypeNode exprNode = CreateUnknownType(expr); + args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); + } if (m_reader.Read() != 'E') throw DemangleException(); if (hadNonTypeArg) *hadNonTypeArg = true; @@ -2176,7 +2277,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) size_t prevTemplateSize = m_templateSubstitute.size(); DemangleTemplateArgs(args, hadNonTypeArg); if (m_topLevel && m_templateSubstitute.size() == prevTemplateSize) - PushTemplateType(CreateUnknownType("auto")); + PushEmptyTemplateParamSubstitution(); break; } case 'T': @@ -2201,8 +2302,12 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) m_reader.UnRead(); topLevel = m_topLevel; m_topLevel = false; - tmp = DemangleType(); + bool emptyPack = false; + if (!TryDemangleTemplateParamPackExpansion(tmp, emptyPack)) + tmp = DemangleType(); m_topLevel = topLevel; + if (emptyPack) + return true; tmpRef = DemangledTypeNode::CreateShared(std::move(tmp)); args.push_back({"", tmpRef}); tmpValid = true; @@ -2558,7 +2663,7 @@ DemangledTypeNode DemangleGNU3::DemangleName() } -DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) +DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplifyTemplates) { NestingGuard nestingGuard(*this); indent(); @@ -2613,7 +2718,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform.GetPtr())}); @@ -2665,7 +2770,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) if (!isVectorABI) { // Guard variable (original behavior) - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); varName.push_back("guard_variable_for_" + t.GetTypeAndName(name, m_platform.GetPtr())); type = DemangledTypeNode::IntegerType(1, false); if (m_reader.Length() == 0) @@ -2854,7 +2959,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) consumeCallOffset(); // return-value adjustment oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); @@ -2882,7 +2987,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); @@ -2891,7 +2996,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) { oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform.GetPtr())}); @@ -2925,7 +3030,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); @@ -2937,7 +3042,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) { oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform.GetPtr())}); @@ -2981,6 +3086,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) ExtendTypeName(type, "[abi:" + abiTag + "]"); } const bool nameRequiresReturnType = m_isParameter || LastTypeNameSegmentHasTemplateArguments(type); + if (simplifyTemplates) + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); varName = type.RenderTypeNameSegments(m_platform.GetPtr()); if (m_isOperatorOverload || type.GetNameType() == ConstructorNameType || @@ -3067,6 +3174,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) type.SetVolatile(vltl); if (rstrct) type.SetPointerSuffixBits(1u << RestrictSuffix); + if (simplifyTemplates) + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); // PrintTables(); MyLogDebug("Done: %s%s%s\n", type.GetStringBeforeName(m_platform.GetPtr()).c_str(), JoinNameSegments(varName).c_str(), @@ -3129,7 +3238,8 @@ bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) namespace { static bool DemangleStringGNU3Segments( - Platform* platform, const string& name, Ref& outType, StringList& outVarName) + Platform* platform, const string& name, Ref& outType, StringList& outVarName, + bool simplifyTemplates = false) { // Handle _block_invoke[.N] and _block_invoke_N suffixes (Clang/Apple block invocations). // E.g. ____ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_Pm_block_invoke.110 @@ -3159,7 +3269,7 @@ namespace string normalized = "_" + base.substr(zPos); Ref baseType; StringList baseName; - if (DemangleStringGNU3Segments(platform, normalized, baseType, baseName)) + if (DemangleStringGNU3Segments(platform, normalized, baseType, baseName, simplifyTemplates)) { outVarName.clear(); outVarName.push_back("invocation_function_for_block_in_" + JoinNameSegments(baseName)); @@ -3179,7 +3289,7 @@ namespace string base = name.substr(0, name.size() - tlvInitSuffix.size()); Ref baseType; StringList baseName; - if (DemangleStringGNU3Segments(platform, base, baseType, baseName)) + if (DemangleStringGNU3Segments(platform, base, baseType, baseName, simplifyTemplates)) { outVarName = std::move(baseName); if (outVarName.size() > 0) @@ -3214,7 +3324,10 @@ namespace demangle.Reset(platform, encoding); try { - outType = demangle.DemangleSymbol(outVarName).Finalize(platform); + DemangledTypeNode type = demangle.DemangleSymbol(outVarName, simplifyTemplates); + if (simplifyTemplates) + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); + outType = type.Finalize(platform); if (outVarName.size() == 0) { @@ -3246,10 +3359,10 @@ namespace bool DemangleGNU3Static::DemangleStringGNU3(Platform* platform, const string& name, Ref& outType, - QualifiedName& outVarName) + QualifiedName& outVarName, bool simplifyTemplates) { StringList outVarNameSegments; - if (!DemangleStringGNU3Segments(platform, name, outType, outVarNameSegments)) + if (!DemangleStringGNU3Segments(platform, name, outType, outVarNameSegments, simplifyTemplates)) return false; outVarName = QualifiedName(outVarNameSegments); return true; @@ -3257,12 +3370,12 @@ bool DemangleGNU3Static::DemangleStringGNU3(Platform* platform, const string& na bool DemangleGNU3Static::DemangleStringGNU3(Architecture* arch, const string& name, Ref& outType, - QualifiedName& outVarName) + QualifiedName& outVarName, bool simplifyTemplates) { Ref platform; if (arch) platform = arch->GetStandalonePlatform(); - return DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName); + return DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName, simplifyTemplates); } @@ -3289,7 +3402,7 @@ class GNU3Demangler: public Demangler BinaryView* view) override #else virtual bool Demangle(Ref arch, const string& name, Ref& outType, QualifiedName& outVarName, - Ref view) override + Ref view, bool simplify) override #endif { if (view) @@ -3299,11 +3412,29 @@ class GNU3Demangler: public Demangler #ifdef BINARYNINJACORE_LIBRARY return DemangleGNU3Static::DemangleStringGNU3(platform, name, outType, outVarName); #else - return DemangleGNU3Static::DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName); + return DemangleGNU3Static::DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName, simplify); #endif } +#ifndef BINARYNINJACORE_LIBRARY + return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName, simplify); +#else return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName); +#endif + } + +#ifdef BINARYNINJACORE_LIBRARY + virtual bool DemangleWithOptions(Architecture* arch, const string& name, Ref& outType, + QualifiedName& outVarName, BinaryView* view, bool simplify) override + { + if (view) + { + auto platform = view->GetDefaultPlatform(); + if (platform) + return DemangleGNU3Static::DemangleStringGNU3(platform, name, outType, outVarName, simplify); + } + return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName, simplify); } +#endif }; diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 0e06baa1b..4b08465dc 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -15,6 +15,7 @@ #pragma once #include #include +#include // XXX: Compiled directly into the core for performance reasons // Will still work fine compiled independently, just at about a @@ -94,7 +95,28 @@ class DemangleGNU3Reader class DemangleGNU3 { using ParamList = _STD_VECTOR; - using NodeRef = DemangledTypeNode::NodeRef; + using TypeNodeRef = DemangledTypeNode::NodeRef; + struct NodeRef + { + TypeNodeRef type; + bool emptyTemplatePack = false; + + NodeRef() = default; + NodeRef(std::nullptr_t) {} + NodeRef(TypeNodeRef typeRef): type(std::move(typeRef)) {} + + static NodeRef EmptyTemplatePack() + { + NodeRef ref; + ref.emptyTemplatePack = true; + return ref; + } + + explicit operator bool() const { return type != nullptr; } + DemangledTypeNode& operator*() const { return *type; } + DemangledTypeNode* operator->() const { return type.get(); } + operator TypeNodeRef() const { return type; } + }; using NodeRefList = _STD_VECTOR; DemangleGNU3Reader m_reader; @@ -149,6 +171,9 @@ class DemangleGNU3 _STD_STRING DemangleNumberAsString(); _STD_STRING DemangleExpression(); _STD_STRING DemanglePrimaryExpression(); + NodeRef DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef = nullptr); + bool TryDemangleTemplateParamPackExpansion(DemangledTypeNode& type, bool& emptyPack); + bool TryDemangleTemplateParamExpressionPackExpansion(_STD_STRING& expr, bool& emptyPack); DemangledTypeNode DemangleName(); DemangledTypeNode DemangleLocalName(); @@ -164,9 +189,11 @@ class DemangleGNU3 NodeRef PushTemplateType(NodeRef type); NodeRef PushTemplateType(const DemangledTypeNode& type); NodeRef PushTemplateType(DemangledTypeNode&& type); + void PushEmptyTemplateParamSubstitution(); NodeRef PushType(NodeRef type); NodeRef PushType(const DemangledTypeNode& type); NodeRef PushType(DemangledTypeNode&& type); + void PushEmptyTypeSubstitution(); NodeRef GetTypeRef(size_t ref); const DemangledTypeNode& GetType(size_t ref); @@ -186,7 +213,7 @@ class DemangleGNU3 public: DemangleGNU3(BN::Platform* platform, const _STD_STRING& mangledName); void Reset(BN::Platform* platform, const _STD_STRING& mangledName); - DemangledTypeNode DemangleSymbol(StringList& varName); + DemangledTypeNode DemangleSymbol(StringList& varName, bool simplifyTemplates = false); }; @@ -196,6 +223,8 @@ class DemangleGNU3Static static bool IsGNU3MangledString(const _STD_STRING& name); static bool DemangleGlobalHeader(_STD_STRING& name, _STD_STRING& header); - static bool DemangleStringGNU3(BN::Platform* platform, const _STD_STRING& name, BN::Ref& outType, BN::QualifiedName& outVarName); - static bool DemangleStringGNU3(BN::Architecture* arch, const _STD_STRING& name, BN::Ref& outType, BN::QualifiedName& outVarName); + static bool DemangleStringGNU3(BN::Platform* platform, const _STD_STRING& name, BN::Ref& outType, + BN::QualifiedName& outVarName, bool simplifyTemplates = false); + static bool DemangleStringGNU3(BN::Architecture* arch, const _STD_STRING& name, BN::Ref& outType, + BN::QualifiedName& outVarName, bool simplifyTemplates = false); }; diff --git a/demangler/msvc/CMakeLists.txt b/demangler/msvc/CMakeLists.txt index f89aee513..8177ef1ea 100644 --- a/demangler/msvc/CMakeLists.txt +++ b/demangler/msvc/CMakeLists.txt @@ -7,7 +7,9 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.c *.h ../demangled_type_node.cpp - ../demangled_type_node.h) + ../demangled_type_node.h + ../demangled_template_simplifier.cpp + ../demangled_template_simplifier.h) if(DEMO) add_library(${PROJECT_NAME} STATIC ${SOURCES}) diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index 55c7b41b8..b3f7a08de 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -16,6 +16,7 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_msvc.h" +#include "demangler/demangled_template_simplifier.h" #ifdef BINARYNINJACORE_LIBRARY #include "unicode.h" #endif @@ -2610,7 +2611,7 @@ Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) return finishContext(); } -std::pair, QualifiedName> Demangle::Finalize(BinaryView* view) +std::pair, QualifiedName> Demangle::Finalize(BinaryView* view, bool simplifyTemplates) { DemangleContext context = DemangleSymbol(); if (m_reader.Length() != 0) @@ -2646,12 +2647,18 @@ std::pair, QualifiedName> Demangle::Finalize(BinaryView* view) if (!platform) platform = arch->GetStandalonePlatform(); + if (simplifyTemplates) + { + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(context.type); + DemangledTemplateSimplifier::SimplifyNameSegmentsInPlace(context.name); + } + return {context.type.Finalize(platform.GetPtr()), QualifiedName(FinalizeNameList(context.name))}; } -std::pair, QualifiedName> Demangle::Finalize() +std::pair, QualifiedName> Demangle::Finalize(bool simplifyTemplates) { - return Finalize(m_view.GetPtr()); + return Finalize(m_view.GetPtr(), simplifyTemplates); } template @@ -2683,27 +2690,45 @@ static bool DemangleMSImpl(const _STD_STRING& mangledName, Ref& outType, Q bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName, const Ref& view) +{ + return DemangleMS(arch, mangledName, outType, outVarName, view, false); +} + +bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, const Ref& view, bool simplifyTemplates) { if (view) { return DemangleMSImpl(mangledName, outType, outVarName, [&]() { Demangle demangle(arch, mangledName); - return demangle.Finalize(view.GetPtr()); + return demangle.Finalize(view.GetPtr(), simplifyTemplates); }); } - return DemangleMS(arch, mangledName, outType, outVarName); + return DemangleMS(arch, mangledName, outType, outVarName, simplifyTemplates); } bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName, BinaryView* view) +{ + return DemangleMS(arch, mangledName, outType, outVarName, view, false); +} + +bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, BinaryView* view, bool simplifyTemplates) { if (view) - return DemangleMS(arch, mangledName, outType, outVarName, Ref(view)); - return DemangleMS(arch, mangledName, outType, outVarName); + return DemangleMS(arch, mangledName, outType, outVarName, Ref(view), simplifyTemplates); + return DemangleMS(arch, mangledName, outType, outVarName, simplifyTemplates); } bool Demangle::DemangleMS(Platform* platform, const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName) +{ + return DemangleMS(platform, mangledName, outType, outVarName, false); +} + +bool Demangle::DemangleMS(Platform* platform, const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, bool simplifyTemplates) { outType = nullptr; if (!platform) @@ -2711,38 +2736,56 @@ bool Demangle::DemangleMS(Platform* platform, const _STD_STRING& mangledName, Re return DemangleMSImpl(mangledName, outType, outVarName, [&]() { Demangle demangle(Ref(platform), mangledName); - return demangle.Finalize(); + return demangle.Finalize(simplifyTemplates); }); } bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName) +{ + return DemangleMS(arch, mangledName, outType, outVarName, false); +} + +bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, bool simplifyTemplates) { return DemangleMSImpl(mangledName, outType, outVarName, [&]() { thread_local Demangle demangle(arch, mangledName); demangle.Reset(arch, mangledName); - return demangle.Finalize(); + return demangle.Finalize(simplifyTemplates); }); } bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName, const Ref& view) +{ + return DemangleMS(mangledName, outType, outVarName, view, false); +} + +bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, const Ref& view, bool simplifyTemplates) { return DemangleMSImpl(mangledName, outType, outVarName, [&]() { // Can't use thread_local here — BinaryView overload needs platform/view state Demangle demangle(view, mangledName); - return demangle.Finalize(); + return demangle.Finalize(simplifyTemplates); }); } bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName, BinaryView* view) +{ + return DemangleMS(mangledName, outType, outVarName, view, false); +} + +bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, BinaryView* view, bool simplifyTemplates) { outType = nullptr; if (!view) return false; - return DemangleMS(mangledName, outType, outVarName, Ref(view)); + return DemangleMS(mangledName, outType, outVarName, Ref(view), simplifyTemplates); } @@ -2764,13 +2807,29 @@ class MSDemangler: public Demangler BinaryView* view) override #else virtual bool Demangle(Ref arch, const _STD_STRING& name, Ref& outType, QualifiedName& outVarName, - Ref view) override + Ref view, bool simplify) override #endif { +#ifdef BINARYNINJACORE_LIBRARY if (view) return Demangle::DemangleMS(arch, name, outType, outVarName, view); return Demangle::DemangleMS(arch, name, outType, outVarName); +#else + if (view) + return Demangle::DemangleMS(arch, name, outType, outVarName, view, simplify); + return Demangle::DemangleMS(arch, name, outType, outVarName, simplify); +#endif } + +#ifdef BINARYNINJACORE_LIBRARY + bool DemangleWithOptions(Architecture* arch, const _STD_STRING& name, Ref& outType, + QualifiedName& outVarName, BinaryView* view, bool simplify) override + { + if (view) + return Demangle::DemangleMS(arch, name, outType, outVarName, view, simplify); + return Demangle::DemangleMS(arch, name, outType, outVarName, simplify); + } +#endif }; extern "C" diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h index bb5e94ecb..91882e1aa 100644 --- a/demangler/msvc/demangle_msvc.h +++ b/demangler/msvc/demangle_msvc.h @@ -339,7 +339,7 @@ class Demangle DemangledTypeNode DemangleTypeInfoName(NameList& symbolName); DemangleContext DemangleDynamicInitFini(bool isDtor, BackrefList& backrefList); DemangleContext DemangleSymbol(BackrefList& backrefList); - std::pair, BN::QualifiedName> Finalize(BN::BinaryView* view); + std::pair, BN::QualifiedName> Finalize(BN::BinaryView* view, bool simplifyTemplates = false); public: Demangle(BN::Architecture* arch, _STD_STRING mangledName); @@ -351,20 +351,34 @@ class Demangle Demangle& operator=(Demangle&&) = delete; void Reset(BN::Architecture* arch, const _STD_STRING& mangledName); DemangleContext DemangleSymbol(); - std::pair, BN::QualifiedName> Finalize(); + std::pair, BN::QualifiedName> Finalize(bool simplifyTemplates = false); // Be careful not to accidentally implicitly cast a BinaryView* to a bool static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, BN::QualifiedName& outVarName, const BN::Ref& view); + static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, + BN::QualifiedName& outVarName, const BN::Ref& view, + bool simplifyTemplates); static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, BN::QualifiedName& outVarName, BN::BinaryView* view); + static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, + BN::QualifiedName& outVarName, BN::BinaryView* view, bool simplifyTemplates); static bool DemangleMS(BN::Platform* platform, const _STD_STRING& mangledName, BN::Ref& outType, BN::QualifiedName& outVarName); + static bool DemangleMS(BN::Platform* platform, const _STD_STRING& mangledName, BN::Ref& outType, + BN::QualifiedName& outVarName, bool simplifyTemplates); static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, BN::QualifiedName& outVarName); + static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, + BN::QualifiedName& outVarName, bool simplifyTemplates); static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, BN::QualifiedName& outVarName, const BN::Ref& view); + static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, + BN::QualifiedName& outVarName, const BN::Ref& view, + bool simplifyTemplates); static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, BN::QualifiedName& outVarName, BN::BinaryView* view); + static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, + BN::QualifiedName& outVarName, BN::BinaryView* view, bool simplifyTemplates); }; diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 6c1d78a8a..1187b24ff 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -24,7 +24,6 @@ use binaryninja::{ platform::Platform, rc::*, symbol::SymbolType, - template_simplifier::simplify_str_to_fqn, types::{FunctionParameter, Type}, variable::NamedVariableWithType, }; @@ -38,6 +37,37 @@ use std::{cmp::Ordering, collections::HashMap, hash::Hash}; pub(crate) type TypeUID = usize; +fn qualified_name_segment_count(name: &str) -> usize { + let mut count = 1; + let mut angle_depth = 0; + let mut paren_depth = 0; + let mut bracket_depth = 0; + let bytes = name.as_bytes(); + let mut index = 0; + while index < bytes.len() { + match bytes[index] { + b'<' => angle_depth += 1, + b'>' if angle_depth > 0 => angle_depth -= 1, + b'(' => paren_depth += 1, + b')' if paren_depth > 0 => paren_depth -= 1, + b'[' => bracket_depth += 1, + b']' if bracket_depth > 0 => bracket_depth -= 1, + b':' if index + 1 < bytes.len() + && bytes[index + 1] == b':' + && angle_depth == 0 + && paren_depth == 0 + && bracket_depth == 0 => + { + count += 1; + index += 1; + } + _ => {} + } + index += 1; + } + count +} + ///////////////////////// // FunctionInfoBuilder @@ -733,10 +763,8 @@ impl DebugInfoBuilder { let symbol_full_name = symbol.full_name(); // If our name has fewer namespaces than the existing name, assume we lost the namespace info - if simplify_str_to_fqn(func_full_name, true).items.len() - < simplify_str_to_fqn(symbol_full_name.clone(), true) - .items - .len() + if qualified_name_segment_count(func_full_name) + < qualified_name_segment_count(&symbol_full_name.to_string_lossy()) { func.full_name = Some(symbol_full_name.to_string_lossy().to_string()); } diff --git a/plugins/dwarf/dwarf_import/src/functions.rs b/plugins/dwarf/dwarf_import/src/functions.rs index 6755f847f..48e68ed6e 100644 --- a/plugins/dwarf/dwarf_import/src/functions.rs +++ b/plugins/dwarf/dwarf_import/src/functions.rs @@ -18,7 +18,6 @@ use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; use crate::types::get_type; use crate::{helpers::*, ReaderType}; -use binaryninja::template_simplifier::simplify_str_to_str; use cpp_demangle::DemangleOptions; use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit}; use regex::Regex; @@ -134,8 +133,7 @@ pub(crate) fn parse_function_entry( if let Ok(sym) = cpp_demangle::Symbol::new(possibly_mangled_name) { if let Ok(demangled) = sym.demangle(demangle_options) { let cleaned = abi_regex.replace_all(&demangled, ""); - let simplified = simplify_str_to_str(&cleaned); - full_name = Some(simplified.to_string_lossy().to_string()); + full_name = Some(cleaned.to_string()); } } } diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs index 6808e6062..8d9068b92 100644 --- a/plugins/dwarf/dwarf_import/src/lib.rs +++ b/plugins/dwarf/dwarf_import/src/lib.rs @@ -33,7 +33,6 @@ use binaryninja::{ binary_view::BinaryView, debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}, settings::Settings, - template_simplifier::simplify_str_to_str, }; use dwarfreader::create_section_reader_object; @@ -247,15 +246,11 @@ fn recover_names_internal( } debug_info_builder_context.set_name( get_uid(dwarf, &unit, entry), - simplify_str_to_str( - namespace_qualifiers - .iter() - .map(|(_, namespace)| namespace.to_owned()) - .collect::>() - .join("::"), - ) - .to_string_lossy() - .to_string(), + namespace_qualifiers + .iter() + .map(|(_, namespace)| namespace.to_owned()) + .collect::>() + .join("::"), ); } constants::DW_TAG_typedef @@ -264,16 +259,12 @@ fn recover_names_internal( if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) { debug_info_builder_context.set_name( get_uid(dwarf, &unit, entry), - simplify_str_to_str( - namespace_qualifiers - .iter() - .chain(vec![&(-1, name)].into_iter()) - .map(|(_, namespace)| namespace.to_owned()) - .collect::>() - .join("::"), - ) - .to_string_lossy() - .to_string(), + namespace_qualifiers + .iter() + .chain(vec![&(-1, name)].into_iter()) + .map(|(_, namespace)| namespace.to_owned()) + .collect::>() + .join("::"), ); } } diff --git a/plugins/workflow_swift/src/demangler/mod.rs b/plugins/workflow_swift/src/demangler/mod.rs index cee0fcc7b..75ddea326 100644 --- a/plugins/workflow_swift/src/demangler/mod.rs +++ b/plugins/workflow_swift/src/demangler/mod.rs @@ -35,6 +35,7 @@ impl CustomDemangler for SwiftDemangler { arch: &CoreArchitecture, name: &str, view: Option>, + _simplify: bool, ) -> Option<(QualifiedName, Option>)> { let ctx = swift_demangler::Context::new(); let symbol = swift_demangler::Symbol::parse(&ctx, name)?; diff --git a/python/demangle.py b/python/demangle.py index 76bc60462..c6cb0fcd9 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -91,11 +91,18 @@ def demangle_generic( if not core.BNDemangleGeneric(arch.handle, mangled_name, out_type, out_var_name, view_handle, simplify): return None, [mangled_name] + try: + result_var_name = types.QualifiedName._from_core_struct(out_var_name) + except UnicodeDecodeError: + core.BNFreeQualifiedName(out_var_name) + if out_type: + core.BNFreeType(out_type) + return None, [mangled_name] + + core.BNFreeQualifiedName(out_var_name) result_type = None if out_type: result_type = types.Type.create(handle=out_type) - result_var_name = types.QualifiedName._from_core_struct(out_var_name) - core.BNFreeQualifiedName(out_var_name) return result_type, result_var_name.name @@ -226,62 +233,6 @@ def demangle_gnu3(arch, mangled_name: str, options: Optional[Union[bool, binaryv return (None, mangled_name) -def simplify_name_to_string(input_name: Union[str, types.QualifiedName]): - """ - ``simplify_name_to_string`` simplifies a templated C++ name with default arguments and returns a string - - :param input_name: String or qualified name to be simplified - :type input_name: Union[str, QualifiedName] - :return: simplified name (or original name if simplifier fails/cannot simplify) - :rtype: str - :Example: - - >>> demangle.simplify_name_to_string("std::__cxx11::basic_string, std::allocator >") - 'std::string' - >>> - """ - result = None - if isinstance(input_name, str): - result = core.BNRustSimplifyStrToStr(input_name) - elif isinstance(input_name, types.QualifiedName): - result = core.BNRustSimplifyStrToStr(str(input_name)) - else: - raise TypeError("Parameter must be of type `str` or `types.QualifiedName`") - return result - - -def simplify_name_to_qualified_name(input_name: Union[str, types.QualifiedName], simplify: bool = True): - """ - ``simplify_name_to_qualified_name`` simplifies a templated C++ name with default arguments and returns a qualified name. This can also tokenize a string to a qualified name with/without simplifying it - - :param input_name: String or qualified name to be simplified - :type input_name: Union[str, QualifiedName] - :param bool simplify: (optional) Whether to simplify input string (no effect if given a qualified name; will always simplify) - :return: simplified name (or one-element array containing the input if simplifier fails/cannot simplify) - :rtype: QualifiedName - :Example: - - >>> demangle.simplify_name_to_qualified_name(QualifiedName(["std", "__cxx11", "basic_string, std::allocator >"]), True) - 'std::wstring' - >>> - """ - name = None - if isinstance(input_name, str): - name = core.BNRustSimplifyStrToFQN(input_name, simplify) - assert name is not None, "core.BNRustSimplifyStrToFQN returned None" - elif isinstance(input_name, types.QualifiedName): - name = core.BNRustSimplifyStrToFQN(str(input_name), True) - assert name is not None, "core.BNRustSimplifyStrToFQN returned None" - else: - raise TypeError("Parameter must be of type `str` or `types.QualifiedName`") - - result = types.QualifiedName._from_core_struct(name) - core.BNFreeQualifiedName(name) - if len(result) == 0: - return None - return result - - class _DemanglerMetaclass(type): def __iter__(self): binaryninja._init_plugins() @@ -392,14 +343,14 @@ def _is_mangled_string(self, ctxt, name): log_error_for_exception("Unhandled Python exception in Demangler._is_mangled_string") return False - def _demangle(self, ctxt, arch, name, out_type, out_var_name, view): + def _demangle(self, ctxt, arch, name, out_type, out_var_name, view, simplify): try: api_arch = CoreArchitecture._from_cache(arch) api_view = None if view is not None: api_view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) - result = self.demangle(api_arch, core.pyNativeStr(name), api_view) + result = self.demangle(api_arch, core.pyNativeStr(name), api_view, simplify) if result is None: return False type, var_name = result @@ -443,7 +394,8 @@ def demangle( self, arch: Architecture, name: str, - view: Optional['binaryview.BinaryView'] = None + view: Optional['binaryview.BinaryView'] = None, + simplify: bool = False ) -> Optional[Tuple['types.Type', 'types.QualifiedName']]: """ Demangle a raw name into a Type and QualifiedName. @@ -468,6 +420,7 @@ def demangle( :param arch: Architecture for context in which the name exists, eg for pointer sizes :param name: Raw mangled name :param view: (Optional) BinaryView context in which the name exists, eg for type lookup + :param simplify: Whether to simplify templates while demangling :return: Tuple of (Type, Name) if successful, None if not. Type may be None if only a demangled name can be recovered from the raw name. """ @@ -479,7 +432,8 @@ class CoreDemangler(Demangler): def is_mangled_string(self, name: str) -> bool: return core.BNIsDemanglerMangledName(self.handle, name) - def demangle(self, arch: Architecture, name: str, view: Optional['binaryview.BinaryView'] = None) -> Optional[Tuple[Optional['types.Type'], 'types.QualifiedName']]: + def demangle(self, arch: Architecture, name: str, view: Optional['binaryview.BinaryView'] = None, + simplify: bool = False) -> Optional[Tuple[Optional['types.Type'], 'types.QualifiedName']]: out_type = ctypes.POINTER(core.BNType)() out_var_name = core.BNQualifiedName() @@ -487,7 +441,8 @@ def demangle(self, arch: Architecture, name: str, view: Optional['binaryview.Bin if view is not None: view_handle = view.handle - if not core.BNDemanglerDemangle(self.handle, arch.handle, name, out_type, out_var_name, view_handle): + if not core.BNDemanglerDemangleWithOptions( + self.handle, arch.handle, name, out_type, out_var_name, view_handle, simplify): return None result_type = None diff --git a/rust/examples/demangler.rs b/rust/examples/demangler.rs index edefaec19..4e686596f 100644 --- a/rust/examples/demangler.rs +++ b/rust/examples/demangler.rs @@ -17,6 +17,7 @@ impl CustomDemangler for TestDemangler { _arch: &CoreArchitecture, name: &str, _view: Option>, + _simplify: bool, ) -> Option<(QualifiedName, Option>)> { match name { "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index d6aad25ce..a6802a531 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -232,6 +232,16 @@ impl Demangler { arch: &CoreArchitecture, name: &str, view: Option<&BinaryView>, + ) -> Option<(QualifiedName, Option>)> { + self.demangle_with_options(arch, name, view, false) + } + + pub fn demangle_with_options( + &self, + arch: &CoreArchitecture, + name: &str, + view: Option<&BinaryView>, + simplify: bool, ) -> Option<(QualifiedName, Option>)> { let name_bytes = name.to_cstr(); @@ -244,13 +254,14 @@ impl Demangler { }; let res = unsafe { - BNDemanglerDemangle( + BNDemanglerDemangleWithOptions( self.handle, arch.handle, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr, + simplify, ) }; @@ -301,6 +312,7 @@ impl Demangler { out_type: *mut *mut BNType, out_var_name: *mut BNQualifiedName, view: *mut BNBinaryView, + simplify: bool, ) -> bool where C: CustomDemangler, @@ -316,7 +328,7 @@ impl Demangler { true => None, }; - match cmd.demangle(&arch, &name, view) { + match cmd.demangle(&arch, &name, view, simplify) { Some((name, ty)) => { // NOTE: Leaked to the caller, who must pick the ref up. *out_type = match ty { @@ -393,5 +405,6 @@ pub trait CustomDemangler: 'static + Sync { arch: &CoreArchitecture, name: &str, view: Option>, + simplify: bool, ) -> Option<(QualifiedName, Option>)>; } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 854bbbcb1..e41ad9273 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -82,7 +82,6 @@ pub mod string; pub mod string_detection; pub mod symbol; pub mod tags; -pub mod template_simplifier; pub mod tracing; pub mod types; pub mod update; diff --git a/rust/src/template_simplifier.rs b/rust/src/template_simplifier.rs deleted file mode 100644 index 81f8deb40..000000000 --- a/rust/src/template_simplifier.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{ - string::{BnString, IntoCStr}, - types::QualifiedName, -}; -use binaryninjacore_sys::{BNRustSimplifyStrToFQN, BNRustSimplifyStrToStr}; - -pub fn simplify_str_to_str(input: S) -> BnString { - let name = input.to_cstr(); - unsafe { BnString::from_raw(BNRustSimplifyStrToStr(name.as_ptr())) } -} - -pub fn simplify_str_to_fqn(input: S, simplify: bool) -> QualifiedName { - let name = input.to_cstr(); - unsafe { QualifiedName::from_owned_raw(BNRustSimplifyStrToFQN(name.as_ptr(), simplify)) } -} diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index 3c1d02160..faca775c8 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -10,7 +10,7 @@ use binaryninja::types::{QualifiedName, Type}; #[test] fn test_demangler_simple() { let _session = Session::new().expect("Failed to initialize session"); - let placeholder_arch = CoreArchitecture::by_name("x86").expect("x86 exists"); + let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86_64 exists"); // Example LLVM-style mangled name let llvm_mangled = "_Z3fooi"; // "foo(int)" in LLVM mangling let llvm_demangled = demangle_llvm(llvm_mangled, true).unwrap(); @@ -24,7 +24,7 @@ fn test_demangler_simple() { // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( gnu_demangled_type.unwrap().to_string(), - "int32_t(int32_t)".to_string() + "int64_t(int32_t)".to_string() ); // Example MSVC-style mangled name @@ -54,9 +54,17 @@ fn test_custom_demangler() { _arch: &CoreArchitecture, name: &str, _view: Option>, + simplify: bool, ) -> Option<(QualifiedName, Option>)> { match name { - "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), + "test_name" => Some(( + QualifiedName::from(vec![if simplify { + "test_name_simplified" + } else { + "test_name" + }]), + Some(Type::bool()), + )), "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), _ => None, } @@ -70,6 +78,14 @@ fn test_custom_demangler() { let demangled = demangle_generic(&placeholder_arch, "test_name", None, true).unwrap(); assert_eq!( demangled, + ( + QualifiedName::from(vec!["test_name_simplified"]), + Some(Type::bool()) + ) + ); + let unsimplified = demangle_generic(&placeholder_arch, "test_name", None, false).unwrap(); + assert_eq!( + unsimplified, (QualifiedName::from(vec!["test_name"]), Some(Type::bool())) ); let demangled2 = demangle_generic(&placeholder_arch, "test_name2", None, true).unwrap(); From 60e258818d76966bb20646613530fde80a1a8f65 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Sun, 7 Jun 2026 09:14:38 -0400 Subject: [PATCH 03/14] Minor DemangledTypeNode cleanup --- demangler/demangled_type_node.cpp | 100 ++++++++++-------------------- demangler/demangled_type_node.h | 8 +-- 2 files changed, 37 insertions(+), 71 deletions(-) diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index ce511a291..9a1e12d72 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -18,8 +18,6 @@ #endif #include "base/assertions.h" #include -#include -#include #ifdef BINARYNINJACORE_LIBRARY using namespace BinaryNinjaCore; @@ -109,14 +107,14 @@ namespace return empty; } - static size_t ResolveAddressWidth(Platform* platform) + static size_t ResolveAddressWidth(const Platform* platform) { if (platform) return platform->GetAddressSize(); return 8; } - static size_t ResolveDefaultIntegerWidth(Platform* platform) + static size_t ResolveDefaultIntegerWidth(const Platform* platform) { if (platform) { @@ -131,7 +129,7 @@ namespace return 4; } - static Ref ResolveCallingConvention(BNCallingConventionName cc, Platform* platform) + static Ref ResolveCallingConvention(BNCallingConventionName cc, const Platform* platform) { #ifndef BINARYNINJACORE_LIBRARY Ref platformArch; @@ -152,45 +150,40 @@ namespace case CdeclCallingConvention: if (platform) { - auto platformCC = platform->GetCdeclCallingConvention(); - if (platformCC) + if (auto platformCC = platform->GetCdeclCallingConvention()) return platformCC; } if (arch) { - auto archCC = arch->GetCdeclCallingConvention(); - if (archCC) + if (auto archCC = arch->GetCdeclCallingConvention()) return archCC; } return arch ? arch->GetCallingConventionByName("cdecl") : nullptr; case STDCallCallingConvention: if (platform) { - auto platformCC = platform->GetStdcallCallingConvention(); - if (platformCC) + if (auto platformCC = platform->GetStdcallCallingConvention()) return platformCC; } if (arch) { - auto archCC = arch->GetStdcallCallingConvention(); - if (archCC) + if (auto archCC = arch->GetStdcallCallingConvention()) return archCC; } return arch ? arch->GetCallingConventionByName("stdcall") : nullptr; case FastcallCallingConvention: if (platform) { - auto platformCC = platform->GetFastcallCallingConvention(); - if (platformCC) + if (auto platformCC = platform->GetFastcallCallingConvention()) return platformCC; } if (arch) { - auto archCC = arch->GetFastcallCallingConvention(); - if (archCC) + if (auto archCC = arch->GetFastcallCallingConvention()) return archCC; + return arch->GetCallingConventionByName("fastcall"); } - return arch ? arch->GetCallingConventionByName("fastcall") : nullptr; + return nullptr; case ThisCallCallingConvention: if (arch) return arch->GetCallingConventionByName("thiscall"); @@ -551,7 +544,7 @@ DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string suffix) DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string separator, NodeRef suffixType) { - DemangledTypeNode n = PostfixType(child, std::move(separator)); + DemangledTypeNode n = PostfixType(std::move(child), std::move(separator)); if (auto payload = std::get_if(&n.m_payload)) payload->suffixType = std::move(suffixType); return n; @@ -578,7 +571,7 @@ uint8_t DemangledTypeNode::PointerSuffixBit(BNPointerSuffix ps) } -size_t DemangledTypeNode::ResolveWidth(size_t width, WidthKind widthKind, Platform* platform) +size_t DemangledTypeNode::ResolveWidth(size_t width, WidthKind widthKind, const Platform* platform) { switch (widthKind) { @@ -693,7 +686,7 @@ bool DemangledTypeNode::AddQualifiersToPointerChild(bool cnst, bool vltl) if (!*childType) return true; - if ((*childType).use_count() > 1) + if (childType->use_count() > 1) *childType = CreateSharedCopy(**childType); if (cnst) (*childType)->SetConst(true); @@ -823,12 +816,8 @@ void DemangledTypeNode::SetCallingConventionName(BNCallingConventionName cc) bool DemangledTypeNode::HasTemplateArguments() const { - const auto* payload = std::get_if(&m_payload); - if (!payload) - return false; - for (const auto& segment: payload->name) - if (segment.HasTemplateArguments()) - return true; + if (const auto* payload = std::get_if(&m_payload)) + return std::ranges::any_of(payload->name, &DemangledNamePart::HasTemplateArguments); return false; } @@ -872,11 +861,11 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons }; if (std::get_if(&m_payload)) - return true; + return std::get_if(&other.m_payload); if (std::get_if(&m_payload)) - return true; + return std::get_if(&other.m_payload); if (std::get_if(&m_payload)) - return true; + return std::get_if(&other.m_payload); if (auto payload = std::get_if(&m_payload)) { auto otherPayload = std::get_if(&other.m_payload); @@ -944,17 +933,17 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons StringList DemangledTypeNode::RenderTypeNameSegments(Platform* platform) const { StringList result; - if (auto payload = std::get_if(&m_payload)) + if (std::get_if(&m_payload)) { result.push_back(GetString(platform)); return result; } - auto payload = std::get_if(&m_payload); - if (!payload) - return result; - result.reserve(payload->name.size()); - for (const auto& segment: payload->name) - result.push_back(segment.GetString(platform)); + if (auto payload = std::get_if(&m_payload)) + { + result.reserve(payload->name.size()); + for (const auto& segment: payload->name) + result.push_back(segment.GetString(platform)); + } return result; } @@ -995,11 +984,9 @@ void DemangledTypeNode::AppendPostfixType(string& out, Platform* platform) const void DemangledTypeNode::AppendModifiers(string& out) const { - if (m_const && m_volatile) - out += " const volatile"; - else if (m_const) + if (m_const) out += " const"; - else if (m_volatile) + if (m_volatile) out += " volatile"; } @@ -1237,12 +1224,6 @@ void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* p } -static string FormatArrayCount(uint64_t elements) -{ - return string(fmt::format("{:#x}", elements)); -} - - void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* parentType, Platform* platform) const { switch (GetPayloadClass()) @@ -1293,7 +1274,7 @@ void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* pa const auto& payload = std::get(m_payload); if (parentType && parentType->GetPayloadClass() == PointerTypeClass) out += ")"; - out += "[" + FormatArrayCount(payload.elements) + "]"; + out += fmt::bnformat("[{:#x}]", payload.elements); if (payload.childType) payload.childType->AppendAfterName(out, this, platform); break; @@ -1306,9 +1287,8 @@ void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* pa void DemangledTypeNode::AppendString(string& out, Platform* platform) const { - size_t beforeEnd = out.size(); AppendBeforeName(out, nullptr, platform); - beforeEnd = out.size(); // track where "before" ends + size_t beforeEnd = out.size(); // track where "before" ends string after; AppendAfterName(after, nullptr, platform); @@ -1328,12 +1308,6 @@ void DemangledTypeNode::AppendString(string& out, Platform* platform) const } -string DemangledTypeNode::GetString() const -{ - return GetString(nullptr); -} - - string DemangledTypeNode::GetString(Platform* platform) const { string out; @@ -1342,12 +1316,6 @@ string DemangledTypeNode::GetString(Platform* platform) const } -string DemangledTypeNode::GetTypeAndName(const StringList& name) const -{ - return GetTypeAndName(name, nullptr); -} - - string DemangledTypeNode::GetTypeAndName(const StringList& name, Platform* platform) const { const string before = GetStringBeforeName(platform); @@ -1484,14 +1452,14 @@ Ref DemangledTypeNode::Finalize(Platform* platform) const if (payload.implicitThisParameterType) { Ref thisType = payload.implicitThisParameterType->Finalize(platform); - finalParams.push_back({"this", thisType->WithConfidence(payload.implicitThisParameterType->GetValueConfidence()), - DefaultLocationSource, Variable()}); + finalParams.emplace_back("this", thisType->WithConfidence(payload.implicitThisParameterType->GetValueConfidence()), + DefaultLocationSource, Variable()); } for (auto& p : payload.params) { Ref pType = p.type ? p.type->Finalize(platform) : Ref(Type::VoidType()); uint8_t pTypeConfidence = p.type ? p.type->GetValueConfidence() : BN_FULL_CONFIDENCE; - finalParams.push_back({p.name, pType->WithConfidence(pTypeConfidence), DefaultLocationSource, Variable()}); + finalParams.emplace_back(p.name, pType->WithConfidence(pTypeConfidence), DefaultLocationSource, Variable()); } Confidence> callingConvention; if (payload.callingConventionName != NoCallingConvention) @@ -1513,7 +1481,7 @@ Ref DemangledTypeNode::Finalize(Platform* platform) const case NamedTypeReferenceClass: { - if (auto payload = std::get_if(&m_payload)) + if (std::get_if(&m_payload)) { QualifiedName name(RenderTypeNameSegments(platform)); TypeBuilder tb = TypeBuilder::NamedType( diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index 77f3c2b1a..b51a967d8 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -187,12 +187,10 @@ class DemangledTypeNode void SetImplicitThisParameter(DemangledTypeNode type); void AppendString(_STD_STRING& out, BN::Platform* platform) const; - _STD_STRING GetString() const; - _STD_STRING GetString(BN::Platform* platform) const; + _STD_STRING GetString(BN::Platform* platform = nullptr) const; _STD_STRING GetStringBeforeName(BN::Platform* platform) const; _STD_STRING GetStringAfterName(BN::Platform* platform) const; - _STD_STRING GetTypeAndName(const StringList& name) const; - _STD_STRING GetTypeAndName(const StringList& name, BN::Platform* platform) const; + _STD_STRING GetTypeAndName(const StringList& name, BN::Platform* platform = nullptr) const; BN::Ref Finalize(BN::Platform* platform = nullptr) const; @@ -282,7 +280,7 @@ class DemangledTypeNode uint8_t GetValueConfidence() const; BNTypeClass GetPayloadClass() const; NodeRef GetPrimaryChild() const; - static size_t ResolveWidth(size_t width, WidthKind widthKind, BN::Platform* platform = nullptr); + static size_t ResolveWidth(size_t width, WidthKind widthKind, const BN::Platform* platform = nullptr); BNNameType m_nameType; uint8_t m_pointerSuffixBits; From e36b135fa60d9091e97d936fa8edb7c7fd13b05f Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Fri, 19 Jun 2026 13:39:18 -0400 Subject: [PATCH 04/14] Preserve structured GNU3 expression template arguments --- demangler/demangled_template_simplifier.h | 2 +- demangler/demangled_type_node.cpp | 110 ++++++++++++++++- demangler/demangled_type_node.h | 21 +++- demangler/gnu3/demangle_gnu3.cpp | 138 ++++++++++++++++++---- demangler/gnu3/demangle_gnu3.h | 6 +- 5 files changed, 250 insertions(+), 27 deletions(-) diff --git a/demangler/demangled_template_simplifier.h b/demangler/demangled_template_simplifier.h index b57c4f281..b8e5c2581 100644 --- a/demangler/demangled_template_simplifier.h +++ b/demangler/demangled_template_simplifier.h @@ -14,7 +14,7 @@ #pragma once -#include "demangled_type_node.h" +#include "demangler/demangled_type_node.h" namespace DemangledTemplateSimplifier { diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index 9a1e12d72..e8de22b5a 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -551,6 +551,22 @@ DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string separator } +DemangledTypeNode DemangledTypeNode::UnaryExpression(string op, NodeRef child) +{ + DemangledTypeNode n; + n.m_payload = UnaryExpressionPayload{std::move(op), std::move(child)}; + return n; +} + + +DemangledTypeNode DemangledTypeNode::BinaryExpression(NodeRef left, string op, NodeRef right) +{ + DemangledTypeNode n; + n.m_payload = BinaryExpressionPayload{std::move(left), std::move(op), std::move(right)}; + return n; +} + + uint8_t DemangledTypeNode::PointerSuffixBit(BNPointerSuffix ps) { switch (ps) @@ -604,7 +620,9 @@ BNTypeClass DemangledTypeNode::GetPayloadClass() const case 9: return FunctionTypeClass; case 10: case 11: - // PostfixPayload is an internal named-type rendering form, so it reports as a named type. + case 12: + case 13: + // Internal expression rendering forms report as named types so they can be carried as template args. return NamedTypeReferenceClass; default: return VoidTypeClass; @@ -624,6 +642,10 @@ DemangledTypeNode::NodeRef DemangledTypeNode::GetPrimaryChild() const return payload->returnType; if (auto payload = std::get_if(&m_payload)) return payload->childType; + if (auto payload = std::get_if(&m_payload)) + return payload->childType; + if (auto payload = std::get_if(&m_payload)) + return payload->leftType; return nullptr; } @@ -660,6 +682,15 @@ bool DemangledTypeNode::MutateChildTypes(const std::functionchildType); mutateRef(payload->suffixType); } + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->childType); + } + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->leftType); + mutateRef(payload->rightType); + } return changed; } @@ -818,6 +849,11 @@ bool DemangledTypeNode::HasTemplateArguments() const { if (const auto* payload = std::get_if(&m_payload)) return std::ranges::any_of(payload->name, &DemangledNamePart::HasTemplateArguments); + if (const auto* payload = std::get_if(&m_payload)) + return payload->childType && payload->childType->HasTemplateArguments(); + if (const auto* payload = std::get_if(&m_payload)) + return (payload->leftType && payload->leftType->HasTemplateArguments()) || + (payload->rightType && payload->rightType->HasTemplateArguments()); return false; } @@ -925,6 +961,19 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons typePtrsEqual(payload->childType, otherPayload->childType) && typePtrsEqual(payload->suffixType, otherPayload->suffixType); } + if (auto payload = std::get_if(&m_payload)) + { + auto otherPayload = std::get_if(&other.m_payload); + return otherPayload && payload->op == otherPayload->op && + typePtrsEqual(payload->childType, otherPayload->childType); + } + if (auto payload = std::get_if(&m_payload)) + { + auto otherPayload = std::get_if(&other.m_payload); + return otherPayload && payload->op == otherPayload->op && + typePtrsEqual(payload->leftType, otherPayload->leftType) && + typePtrsEqual(payload->rightType, otherPayload->rightType); + } return false; } @@ -938,6 +987,16 @@ StringList DemangledTypeNode::RenderTypeNameSegments(Platform* platform) const result.push_back(GetString(platform)); return result; } + if (std::get_if(&m_payload)) + { + result.push_back(GetString(platform)); + return result; + } + if (std::get_if(&m_payload)) + { + result.push_back(GetString(platform)); + return result; + } if (auto payload = std::get_if(&m_payload)) { result.reserve(payload->name.size()); @@ -982,6 +1041,36 @@ void DemangledTypeNode::AppendPostfixType(string& out, Platform* platform) const } +void DemangledTypeNode::AppendUnaryExpression(string& out, Platform* platform) const +{ + const auto* payload = std::get_if(&m_payload); + if (!payload) + return; + out += payload->op; + out += '('; + if (payload->childType) + payload->childType->AppendString(out, platform); + out += ')'; +} + + +void DemangledTypeNode::AppendBinaryExpression(string& out, Platform* platform) const +{ + const auto* payload = std::get_if(&m_payload); + if (!payload) + return; + out += '('; + if (payload->leftType) + payload->leftType->AppendString(out, platform); + out += ") "; + out += payload->op; + out += " ("; + if (payload->rightType) + payload->rightType->AppendString(out, platform); + out += ')'; +} + + void DemangledTypeNode::AppendModifiers(string& out) const { if (m_const) @@ -1192,6 +1281,18 @@ void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* p AppendModifiers(out); break; } + if (std::get_if(&m_payload)) + { + AppendUnaryExpression(out, platform); + AppendModifiers(out); + break; + } + if (std::get_if(&m_payload)) + { + AppendBinaryExpression(out, platform); + AppendModifiers(out); + break; + } { const auto& payload = std::get(m_payload); switch (payload.ntrClass) @@ -1334,6 +1435,10 @@ bool DemangledTypeNode::HasUndeterminedTopLevelSize() const return payload->widthKind == FixedWidth && payload->width == 0; if (std::holds_alternative(m_payload)) return true; + if (std::holds_alternative(m_payload)) + return true; + if (std::holds_alternative(m_payload)) + return true; if (auto payload = std::get_if(&m_payload)) return payload->childType && payload->childType->HasUndeterminedTopLevelSize(); return false; @@ -1481,7 +1586,8 @@ Ref DemangledTypeNode::Finalize(Platform* platform) const case NamedTypeReferenceClass: { - if (std::get_if(&m_payload)) + if (std::get_if(&m_payload) || std::get_if(&m_payload) || + std::get_if(&m_payload)) { QualifiedName name(RenderTypeNameSegments(platform)); TypeBuilder tb = TypeBuilder::NamedType( diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index b51a967d8..5e2ffa385 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -152,6 +152,8 @@ class DemangledTypeNode StringList nameSegments, bool isSigned = false); static DemangledTypeNode PostfixType(NodeRef child, _STD_STRING suffix); static DemangledTypeNode PostfixType(NodeRef child, _STD_STRING separator, NodeRef suffixType); + static DemangledTypeNode UnaryExpression(_STD_STRING op, NodeRef child); + static DemangledTypeNode BinaryExpression(NodeRef left, _STD_STRING op, NodeRef right); static NodeRef CreateShared(DemangledTypeNode node); static NodeRef CreateSharedCopy(const DemangledTypeNode& node); @@ -262,6 +264,19 @@ class DemangledTypeNode NodeRef suffixType; }; + struct UnaryExpressionPayload + { + _STD_STRING op; + NodeRef childType; + }; + + struct BinaryExpressionPayload + { + NodeRef leftType; + _STD_STRING op; + NodeRef rightType; + }; + using Payload = std::variant< VoidPayload, BoolPayload, @@ -274,7 +289,9 @@ class DemangledTypeNode ArrayPayload, FunctionPayload, NamedTypePayload, - PostfixPayload>; + PostfixPayload, + UnaryExpressionPayload, + BinaryExpressionPayload>; bool HasUndeterminedTopLevelSize() const; uint8_t GetValueConfidence() const; @@ -294,6 +311,8 @@ class DemangledTypeNode void AddPointerSuffixes(BN::TypeBuilder& tb, bool omitPtr64 = true) const; bool HasPostfixType() const; void AppendPostfixType(_STD_STRING& out, BN::Platform* platform) const; + void AppendUnaryExpression(_STD_STRING& out, BN::Platform* platform) const; + void AppendBinaryExpression(_STD_STRING& out, BN::Platform* platform) const; void AppendModifiers(_STD_STRING& out) const; void AppendPointerSuffix(_STD_STRING& out) const; static void AppendNamePartList(_STD_STRING& out, const DemangledQualifiedName& name, diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index b85c3c2c5..152f1dce0 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -135,6 +135,30 @@ static DemangledNamePart NameSegmentWithTemplateArgs(const string& name, vector< } +static void AppendStructuredNameSegments(DemangledQualifiedName& name, const StringList& segments) +{ + for (const auto& segment: segments) + name.emplace_back(segment); +} + + +static void SetStructuredExpressionNode(DemangledTypeNode* outNode, const DemangledQualifiedName& name) +{ + if (outNode && !name.empty()) + { + if (name.size() > 1 && !name.back().HasTemplateArguments()) + { + DemangledQualifiedName baseName(name.begin(), name.end() - 1); + DemangledTypeNode baseType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, std::move(baseName)); + *outNode = DemangledTypeNode::PostfixType( + DemangledTypeNode::CreateShared(std::move(baseType)), "::" + name.back().GetString()); + return; + } + *outNode = DemangledTypeNode::NamedType(UnknownNamedTypeClass, name); + } +} + + static string GetOperator(char elm1, char elm2) { switch (hash(elm1, elm2)) @@ -1366,18 +1390,41 @@ string DemangleGNU3::DemangleUnarySuffixExpression(const string& op) } -string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op) +string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op, DemangledTypeNode* outNode) { - return op + "(" + DemangleExpression() + ")"; + DemangledTypeNode exprNode; + string expr = DemangleExpression(outNode ? &exprNode : nullptr); + if (outNode) + { + if (exprNode.GetClass() == VoidTypeClass) + exprNode = CreateUnknownType(expr); + *outNode = DemangledTypeNode::UnaryExpression( + op, DemangledTypeNode::CreateShared(std::move(exprNode))); + } + return op + "(" + expr + ")"; } -string DemangleGNU3::DemangleBinaryExpression(const string& op) +string DemangleGNU3::DemangleBinaryExpression(const string& op, DemangledTypeNode* outNode) { indent(); MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); - const string lhs = "(" + DemangleExpression() + ")"; - const string rhs = "(" + DemangleExpression() + ")"; + DemangledTypeNode lhsNode; + DemangledTypeNode rhsNode; + const string lhsExpr = DemangleExpression(outNode ? &lhsNode : nullptr); + const string rhsExpr = DemangleExpression(outNode ? &rhsNode : nullptr); + const string lhs = "(" + lhsExpr + ")"; + const string rhs = "(" + rhsExpr + ")"; + if (outNode) + { + if (lhsNode.GetClass() == VoidTypeClass) + lhsNode = CreateUnknownType(lhsExpr); + if (rhsNode.GetClass() == VoidTypeClass) + rhsNode = CreateUnknownType(rhsExpr); + *outNode = DemangledTypeNode::BinaryExpression( + DemangledTypeNode::CreateShared(std::move(lhsNode)), op, + DemangledTypeNode::CreateShared(std::move(rhsNode))); + } dedent(); return lhs + " " + op + " " + rhs; } @@ -1758,7 +1805,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() } -string DemangleGNU3::DemangleExpression() +string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) { MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); /* @@ -1815,7 +1862,10 @@ string DemangleGNU3::DemangleExpression() } else if (elm1 == 'T') // { - return DemangleTemplateSubstitution().GetString(); + DemangledTypeNode type = DemangleTemplateSubstitution(); + if (outNode) + *outNode = type; + return type.GetString(); } elm2 = m_reader.Read(); @@ -1877,7 +1927,7 @@ string DemangleGNU3::DemangleExpression() case hash('p','s'): // + (unary) case hash('a','d'): // & (unary) case hash('d','e'): // * (unary) - return DemangleUnaryPrefixExpression(GetOperator(elm1, elm2)); + return DemangleUnaryPrefixExpression(GetOperator(elm1, elm2), outNode); case hash('i','x'): // [] case hash('p','p'): // ++ (postfix in context) case hash('m','m'): // -- (postfix in context) @@ -1926,7 +1976,7 @@ string DemangleGNU3::DemangleExpression() case hash('a','N'): // &= case hash('o','R'): // |= case hash('e','O'): // ^= - return DemangleBinaryExpression(GetOperator(elm1, elm2)); + return DemangleBinaryExpression(GetOperator(elm1, elm2), outNode); case hash('d','l'): // delete case hash('d','a'): // delete[] case hash('n','w'): // new @@ -2042,9 +2092,12 @@ string DemangleGNU3::DemangleExpression() throw DemangleException(); } out = type.GetString(); + if (outNode) + *outNode = type; break; } case hash('s','r'): + { /* ::= ::= # T::x / decltype(p)::x @@ -2064,6 +2117,7 @@ string DemangleGNU3::DemangleExpression() ::= dn # destructor or pseudo-destructor; # e.g. ~X or ~X */ + DemangledQualifiedName structuredName; if (m_reader.Peek() == 'N') { m_reader.Consume(); @@ -2073,10 +2127,17 @@ string DemangleGNU3::DemangleExpression() // When the first component is a digit (source name), skip the // unresolved-type and let the loop below handle all qualifiers. if (!isdigit(m_reader.Peek())) - out += DemangleUnresolvedType().GetString() + "::"; + { + DemangledTypeNode unresolvedType = DemangleUnresolvedType(); + out += unresolvedType.GetString() + "::"; + AppendStructuredNameSegments(structuredName, unresolvedType.RenderTypeNameSegments()); + } do { - out += DemangleSourceName(); + const string segName = DemangleSourceName(); + const size_t segmentStart = out.size(); + out += segName; + DemangledNamePart structuredSegment(segName); // Push bare name (before template args) to substitution table. PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); if (m_reader.Peek() == 'I') @@ -2085,15 +2146,21 @@ string DemangleGNU3::DemangleExpression() m_reader.Consume(); // DemangleTemplateArgs(args); - out = NameSegmentWithTemplateArgs(out, std::move(args)).GetString(); + structuredSegment = NameSegmentWithTemplateArgs(segName, std::move(args)); + out.resize(segmentStart); + out += structuredSegment.GetString(); // Also push the template instantiation (name+args). PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); } + structuredName.push_back(std::move(structuredSegment)); out += "::"; }while (m_reader.Peek() != 'E'); m_reader.Consume(); - out += JoinNameSegments(DemangleBaseUnresolvedName()); + StringList baseName = DemangleBaseUnresolvedName(); + out += JoinNameSegments(baseName); + AppendStructuredNameSegments(structuredName, baseName); + SetStructuredExpressionNode(outNode, structuredName); return out; } if (isdigit(m_reader.Peek())) @@ -2113,7 +2180,9 @@ string DemangleGNU3::DemangleExpression() { hadTemplateArgs = false; const string segName = DemangleSourceName(); + const size_t segmentStart = out.size(); out += segName; + DemangledNamePart structuredSegment(segName); // Push bare name to substitution table. PushType(CreateUnknownType(out)); if (m_reader.Peek() == 'I') @@ -2121,11 +2190,14 @@ string DemangleGNU3::DemangleExpression() ParamList args; m_reader.Consume(); DemangleTemplateArgs(args); // consumes the trailing 'E' - out = NameSegmentWithTemplateArgs(out, std::move(args)).GetString(); + structuredSegment = NameSegmentWithTemplateArgs(segName, std::move(args)); + out.resize(segmentStart); + out += structuredSegment.GetString(); // Also push the template instantiation. PushType(CreateUnknownType(out)); hadTemplateArgs = true; } + structuredName.push_back(std::move(structuredSegment)); out += "::"; }while (!hadTemplateArgs && m_reader.Peek() != 'E'); // Consume qualifier-list 'E' if present. GCC sometimes omits it when @@ -2133,12 +2205,17 @@ string DemangleGNU3::DemangleExpression() // so check rather than unconditionally consuming. if (m_reader.Peek() == 'E') m_reader.Consume(); - out += JoinNameSegments(DemangleBaseUnresolvedName()); + StringList baseName = DemangleBaseUnresolvedName(); + out += JoinNameSegments(baseName); + AppendStructuredNameSegments(structuredName, baseName); + SetStructuredExpressionNode(outNode, structuredName); return out; } else { - out += DemangleUnresolvedType().GetString() + "::"; + DemangledTypeNode unresolvedType = DemangleUnresolvedType(); + out += unresolvedType.GetString() + "::"; + AppendStructuredNameSegments(structuredName, unresolvedType.RenderTypeNameSegments()); // GCC may encode multi-level scoped names without the 'N' qualifier // prefix, e.g. "sr St 6__and_IE 5value" for std::__and_::value. // Process any digit-started names: if a name has template args AND @@ -2152,6 +2229,7 @@ string DemangleGNU3::DemangleExpression() ParamList args; m_reader.Consume(); DemangleTemplateArgs(args); + DemangledNamePart structuredSegment = NameSegmentWithTemplateArgs(segName, args); if (isdigit(m_reader.Peek())) { // Another source name follows — intermediate qualifier. @@ -2160,11 +2238,14 @@ string DemangleGNU3::DemangleExpression() string segment = NameSegmentWithTemplateArgs(segName, std::move(args)).GetString(); PushType(CreateUnknownType(out + segment)); out += segment + "::"; + structuredName.push_back(std::move(structuredSegment)); } else { // No more source names — this template-id is the final name. out += NameSegmentWithTemplateArgs(segName, std::move(args)).GetString(); + structuredName.push_back(std::move(structuredSegment)); + SetStructuredExpressionNode(outNode, structuredName); return out; } } @@ -2172,24 +2253,35 @@ string DemangleGNU3::DemangleExpression() { // Plain source name with no template args — final base name. out += segName; + structuredName.emplace_back(segName); + SetStructuredExpressionNode(outNode, structuredName); return out; } } // peek is not a digit: fall back for operator-names ("on") / destructor-names ("dn"). - out += JoinNameSegments(DemangleBaseUnresolvedName()); + StringList baseName = DemangleBaseUnresolvedName(); + out += JoinNameSegments(baseName); + AppendStructuredNameSegments(structuredName, baseName); + SetStructuredExpressionNode(outNode, structuredName); } return out; + } default: m_reader.UnRead(2); out = DemangleSourceName(); + DemangledNamePart structuredSegment(out); if (m_reader.Peek() == 'I') { ParamList args; m_reader.Consume(); // DemangleTemplateArgs(args); - out = NameSegmentWithTemplateArgs(out, std::move(args)).GetString(); + structuredSegment = NameSegmentWithTemplateArgs(out, std::move(args)); + out = structuredSegment.GetString(); } + if (outNode) + *outNode = DemangledTypeNode::NamedType( + UnknownNamedTypeClass, DemangledQualifiedName{std::move(structuredSegment)}); break; } return out; @@ -2258,12 +2350,18 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) case 'X': { string expr; + DemangledTypeNode exprNode; + bool haveExprNode = false; bool emptyPack = false; if (!TryDemangleTemplateParamExpressionPackExpansion(expr, emptyPack)) - expr = DemangleExpression(); + { + expr = DemangleExpression(&exprNode); + haveExprNode = true; + } if (!emptyPack) { - DemangledTypeNode exprNode = CreateUnknownType(expr); + if (!haveExprNode || exprNode.GetClass() == VoidTypeClass) + exprNode = CreateUnknownType(expr); args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); } if (m_reader.Read() != 'E') diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 4b08465dc..d4352d2c7 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -161,15 +161,15 @@ class DemangleGNU3 StringList DemangleBaseUnresolvedName(); DemangledTypeNode DemangleUnresolvedType(); _STD_STRING DemangleUnarySuffixExpression(const _STD_STRING& op); - _STD_STRING DemangleUnaryPrefixExpression(const _STD_STRING& op); - _STD_STRING DemangleBinaryExpression(const _STD_STRING& op); + _STD_STRING DemangleUnaryPrefixExpression(const _STD_STRING& op, DemangledTypeNode* outNode = nullptr); + _STD_STRING DemangleBinaryExpression(const _STD_STRING& op, DemangledTypeNode* outNode = nullptr); _STD_STRING DemangleUnaryPrefixType(const _STD_STRING& op); _STD_STRING DemangleTypeString(); _STD_STRING DemangleExpressionList(); DemangledTypeNode DemangleUnqualifiedName(); _STD_STRING DemangleSourceName(); _STD_STRING DemangleNumberAsString(); - _STD_STRING DemangleExpression(); + _STD_STRING DemangleExpression(DemangledTypeNode* outNode = nullptr); _STD_STRING DemanglePrimaryExpression(); NodeRef DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef = nullptr); bool TryDemangleTemplateParamPackExpansion(DemangledTypeNode& type, bool& emptyPack); From 2777acd0e2c369ed25bf7b06ccf2c0bfd866c23c Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Wed, 24 Jun 2026 16:30:59 -0400 Subject: [PATCH 05/14] Simplify demangler API surface Use fallback platform for demangler display defaults --- binaryninjaapi.h | 104 ++++----- binaryninjacore.h | 66 +++--- demangle.cpp | 239 ++++++++++++-------- demangler/demangled_type_node.cpp | 199 +++++++++------- demangler/demangled_type_node.h | 36 +-- demangler/gnu3/CMakeLists.txt | 3 +- demangler/gnu3/demangle_gnu3.cpp | 124 +++------- demangler/gnu3/demangle_gnu3.h | 32 +-- demangler/msvc/CMakeLists.txt | 1 + demangler/msvc/demangle_msvc.cpp | 261 +++------------------ demangler/msvc/demangle_msvc.h | 64 +----- demangler/nesting_guard.h | 57 +++++ plugins/pdb-ng/src/symbol_parser.rs | 8 +- plugins/rtti/itanium.cpp | 1 + plugins/rtti/microsoft.cpp | 6 +- plugins/rtti/rtti.cpp | 7 +- plugins/rtti/rtti.h | 4 +- python/demangle.py | 279 ++++++++++++----------- rust/src/demangle.rs | 314 ++++++++++---------------- rust/tests/demangler.rs | 10 +- ui/linearview.h | 3 +- view/elf/elfview.cpp | 2 +- view/kernelcache/api/kernelcache.cpp | 2 +- view/kernelcache/core/KernelCache.cpp | 2 +- view/macho/machoview.cpp | 2 +- view/pe/coffview.cpp | 2 +- view/pe/peview.cpp | 2 +- view/sharedcache/api/sharedcache.cpp | 2 +- view/sharedcache/core/SharedCache.cpp | 2 +- 29 files changed, 801 insertions(+), 1033 deletions(-) create mode 100644 demangler/nesting_guard.h diff --git a/binaryninjaapi.h b/binaryninjaapi.h index e384f25dd..5d15c647d 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2437,7 +2437,7 @@ namespace BinaryNinja { \see Demangler::Demangle for a discussion on which demangler will be used. - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. + \param[in] platform Platform for the symbol. Used for pointer/integer sizes and calling conventions. \param[in] mangledName a mangled Microsoft Visual Studio C++ name \param[out] outType Pointer to Type to output \param[out] outVarName QualifiedName reference to write the output name to. @@ -2447,23 +2447,23 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleGeneric(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - Ref view = nullptr, const bool simplify = false); + bool DemangleGeneric(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + Ref view = nullptr, bool simplify = false); /*! Demangles using LLVM's demangler - \param[in] mangledName a mangled (msvc/itanium/rust/dlang) name + \param[in] mangledName a mangled (msvc/gnu3/rust/dlang) name \param[out] outVarName QualifiedName reference to write the output name to. \param[in] simplify Whether to simplify demangled names. \return True if the name was demangled and written to the out* parameters \ingroup demangle */ - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, const bool simplify = false); + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, bool simplify = false); /*! Demangles using LLVM's demangler - \param[in] mangledName a mangled (msvc/itanium/rust/dlang) name + \param[in] mangledName a mangled (msvc/gnu3/rust/dlang) name \param[out] outVarName QualifiedName reference to write the output name to. \param[in] view View to check the analysis.types.templateSimplifier for \return True if the name was demangled and written to the out* parameters @@ -2474,7 +2474,7 @@ namespace BinaryNinja { /*! Demangles a Microsoft Visual Studio C++ name - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. + \param[in] platform Platform for the symbol. Required for pointer/integer sizes and calling conventions. \param[in] mangledName a mangled Microsoft Visual Studio C++ name \param[out] outType Reference to Type to output \param[out] outVarName QualifiedName reference to write the output name to. @@ -2483,29 +2483,12 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - const bool simplify = false); - - /*! Demangles a Microsoft Visual Studio C++ name - - This overload will use the view's "analysis.types.templateSimplifier" setting - to determine whether to simplify the mangled name. - - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. - \param[in] mangledName a mangled Microsoft Visual Studio C++ name - \param[out] outType Reference to Type to output - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view View to check the analysis.types.templateSimplifier for - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - BinaryView* view); + bool DemangleMS(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify = false); /*! Demangles a GNU3 name - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. + \param[in] platform Platform for the symbol. Required for pointer/integer sizes and calling conventions. \param[in] mangledName a mangled GNU3 name \param[out] outType Reference to Type to output \param[out] outVarName QualifiedName reference to write the output name to. @@ -2514,25 +2497,8 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, - QualifiedName& outVarName, const bool simplify = false); - - /*! Demangles a GNU3 name - - This overload will use the view's "analysis.types.templateSimplifier" setting - to determine whether to simplify the mangled name. - - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. - \param[in] mangledName a mangled GNU3 name - \param[out] outType Reference to Type to output - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view View to check the analysis.types.templateSimplifier for - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view); + bool DemangleGNU3(Platform* platform, const std::string& mangledName, Ref& outType, + QualifiedName& outVarName, bool simplify = false); /*! Determines if a symbol name is a mangled GNU3 name @@ -4811,6 +4777,28 @@ namespace BinaryNinja { static QualifiedName FromAPIObject(const BNQualifiedName* name); }; + struct DemanglerConfig + { + Ref platform; + Ref view; + bool simplifyTemplates = false; + + static DemanglerConfig Default(); + static DemanglerConfig ForPlatform(Platform* platform, bool simplifyTemplates = false); + static DemanglerConfig ForBinaryView(BinaryView* view); + + BNDemanglerConfig GetAPIObject() const; + }; + + struct DemanglerResult + { + QualifiedName name; + Ref type; + }; + + std::optional TryDemangle( + const std::string& mangledName, const DemanglerConfig& config = DemanglerConfig::Default()); + /*! \ingroup namelist @@ -22492,6 +22480,11 @@ namespace BinaryNinja { */ class Demangler: public StaticCoreRefCountObject { + public: + using Config = DemanglerConfig; + using Result = DemanglerResult; + + private: std::string m_nameForRegister; protected: @@ -22500,9 +22493,9 @@ namespace BinaryNinja { virtual ~Demangler() = default; static bool IsMangledStringCallback(void* ctxt, const char* name); - static bool DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); - static void FreeVarNameCallback(void* ctxt, BNQualifiedName* name); + static bool DemangleCallback(void* ctxt, const char* name, const BNDemanglerConfig* config, + BNDemanglerResult* result); + static void FreeResultCallback(void* ctxt, BNDemanglerResult* result); public: /*! Register a custom Demangler. Newly registered demanglers will get priority over @@ -22554,15 +22547,11 @@ namespace BinaryNinja { If the mangled name has no type information, but a name is still possible to extract, this function may return a successful result with outType=nullptr, which will be accepted. - \param arch Architecture for context in which the name exists, eg for pointer sizes \param name Raw mangled name - \param outType Resulting type, if one can be deduced, will be written here. Otherwise nullptr will be written - \param outVarName Resulting variable name - \param view (Optional) BinaryView context in which the name exists, eg for type lookup - \return True if demangling was successful and results were stored into out-parameters + \param config Platform/view/options used while demangling + \return Demangled type/name if successful */ - virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view = nullptr, bool simplify = false) = 0; + virtual std::optional Demangle(const std::string& name, const Config& config) = 0; }; /*! @@ -22575,8 +22564,7 @@ namespace BinaryNinja { virtual ~CoreDemangler() = default; virtual bool IsMangledString(const std::string& name); - virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view, bool simplify = false); + virtual std::optional Demangle(const std::string& name, const Config& config); }; namespace Unicode diff --git a/binaryninjacore.h b/binaryninjacore.h index b07b9fb3e..f18c3d750 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 176 +#define BN_CURRENT_CORE_ABI_VERSION 177 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 176 +#define BN_MINIMUM_CORE_ABI_VERSION 177 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -3898,13 +3898,27 @@ extern "C" void (*freeConflictList)(void* context, BNAnalysisMergeConflict** conflictList, size_t count); } BNAnalysisMergeConflictSplitterCallbacks; + typedef struct BNDemanglerConfig + { + BNPlatform* platform; + BNBinaryView* view; + bool simplifyTemplates; + } BNDemanglerConfig; + + typedef struct BNDemanglerResult + { + BNQualifiedName name; + BNType* type; + } BNDemanglerResult; + typedef struct BNDemanglerCallbacks { + size_t size; void* context; bool (*isMangledString)(void* ctxt, const char* name); - bool (*demangle)(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); - void (*freeVarName)(void* ctxt, BNQualifiedName* name); + bool (*demangle)(void* ctxt, const char* name, const BNDemanglerConfig* config, + BNDemanglerResult* result); + void (*freeResult)(void* ctxt, BNDemanglerResult* result); } BNDemanglerCallbacks; BN_ENUM(uint8_t, BNScopeType) @@ -8246,39 +8260,27 @@ extern "C" BINARYNINJACOREAPI void BNUpdateReportFlowGraph(BNReportCollection* reports, size_t i, BNFlowGraph* graph); // Demangler - BINARYNINJACOREAPI bool BNDemangleMS(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - BINARYNINJACOREAPI bool BNDemangleMSWithOptions(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); - BINARYNINJACOREAPI bool BNDemangleMSPlatform(BNPlatform* platform, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - - BINARYNINJACOREAPI bool BNIsGNU3MangledString(const char* mangledName); - BINARYNINJACOREAPI bool BNDemangleGNU3(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - BINARYNINJACOREAPI bool BNDemangleGNU3WithOptions(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); - BINARYNINJACOREAPI void BNFreeDemangledName(char*** name, size_t nameElements); - - BINARYNINJACOREAPI bool BNDemangleLLVM(const char* mangledName, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - BINARYNINJACOREAPI bool BNDemangleLLVMWithOptions(const char* mangledName, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); - - BINARYNINJACOREAPI BNDemangler* BNRegisterDemangler(const char* name, BNDemanglerCallbacks* callbacks); +#define BN_DEMANGLER_MSVC "msvc" +#define BN_DEMANGLER_GNU3 "gnu3" +#define BN_DEMANGLER_LLVM "llvm" + + BINARYNINJACOREAPI BNDemanglerConfig BNGetDefaultDemanglerConfig(void); + BINARYNINJACOREAPI BNDemanglerConfig BNGetDemanglerConfigForPlatform(BNPlatform* platform, + bool simplifyTemplates); + BINARYNINJACOREAPI BNDemanglerConfig BNGetDemanglerConfigForBinaryView(BNBinaryView* view); + BINARYNINJACOREAPI bool BNDemangle(const char* name, const BNDemanglerConfig* config, + BNDemanglerResult* result); + BINARYNINJACOREAPI bool BNDemangleWithDemangler(BNDemangler* demangler, const char* name, + const BNDemanglerConfig* config, BNDemanglerResult* result); + BINARYNINJACOREAPI void BNFreeDemanglerResult(BNDemanglerResult* result); + + BINARYNINJACOREAPI BNDemangler* BNRegisterDemangler(const char* name, const BNDemanglerCallbacks* callbacks); BINARYNINJACOREAPI BNDemangler** BNGetDemanglerList(size_t* count); BINARYNINJACOREAPI void BNFreeDemanglerList(BNDemangler** demanglers); BINARYNINJACOREAPI BNDemangler* BNGetDemanglerByName(const char* name); BINARYNINJACOREAPI char* BNGetDemanglerName(BNDemangler* demangler); BINARYNINJACOREAPI void BNPromoteDemangler(BNDemangler* demangler); - BINARYNINJACOREAPI bool BNIsDemanglerMangledName(BNDemangler* demangler, const char* name); - BINARYNINJACOREAPI bool BNDemanglerDemangleWithOptions(BNDemangler* demangler, BNArchitecture* arch, - const char* name, BNType** outType, BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); - BINARYNINJACOREAPI bool BNDemanglerDemangle(BNDemangler* demangler, BNArchitecture* arch, const char* name, - BNType** outType, BNQualifiedName* outVarName, BNBinaryView* view); - BINARYNINJACOREAPI bool BNDemangleGeneric(BNArchitecture* arch, const char* name, - BNType** outType, BNQualifiedName* outVarName, BNBinaryView* view, bool simplify); // Plugin repository APIs BINARYNINJACOREAPI char** BNPluginGetApis(BNPlugin* p, size_t* count); diff --git a/demangle.cpp b/demangle.cpp index 058dc9fb5..4d2586642 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -4,109 +4,159 @@ using namespace std; using namespace BinaryNinja; namespace BinaryNinja { - bool DemangleGeneric(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view, bool simplify) + static void StoreDemanglerResult(BNDemanglerResult& apiResult, DemanglerResult& result) { - BNType* apiType = nullptr; - BNQualifiedName apiVarName; - bool success = BNDemangleGeneric( - arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr, simplify); + result.name = QualifiedName::FromAPIObject(&apiResult.name); + if (apiResult.type) + { + result.type = new Type(apiResult.type); + apiResult.type = nullptr; + } + else + { + result.type = nullptr; + } + } - if (!success) - return false; + static DemanglerConfig DemanglerConfigFromAPIObject(const BNDemanglerConfig* config) + { + DemanglerConfig result; + if (!config) + return result; - outType = apiType ? new Type(apiType) : nullptr; - outVarName = QualifiedName::FromAPIObject(&apiVarName); - BNFreeQualifiedName(&apiVarName); - return true; + result.platform = config->platform ? new CorePlatform(BNNewPlatformReference(config->platform)) : nullptr; + result.view = config->view ? new BinaryView(BNNewViewReference(config->view)) : nullptr; + result.simplifyTemplates = config->simplifyTemplates; + return result; } - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, - BinaryView* view) + DemanglerConfig DemanglerConfig::Default() { - const bool simplify = Settings::Instance()->Get("analysis.types.templateSimplifier", view); - return DemangleLLVM(mangledName, outVarName, simplify); + BNDemanglerConfig config = BNGetDefaultDemanglerConfig(); + return DemanglerConfigFromAPIObject(&config); } - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, - const bool simplify) + DemanglerConfig DemanglerConfig::ForPlatform(Platform* platform, bool simplifyTemplates) { - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleLLVM(mangledName.c_str(), &localVarName, &localSize, simplify)) - return false; - for (size_t i = 0; i < localSize; i++) + BNDemanglerConfig config = BNGetDemanglerConfigForPlatform(platform ? platform->GetObject() : nullptr, + simplifyTemplates); + return DemanglerConfigFromAPIObject(&config); + } + + DemanglerConfig DemanglerConfig::ForBinaryView(BinaryView* view) + { + BNDemanglerConfig config = BNGetDemanglerConfigForBinaryView(view ? view->GetObject() : nullptr); + return DemanglerConfigFromAPIObject(&config); + } + + BNDemanglerConfig DemanglerConfig::GetAPIObject() const + { + return { + platform ? platform->GetObject() : nullptr, + view ? view->GetObject() : nullptr, + simplifyTemplates, + }; + } + + static BNDemanglerConfig DemanglerConfigForContext(Platform* platform, BinaryView* view, bool simplify) + { + BNDemanglerConfig config; + if (view) { - outVarName.push_back(localVarName[i]); + config = BNGetDemanglerConfigForBinaryView(view->GetObject()); + config.simplifyTemplates = simplify; + if (platform) + config.platform = platform->GetObject(); + return config; } - BNFreeDemangledName(&localVarName, localSize); - return true; + + return BNGetDemanglerConfigForPlatform(platform ? platform->GetObject() : nullptr, simplify); } - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - BinaryView* view) + static bool DemangleWithNamedDemangler(const char* demanglerName, Platform* platform, const std::string& mangledName, + Ref& outType, QualifiedName& outVarName, BinaryView* view, bool simplify) { - BNType* localType = nullptr; - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleMSWithOptions(arch->GetObject(), mangledName.c_str(), &localType, &localVarName, &localSize, - view ? view->GetObject() : nullptr)) + BNDemangler* demangler = BNGetDemanglerByName(demanglerName); + if (!demangler) return false; - outType = localType ? new Type(localType) : nullptr; - for (size_t i = 0; i < localSize; i++) - { - outVarName.push_back(localVarName[i]); - } - BNFreeDemangledName(&localVarName, localSize); + + BNDemanglerConfig apiConfig = DemanglerConfigForContext(platform, view, simplify); + BNDemanglerResult apiResult = {}; + if (!BNDemangleWithDemangler(demangler, mangledName.c_str(), &apiConfig, &apiResult)) + return false; + + DemanglerResult result; + StoreDemanglerResult(apiResult, result); + BNFreeDemanglerResult(&apiResult); + outType = result.type; + outVarName = result.name; return true; } - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - const bool simplify) + std::optional TryDemangle(const std::string& mangledName, const DemanglerConfig& config) + { + BNDemanglerConfig apiConfig = config.GetAPIObject(); + BNDemanglerResult apiResult = {}; + if (!BNDemangle(mangledName.c_str(), &apiConfig, &apiResult)) + return std::nullopt; + + DemanglerResult result; + StoreDemanglerResult(apiResult, result); + BNFreeDemanglerResult(&apiResult); + return result; + } + + bool DemangleGeneric(Platform* platform, const std::string& name, Ref& outType, + QualifiedName& outVarName, Ref view, bool simplify) { - BNType* localType = nullptr; - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleMS(arch->GetObject(), mangledName.c_str(), &localType, &localVarName, &localSize, simplify)) + BNDemanglerConfig apiConfig = DemanglerConfigForContext(platform, view, simplify); + BNDemanglerResult apiResult = {}; + if (!BNDemangle(name.c_str(), &apiConfig, &apiResult)) return false; - outType = localType ? new Type(localType) : nullptr; - for (size_t i = 0; i < localSize; i++) - { - outVarName.push_back(localVarName[i]); - } - BNFreeDemangledName(&localVarName, localSize); + + DemanglerResult result; + StoreDemanglerResult(apiResult, result); + BNFreeDemanglerResult(&apiResult); + outType = result.type; + outVarName = result.name; return true; } - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - BinaryView* view) + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, + BinaryView* view) { const bool simplify = Settings::Instance()->Get("analysis.types.templateSimplifier", view); - return DemangleGNU3(arch, mangledName, outType, outVarName, simplify); + return DemangleLLVM(mangledName, outVarName, simplify); } - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - const bool simplify) + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, + bool simplify) + { + Ref outType; + return DemangleWithNamedDemangler(BN_DEMANGLER_LLVM, nullptr, mangledName, outType, outVarName, nullptr, + simplify); + } + + bool DemangleMS(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify) + { + return DemangleWithNamedDemangler(BN_DEMANGLER_MSVC, platform, mangledName, outType, outVarName, nullptr, + simplify); + } + + bool DemangleGNU3(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify) { - BNType* localType = nullptr; - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleGNU3(arch->GetObject(), mangledName.c_str(), &localType, &localVarName, &localSize, simplify)) - return false; - outType = localType ? new Type(localType) : nullptr; outVarName.clear(); - for (size_t i = 0; i < localSize; i++) - { - outVarName.push_back(localVarName[i]); - } - BNFreeDemangledName(&localVarName, localSize); - return true; + return DemangleWithNamedDemangler(BN_DEMANGLER_GNU3, platform, mangledName, outType, outVarName, nullptr, + simplify); } bool IsGNU3MangledString(const std::string& mangledName) { - return BNIsGNU3MangledString(mangledName.c_str()); + BNDemangler* demangler = BNGetDemanglerByName(BN_DEMANGLER_GNU3); + return demangler && BNIsDemanglerMangledName(demangler, mangledName.c_str()); } @@ -125,45 +175,44 @@ namespace BinaryNinja { return demangler->IsMangledString(name); } - bool Demangler::DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view, bool simplify) + bool Demangler::DemangleCallback(void* ctxt, const char* name, const BNDemanglerConfig* config, + BNDemanglerResult* result) { Demangler* demangler = (Demangler*)ctxt; - Ref apiArch = new CoreArchitecture(arch); - Ref apiView = view ? new BinaryView(BNNewViewReference(view)) : nullptr; + if (!name || !result) + return false; - Ref apiType; - QualifiedName apiVarName; - bool success = demangler->Demangle(apiArch, name, apiType, apiVarName, apiView, simplify); - if (!success) + auto demangleResult = demangler->Demangle(name, DemanglerConfigFromAPIObject(config)); + if (!demangleResult) return false; - if (apiType) + if (demangleResult->type) { - *outType = BNNewTypeReference(apiType->m_object); + result->type = BNNewTypeReference(demangleResult->type->m_object); } else { - *outType = nullptr; + result->type = nullptr; } - *outVarName = apiVarName.GetAPIObject(); + result->name = demangleResult->name.GetAPIObject(); return true; } - void Demangler::FreeVarNameCallback(void* ctxt, BNQualifiedName* name) + void Demangler::FreeResultCallback(void* ctxt, BNDemanglerResult* result) { - QualifiedName::FreeAPIObject(name); + BNFreeDemanglerResult(result); } void Demangler::Register(Demangler* demangler) { - BNDemanglerCallbacks cb; + BNDemanglerCallbacks cb = {}; + cb.size = sizeof(cb); cb.context = (void*)demangler; cb.isMangledString = IsMangledStringCallback; cb.demangle = DemangleCallback; - cb.freeVarName = FreeVarNameCallback; + cb.freeResult = FreeResultCallback; demangler->m_object = BNRegisterDemangler(demangler->m_nameForRegister.c_str(), &cb); } @@ -209,20 +258,18 @@ namespace BinaryNinja { return BNIsDemanglerMangledName(m_object, name.c_str()); } - bool CoreDemangler::Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view, bool simplify) + std::optional CoreDemangler::Demangle(const std::string& name, const Config& config) { - BNType* apiType = nullptr; - BNQualifiedName apiVarName; - bool success = BNDemanglerDemangleWithOptions( - m_object, arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr, simplify); + BNDemanglerConfig apiConfig = config.GetAPIObject(); + BNDemanglerResult apiResult = {}; + bool success = BNDemangleWithDemangler(m_object, name.c_str(), &apiConfig, &apiResult); if (!success) - return false; + return std::nullopt; - outType = apiType ? new Type(apiType) : nullptr; - outVarName = QualifiedName::FromAPIObject(&apiVarName); - BNFreeQualifiedName(&apiVarName); - return true; + Result result; + StoreDemanglerResult(apiResult, result); + BNFreeDemanglerResult(&apiResult); + return result; } } // namespace BinaryNinja diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index e8de22b5a..7aa274df5 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -18,6 +18,7 @@ #endif #include "base/assertions.h" #include +#include #ifdef BINARYNINJACORE_LIBRARY using namespace BinaryNinjaCore; @@ -35,6 +36,68 @@ namespace static constexpr uint8_t DemangledReferenceBit = 1u << 3; static constexpr uint8_t DemangledLvalueBit = 1u << 4; + class DemanglerFallbackArchitecture : public Architecture + { + public: + DemanglerFallbackArchitecture() : Architecture("demangler_fallback") {} + + BNEndianness GetEndianness() const override { return LittleEndian; } + size_t GetAddressSize() const override { return 8; } + size_t GetDefaultIntegerSize() const override { return 4; } + size_t GetInstructionAlignment() const override { return 1; } + size_t GetMaxInstructionLength() const override { return 1; } + size_t GetOpcodeDisplayLength() const override { return 1; } + +#ifdef BINARYNINJACORE_LIBRARY + bool GetInstructionInfo(const uint8_t*, uint64_t, size_t, InstructionInfo&) override { return false; } + bool GetInstructionText(const uint8_t*, uint64_t, size_t&, vector&) override { return false; } + bool GetInstructionTextWithContext(const uint8_t*, uint64_t, size_t&, void*, vector&) override { return false; } + bool GetInstructionLowLevelIL(const uint8_t*, uint64_t, size_t&, LowLevelILFunction&) override { return false; } + void AnalyzeBasicBlocks(Function&, BNBasicBlockAnalysisContext*) override {} + bool LiftFunction(LowLevelILFunction&, BNFunctionLifterContext*) override { return false; } + void FreeFunctionArchContext(void*) override {} + string GetRegisterName(uint32_t) override { return {}; } + string GetFlagName(uint32_t) override { return {}; } + string GetFlagWriteTypeName(uint32_t) override { return {}; } + string GetSemanticFlagClassName(uint32_t) override { return {}; } + string GetSemanticFlagGroupName(uint32_t) override { return {}; } + vector GetFullWidthRegisters() override { return {}; } + vector GetAllRegisters() override { return {}; } + vector GetAllFlags() override { return {}; } + vector GetAllFlagWriteTypes() override { return {}; } + vector GetAllSemanticFlagClasses() override { return {}; } + vector GetAllSemanticFlagGroups() override { return {}; } + BNFlagRole GetFlagRole(uint32_t, uint32_t = 0) override { return SpecialFlagRole; } + vector GetFlagsRequiredForFlagCondition(BNLowLevelILFlagCondition, uint32_t = 0) override { return {}; } + vector GetFlagsRequiredForSemanticFlagGroup(uint32_t) override { return {}; } + map GetFlagConditionsForSemanticFlagGroup(uint32_t) override { return {}; } + uint32_t GetSemanticClassForFlagWriteType(uint32_t) override { return 0; } + BNRegisterInfo GetRegisterInfo(uint32_t) override { return {}; } + uint32_t GetStackPointerRegister() override { return 0; } + BNIntrinsicClass GetIntrinsicClass(uint32_t) override { return GeneralIntrinsicClass; } + string GetIntrinsicName(uint32_t) override { return {}; } + vector GetAllIntrinsics() override { return {}; } + vector GetIntrinsicInputs(uint32_t) override { return {}; } + vector>> GetIntrinsicOutputs(uint32_t) override { return {}; } + bool CanAssemble() override { return false; } + bool Assemble(const string&, uint64_t, DataBuffer&, string&) override { return false; } + bool IsNeverBranchPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsAlwaysBranchPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsInvertBranchPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsSkipAndReturnZeroPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsSkipAndReturnValuePatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool ConvertToNop(uint8_t*, uint64_t, size_t) override { return false; } + bool AlwaysBranch(uint8_t*, uint64_t, size_t) override { return false; } + bool InvertBranch(uint8_t*, uint64_t, size_t) override { return false; } + bool SkipAndReturnValue(uint8_t*, uint64_t, size_t, uint64_t) override { return false; } + Architecture* RegisterArchitectureHook(BNCustomArchitecture*) override { return nullptr; } +#else + bool GetInstructionInfo(const uint8_t*, uint64_t, size_t, InstructionInfo&) override { return false; } + bool GetInstructionText(const uint8_t*, uint64_t, size_t&, vector&) override { return false; } + bool GetInstructionLowLevelIL(const uint8_t*, uint64_t, size_t&, LowLevelILFunction&) override { return false; } +#endif + }; + static void AppendPointerSuffixToken(string& out, const char* token) { if (!out.empty() && out.back() != ' ') @@ -65,7 +128,7 @@ namespace } static void AppendTemplateArgumentList(string& out, const vector& args, - bool spaceAfterComma, Platform* platform) + bool spaceAfterComma, Platform& platform) { if (args.empty()) return; @@ -107,87 +170,44 @@ namespace return empty; } - static size_t ResolveAddressWidth(const Platform* platform) + static size_t ResolveAddressWidth(const Platform& platform) { - if (platform) - return platform->GetAddressSize(); - return 8; + return platform.GetAddressSize(); } - static size_t ResolveDefaultIntegerWidth(const Platform* platform) + static size_t ResolveDefaultIntegerWidth(const Platform& platform) { - if (platform) - { -#ifdef BINARYNINJACORE_LIBRARY - Architecture* platformArch = platform->GetArchitecture(); -#else - Ref platformArch = platform->GetArchitecture(); -#endif - if (platformArch) - return platformArch->GetDefaultIntegerSize(); - } - return 4; + auto platformArch = platform.GetArchitecture(); + return platformArch->GetDefaultIntegerSize(); } - static Ref ResolveCallingConvention(BNCallingConventionName cc, const Platform* platform) + static Ref ResolveCallingConvention(BNCallingConventionName cc, const Platform& platform) { -#ifndef BINARYNINJACORE_LIBRARY - Ref platformArch; -#endif - Architecture* arch = nullptr; - if (platform) - { -#ifdef BINARYNINJACORE_LIBRARY - arch = platform->GetArchitecture(); -#else - platformArch = platform->GetArchitecture(); - arch = platformArch.GetPtr(); -#endif - } + auto platformArch = platform.GetArchitecture(); + Architecture* arch = platformArch; switch (cc) { case CdeclCallingConvention: - if (platform) - { - if (auto platformCC = platform->GetCdeclCallingConvention()) - return platformCC; - } - if (arch) - { - if (auto archCC = arch->GetCdeclCallingConvention()) - return archCC; - } - return arch ? arch->GetCallingConventionByName("cdecl") : nullptr; + if (auto platformCC = platform.GetCdeclCallingConvention()) + return platformCC; + if (auto archCC = arch->GetCdeclCallingConvention()) + return archCC; + return arch->GetCallingConventionByName("cdecl"); case STDCallCallingConvention: - if (platform) - { - if (auto platformCC = platform->GetStdcallCallingConvention()) - return platformCC; - } - if (arch) - { - if (auto archCC = arch->GetStdcallCallingConvention()) - return archCC; - } - return arch ? arch->GetCallingConventionByName("stdcall") : nullptr; + if (auto platformCC = platform.GetStdcallCallingConvention()) + return platformCC; + if (auto archCC = arch->GetStdcallCallingConvention()) + return archCC; + return arch->GetCallingConventionByName("stdcall"); case FastcallCallingConvention: - if (platform) - { - if (auto platformCC = platform->GetFastcallCallingConvention()) - return platformCC; - } - if (arch) - { - if (auto archCC = arch->GetFastcallCallingConvention()) - return archCC; - return arch->GetCallingConventionByName("fastcall"); - } - return nullptr; + if (auto platformCC = platform.GetFastcallCallingConvention()) + return platformCC; + if (auto archCC = arch->GetFastcallCallingConvention()) + return archCC; + return arch->GetCallingConventionByName("fastcall"); case ThisCallCallingConvention: - if (arch) - return arch->GetCallingConventionByName("thiscall"); - return nullptr; + return arch->GetCallingConventionByName("thiscall"); default: return nullptr; } @@ -195,6 +215,13 @@ namespace } +Platform& GetDemanglerFallbackPlatform() +{ + static DemanglerFallbackArchitecture arch; + static auto platform = arch.GetStandalonePlatform(); + return *platform; +} + #define HAS_POINTER_SUFFIX(bit) ((m_pointerSuffixBits & (bit)) != 0) static const char* CallingConventionString[] = @@ -267,7 +294,7 @@ void DemangledNamePart::ClearTemplateArguments() } -void DemangledNamePart::AppendString(string& out, Platform* platform) const +void DemangledNamePart::AppendString(string& out, Platform& platform) const { out += m_base; if (m_baseTypeSuffix) @@ -281,7 +308,7 @@ void DemangledNamePart::AppendString(string& out, Platform* platform) const } -string DemangledNamePart::GetString(Platform* platform) const +string DemangledNamePart::GetString(Platform& platform) const { string out; AppendString(out, platform); @@ -587,7 +614,7 @@ uint8_t DemangledTypeNode::PointerSuffixBit(BNPointerSuffix ps) } -size_t DemangledTypeNode::ResolveWidth(size_t width, WidthKind widthKind, const Platform* platform) +size_t DemangledTypeNode::ResolveWidth(size_t width, WidthKind widthKind, const Platform& platform) { switch (widthKind) { @@ -979,7 +1006,7 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons } -StringList DemangledTypeNode::RenderTypeNameSegments(Platform* platform) const +StringList DemangledTypeNode::RenderTypeNameSegments(Platform& platform) const { StringList result; if (std::get_if(&m_payload)) @@ -1028,7 +1055,7 @@ bool DemangledTypeNode::HasPostfixType() const } -void DemangledTypeNode::AppendPostfixType(string& out, Platform* platform) const +void DemangledTypeNode::AppendPostfixType(string& out, Platform& platform) const { const auto* payload = std::get_if(&m_payload); if (!payload) @@ -1041,7 +1068,7 @@ void DemangledTypeNode::AppendPostfixType(string& out, Platform* platform) const } -void DemangledTypeNode::AppendUnaryExpression(string& out, Platform* platform) const +void DemangledTypeNode::AppendUnaryExpression(string& out, Platform& platform) const { const auto* payload = std::get_if(&m_payload); if (!payload) @@ -1054,7 +1081,7 @@ void DemangledTypeNode::AppendUnaryExpression(string& out, Platform* platform) c } -void DemangledTypeNode::AppendBinaryExpression(string& out, Platform* platform) const +void DemangledTypeNode::AppendBinaryExpression(string& out, Platform& platform) const { const auto* payload = std::get_if(&m_payload); if (!payload) @@ -1094,7 +1121,7 @@ void DemangledTypeNode::AppendPointerSuffix(string& out) const void DemangledTypeNode::AppendNamePartList( - string& out, const DemangledQualifiedName& name, Platform* platform) + string& out, const DemangledQualifiedName& name, Platform& platform) { if (name.empty()) return; @@ -1107,14 +1134,14 @@ void DemangledTypeNode::AppendNamePartList( } -void DemangledTypeNode::AppendTypeName(string& out, Platform* platform) const +void DemangledTypeNode::AppendTypeName(string& out, Platform& platform) const { if (auto payload = std::get_if(&m_payload)) AppendNamePartList(out, payload->name, platform); } -string DemangledTypeNode::GetStringBeforeName(Platform* platform) const +string DemangledTypeNode::GetStringBeforeName(Platform& platform) const { string out; AppendBeforeName(out, nullptr, platform); @@ -1122,7 +1149,7 @@ string DemangledTypeNode::GetStringBeforeName(Platform* platform) const } -string DemangledTypeNode::GetStringAfterName(Platform* platform) const +string DemangledTypeNode::GetStringAfterName(Platform& platform) const { string out; AppendAfterName(out, nullptr, platform); @@ -1130,7 +1157,7 @@ string DemangledTypeNode::GetStringAfterName(Platform* platform) const } -void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* parentType, Platform* platform) const +void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* parentType, Platform& platform) const { switch (GetPayloadClass()) { @@ -1325,7 +1352,7 @@ void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* p } -void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* parentType, Platform* platform) const +void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* parentType, Platform& platform) const { switch (GetPayloadClass()) { @@ -1375,7 +1402,7 @@ void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* pa const auto& payload = std::get(m_payload); if (parentType && parentType->GetPayloadClass() == PointerTypeClass) out += ")"; - out += fmt::bnformat("[{:#x}]", payload.elements); + out += fmt::format("[{:#x}]", payload.elements); if (payload.childType) payload.childType->AppendAfterName(out, this, platform); break; @@ -1386,7 +1413,7 @@ void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* pa } -void DemangledTypeNode::AppendString(string& out, Platform* platform) const +void DemangledTypeNode::AppendString(string& out, Platform& platform) const { AppendBeforeName(out, nullptr, platform); size_t beforeEnd = out.size(); // track where "before" ends @@ -1409,7 +1436,7 @@ void DemangledTypeNode::AppendString(string& out, Platform* platform) const } -string DemangledTypeNode::GetString(Platform* platform) const +string DemangledTypeNode::GetString(Platform& platform) const { string out; AppendString(out, platform); @@ -1417,7 +1444,7 @@ string DemangledTypeNode::GetString(Platform* platform) const } -string DemangledTypeNode::GetTypeAndName(const StringList& name, Platform* platform) const +string DemangledTypeNode::GetTypeAndName(const StringList& name, Platform& platform) const { const string before = GetStringBeforeName(platform); const string qName = JoinNameList(name); @@ -1451,7 +1478,7 @@ uint8_t DemangledTypeNode::GetValueConfidence() const } -Ref DemangledTypeNode::Finalize(Platform* platform) const +Ref DemangledTypeNode::Finalize(Platform& platform) const { switch (GetPayloadClass()) { diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index 5e2ffa385..34d348bf2 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -54,6 +54,8 @@ namespace BinaryNinja { class Platform; } using StringList = _STD_VECTOR<_STD_STRING>; +BN::Platform& GetDemanglerFallbackPlatform(); + class DemangledTypeNode; struct DemangledTypeNodeParam @@ -84,8 +86,8 @@ class DemangledNamePart void SetTemplateArguments(_STD_VECTOR args, bool spaceAfterComma = false); void ClearTemplateArguments(); - void AppendString(_STD_STRING& out, BN::Platform* platform) const; - _STD_STRING GetString(BN::Platform* platform = nullptr) const; + void AppendString(_STD_STRING& out, BN::Platform& platform) const; + _STD_STRING GetString(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; bool IsStructurallyEqual(const DemangledNamePart& other) const; static Ref CreateShared(DemangledNamePart part); @@ -170,7 +172,7 @@ class DemangledTypeNode bool GetWideCharTypeInfo(size_t& width, std::string_view& altName) const; bool GetPointerChildType(const DemangledTypeNode*& childType, BNReferenceType& referenceType) const; void SetParenthesizedMemberPointer(bool parenthesized); - StringList RenderTypeNameSegments(BN::Platform* platform = nullptr) const; + StringList RenderTypeNameSegments(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; bool IsStructurallyEqual(const DemangledTypeNode& other) const; bool MutateChildTypes(const std::function& mutator); bool MutateQualifiedNames(const std::function& mutator); @@ -188,13 +190,13 @@ class DemangledTypeNode void SetNTRType(BNNamedTypeReferenceClass cls); void SetImplicitThisParameter(DemangledTypeNode type); - void AppendString(_STD_STRING& out, BN::Platform* platform) const; - _STD_STRING GetString(BN::Platform* platform = nullptr) const; - _STD_STRING GetStringBeforeName(BN::Platform* platform) const; - _STD_STRING GetStringAfterName(BN::Platform* platform) const; - _STD_STRING GetTypeAndName(const StringList& name, BN::Platform* platform = nullptr) const; + void AppendString(_STD_STRING& out, BN::Platform& platform) const; + _STD_STRING GetString(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + _STD_STRING GetStringBeforeName(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + _STD_STRING GetStringAfterName(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + _STD_STRING GetTypeAndName(const StringList& name, BN::Platform& platform = GetDemanglerFallbackPlatform()) const; - BN::Ref Finalize(BN::Platform* platform = nullptr) const; + BN::Ref Finalize(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; private: struct VoidPayload {}; @@ -297,7 +299,7 @@ class DemangledTypeNode uint8_t GetValueConfidence() const; BNTypeClass GetPayloadClass() const; NodeRef GetPrimaryChild() const; - static size_t ResolveWidth(size_t width, WidthKind widthKind, const BN::Platform* platform = nullptr); + static size_t ResolveWidth(size_t width, WidthKind widthKind, const BN::Platform& platform = GetDemanglerFallbackPlatform()); BNNameType m_nameType; uint8_t m_pointerSuffixBits; @@ -310,14 +312,14 @@ class DemangledTypeNode static uint8_t PointerSuffixBit(BNPointerSuffix ps); void AddPointerSuffixes(BN::TypeBuilder& tb, bool omitPtr64 = true) const; bool HasPostfixType() const; - void AppendPostfixType(_STD_STRING& out, BN::Platform* platform) const; - void AppendUnaryExpression(_STD_STRING& out, BN::Platform* platform) const; - void AppendBinaryExpression(_STD_STRING& out, BN::Platform* platform) const; + void AppendPostfixType(_STD_STRING& out, BN::Platform& platform) const; + void AppendUnaryExpression(_STD_STRING& out, BN::Platform& platform) const; + void AppendBinaryExpression(_STD_STRING& out, BN::Platform& platform) const; void AppendModifiers(_STD_STRING& out) const; void AppendPointerSuffix(_STD_STRING& out) const; static void AppendNamePartList(_STD_STRING& out, const DemangledQualifiedName& name, - BN::Platform* platform); - void AppendTypeName(_STD_STRING& out, BN::Platform* platform) const; - void AppendBeforeName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform* platform) const; - void AppendAfterName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform* platform) const; + BN::Platform& platform); + void AppendTypeName(_STD_STRING& out, BN::Platform& platform) const; + void AppendBeforeName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform& platform) const; + void AppendAfterName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform& platform) const; }; diff --git a/demangler/gnu3/CMakeLists.txt b/demangler/gnu3/CMakeLists.txt index 9c1ea197c..d39244d0c 100644 --- a/demangler/gnu3/CMakeLists.txt +++ b/demangler/gnu3/CMakeLists.txt @@ -9,7 +9,8 @@ file(GLOB SOURCES CONFIGURE_DEPENDS ../demangled_template_simplifier.cpp ../demangled_template_simplifier.h ../demangled_type_node.cpp - ../demangled_type_node.h) + ../demangled_type_node.h + ../nesting_guard.h) if(DEMO) add_library(${PROJECT_NAME} STATIC ${SOURCES}) diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 152f1dce0..6af770709 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -30,7 +30,6 @@ using namespace std; #endif -static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 1024; static constexpr size_t MAX_DEMANGLE_NODE_LENGTH = 8192; static BNTypeClass GetFinalizedTypeClass(const Ref& type) @@ -382,12 +381,11 @@ string DemangleGNU3Reader::ReadString(size_t count) // ===== DemangleGNU3 implementation ===== -DemangleGNU3::DemangleGNU3(Platform* platform, const string& mangledName) : +DemangleGNU3::DemangleGNU3(Platform& platform, const string& mangledName) : m_reader(mangledName), m_platform(platform), m_lastTypeRef(nullptr), m_isParameter(false), - m_shouldDeleteReader(true), m_topLevel(true), m_isOperatorOverload(false), m_parsingLambdaParams(false), @@ -399,38 +397,16 @@ DemangleGNU3::DemangleGNU3(Platform* platform, const string& mangledName) : MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); } - -DemangleGNU3::NestingGuard::NestingGuard(DemangleGNU3& demangler) : m_demangler(demangler) -{ - m_demangler.m_nestingDepth++; - if (m_demangler.m_nestingDepth > MAX_DEMANGLE_NESTING_DEPTH) - { - m_demangler.m_nestingDepth--; - throw DemangleException("Detected adversarial mangled string"); - } -} - - -DemangleGNU3::NestingGuard::~NestingGuard() -{ - m_demangler.m_nestingDepth--; -} - - -void DemangleGNU3::Reset(Platform* platform, const string& mangledName) +void DemangleGNU3::Reset(Platform& platform, const string& mangledName) { m_reader.Reset(mangledName); - m_platform = platform; + m_platform = std::ref(platform); m_substitute.clear(); m_templateSubstitute.clear(); m_functionSubstitute.clear(); m_lastTypeRef = nullptr; m_lastName.clear(); - m_nameType = {}; - m_localType = {}; - m_hasReturnType = {}; m_isParameter = false; - m_shouldDeleteReader = true; m_topLevel = true; m_isOperatorOverload = false; m_parsingLambdaParams = false; @@ -665,7 +641,7 @@ string DemangleGNU3::DemangleSourceName() DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); indent(); MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); bool old_isparam; @@ -812,7 +788,7 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef DemangledTypeNode DemangleGNU3::DemangleType() { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); indent(); MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); m_lastTypeRef = nullptr; @@ -1315,7 +1291,7 @@ string DemangleGNU3::DemanglePrimaryExpression() DemangledTypeNode t = DemangleSymbol(tmpList); m_topLevel = oldTopLevel; m_templateSubstitute = std::move(savedTemplateSubstitute); - out += t.GetTypeAndName(tmpList, m_platform.GetPtr()); + out += t.GetTypeAndName(tmpList, m_platform); dedent() return out; } @@ -1330,7 +1306,7 @@ string DemangleGNU3::DemanglePrimaryExpression() DemangledTypeNode t2 = DemangleSymbol(tmpList); m_topLevel = oldTopLevel; m_templateSubstitute = std::move(savedTemplateSubstitute2); - out += t2.GetTypeAndName(tmpList, m_platform.GetPtr()); + out += t2.GetTypeAndName(tmpList, m_platform); dedent(); return out; } @@ -2421,7 +2397,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); indent(); MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); const string lastName = m_lastName; @@ -2439,7 +2415,7 @@ void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bool pushBareTemplatePrefix) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); /* This can be either a qualified name like: "foo::bar::bas" or it can be a qualified type like: "foo::bar::bas & const" thus we return either @@ -2597,7 +2573,7 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo DemangledTypeNode DemangleGNU3::DemangleLocalName() { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); indent(); MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode type; @@ -2617,7 +2593,7 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() m_inLocalName = savedInLocalName; if (varName.size() > 0) - varName.back() += type.GetStringAfterName(m_platform.GetPtr()); + varName.back() += type.GetStringAfterName(m_platform); else varName.push_back(type.GetString()); @@ -2674,7 +2650,7 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() DemangledTypeNode DemangleGNU3::DemangleName() { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); indent(); MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); /* @@ -2763,7 +2739,7 @@ DemangledTypeNode DemangleGNU3::DemangleName() DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplifyTemplates) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); indent(); MyLogDebug("%s: %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode returnType; @@ -2819,7 +2795,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform.GetPtr())}); + StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform)}); } case 'V': { @@ -2869,7 +2845,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif { // Guard variable (original behavior) DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); - varName.push_back("guard_variable_for_" + t.GetTypeAndName(name, m_platform.GetPtr())); + varName.push_back("guard_variable_for_" + t.GetTypeAndName(name, m_platform)); type = DemangledTypeNode::IntegerType(1, false); if (m_reader.Length() == 0) return type; @@ -3060,7 +3036,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); + StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'C': { @@ -3088,7 +3064,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); + StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'H': // TLS init function { @@ -3097,7 +3073,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform.GetPtr())}); + StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform)}); } case 'I': return DemangledTypeNode::NamedType(UnknownNamedTypeClass, @@ -3131,7 +3107,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); + StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'V': //Vtable return DemangledTypeNode::NamedType(StructNamedTypeClass, @@ -3143,7 +3119,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform.GetPtr())}); + StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform)}); } default: throw DemangleException(); @@ -3186,7 +3162,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif const bool nameRequiresReturnType = m_isParameter || LastTypeNameSegmentHasTemplateArguments(type); if (simplifyTemplates) DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); - varName = type.RenderTypeNameSegments(m_platform.GetPtr()); + varName = type.RenderTypeNameSegments(m_platform); if (m_isOperatorOverload || type.GetNameType() == ConstructorNameType || type.GetNameType() == DestructorNameType) @@ -3276,8 +3252,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); // PrintTables(); - MyLogDebug("Done: %s%s%s\n", type.GetStringBeforeName(m_platform.GetPtr()).c_str(), JoinNameSegments(varName).c_str(), - type.GetStringAfterName(m_platform.GetPtr()).c_str()); + MyLogDebug("Done: %s%s%s\n", type.GetStringBeforeName(m_platform).c_str(), JoinNameSegments(varName).c_str(), + type.GetStringAfterName(m_platform).c_str()); dedent(); return type; @@ -3336,7 +3312,7 @@ bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) namespace { static bool DemangleStringGNU3Segments( - Platform* platform, const string& name, Ref& outType, StringList& outVarName, + Platform& platform, const string& name, Ref& outType, StringList& outVarName, bool simplifyTemplates = false) { // Handle _block_invoke[.N] and _block_invoke_N suffixes (Clang/Apple block invocations). @@ -3456,7 +3432,7 @@ namespace } -bool DemangleGNU3Static::DemangleStringGNU3(Platform* platform, const string& name, Ref& outType, +bool DemangleGNU3Static::DemangleStringGNU3(Platform& platform, const string& name, Ref& outType, QualifiedName& outVarName, bool simplifyTemplates) { StringList outVarNameSegments; @@ -3470,10 +3446,13 @@ bool DemangleGNU3Static::DemangleStringGNU3(Platform* platform, const string& na bool DemangleGNU3Static::DemangleStringGNU3(Architecture* arch, const string& name, Ref& outType, QualifiedName& outVarName, bool simplifyTemplates) { - Ref platform; if (arch) - platform = arch->GetStandalonePlatform(); - return DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName, simplifyTemplates); + { + auto platform = arch->GetStandalonePlatform(); + if (platform) + return DemangleStringGNU3(*platform, name, outType, outVarName, simplifyTemplates); + } + return DemangleStringGNU3(GetDemanglerFallbackPlatform(), name, outType, outVarName, simplifyTemplates); } @@ -3485,7 +3464,7 @@ bool DemangleGNU3Static::DemangleStringGNU3(Architecture* arch, const string& na class GNU3Demangler: public Demangler { public: - GNU3Demangler(): Demangler("GNU3") + GNU3Demangler(): Demangler("gnu3") { } ~GNU3Demangler() override {} @@ -3495,44 +3474,11 @@ class GNU3Demangler: public Demangler return DemangleGNU3Static::IsGNU3MangledString(name); } -#ifdef BINARYNINJACORE_LIBRARY - virtual bool Demangle(Architecture* arch, const string& name, Ref& outType, QualifiedName& outVarName, - BinaryView* view) override -#else - virtual bool Demangle(Ref arch, const string& name, Ref& outType, QualifiedName& outVarName, - Ref view, bool simplify) override -#endif + virtual bool Demangle(const string& name, const DemanglerConfig& config, DemanglerResult& result) override { - if (view) - { - auto platform = view->GetDefaultPlatform(); - if (platform) -#ifdef BINARYNINJACORE_LIBRARY - return DemangleGNU3Static::DemangleStringGNU3(platform, name, outType, outVarName); -#else - return DemangleGNU3Static::DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName, simplify); -#endif - } -#ifndef BINARYNINJACORE_LIBRARY - return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName, simplify); -#else - return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName); -#endif + return DemangleGNU3Static::DemangleStringGNU3(config.GetPlatform(), name, result.type, result.name, + config.simplifyTemplates); } - -#ifdef BINARYNINJACORE_LIBRARY - virtual bool DemangleWithOptions(Architecture* arch, const string& name, Ref& outType, - QualifiedName& outVarName, BinaryView* view, bool simplify) override - { - if (view) - { - auto platform = view->GetDefaultPlatform(); - if (platform) - return DemangleGNU3Static::DemangleStringGNU3(platform, name, outType, outVarName, simplify); - } - return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName, simplify); - } -#endif }; diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index d4352d2c7..31e5cbe4b 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -13,8 +13,7 @@ // limitations under the License. #pragma once -#include -#include +#include #include // XXX: Compiled directly into the core for performance reasons @@ -37,14 +36,7 @@ #endif #include "demangler/demangled_type_node.h" - -class DemangleException: public std::exception -{ - _STD_STRING m_message; -public: - DemangleException(_STD_STRING msg="Attempt to read beyond bounds or missing expected character"): m_message(msg){} - virtual const char* what() const noexcept { return m_message.c_str(); } -}; +#include "demangler/nesting_guard.h" class DemangleGNU3Reader { @@ -120,17 +112,13 @@ class DemangleGNU3 using NodeRefList = _STD_VECTOR; DemangleGNU3Reader m_reader; - BN::Ref m_platform; + std::reference_wrapper m_platform; NodeRefList m_substitute; NodeRefList m_templateSubstitute; _STD_VECTOR m_functionSubstitute; NodeRef m_lastTypeRef; _STD_STRING m_lastName; - BNNameType m_nameType; - bool m_localType; - bool m_hasReturnType; bool m_isParameter; - bool m_shouldDeleteReader; bool m_topLevel; bool m_isOperatorOverload; bool m_parsingLambdaParams; @@ -149,13 +137,7 @@ class DemangleGNU3 NodeRef typeRef; }; _STD_VECTOR m_pendingForwardRefs; - class NestingGuard - { - DemangleGNU3& m_demangler; - public: - NestingGuard(DemangleGNU3& demangler); - ~NestingGuard(); - }; + using NestingGuard = DemangleNestingGuard; void ResolveForwardTemplateRefs(DemangledTypeNode& type, const ParamList& args); enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name}; StringList DemangleBaseUnresolvedName(); @@ -211,8 +193,8 @@ class DemangleGNU3 #endif public: - DemangleGNU3(BN::Platform* platform, const _STD_STRING& mangledName); - void Reset(BN::Platform* platform, const _STD_STRING& mangledName); + DemangleGNU3(BN::Platform& platform, const _STD_STRING& mangledName); + void Reset(BN::Platform& platform, const _STD_STRING& mangledName); DemangledTypeNode DemangleSymbol(StringList& varName, bool simplifyTemplates = false); }; @@ -223,7 +205,7 @@ class DemangleGNU3Static static bool IsGNU3MangledString(const _STD_STRING& name); static bool DemangleGlobalHeader(_STD_STRING& name, _STD_STRING& header); - static bool DemangleStringGNU3(BN::Platform* platform, const _STD_STRING& name, BN::Ref& outType, + static bool DemangleStringGNU3(BN::Platform& platform, const _STD_STRING& name, BN::Ref& outType, BN::QualifiedName& outVarName, bool simplifyTemplates = false); static bool DemangleStringGNU3(BN::Architecture* arch, const _STD_STRING& name, BN::Ref& outType, BN::QualifiedName& outVarName, bool simplifyTemplates = false); diff --git a/demangler/msvc/CMakeLists.txt b/demangler/msvc/CMakeLists.txt index 8177ef1ea..7e635d6c5 100644 --- a/demangler/msvc/CMakeLists.txt +++ b/demangler/msvc/CMakeLists.txt @@ -8,6 +8,7 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.h ../demangled_type_node.cpp ../demangled_type_node.h + ../nesting_guard.h ../demangled_template_simplifier.cpp ../demangled_template_simplifier.h) diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index b3f7a08de..52ab12e0d 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -17,6 +17,7 @@ #include "demangle_msvc.h" #include "demangler/demangled_template_simplifier.h" +#include "base/unicode.h" #ifdef BINARYNINJACORE_LIBRARY #include "unicode.h" #endif @@ -34,8 +35,6 @@ using namespace std; #endif -// The largest observed depth in a real-world corpus of roughly 200k MSVC symbols was 54. -static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 256; static constexpr size_t MAX_ENCODED_NUMBER_HEX_DIGITS = 16; static constexpr size_t MAX_BACKREFS = 10; @@ -241,61 +240,19 @@ void Demangle::BackrefContextSwitch::Swap(BackrefList& left, BackrefList& right) -Demangle::Demangle(Architecture* arch, _STD_STRING mangledName) : +Demangle::Demangle(Platform& platform, _STD_STRING mangledName) : m_mangledName(std::move(mangledName)), m_reader(m_mangledName), - m_arch(arch), - m_platform(nullptr), - m_view(nullptr) + m_platform(platform) { } - -Demangle::Demangle(Ref platform, _STD_STRING mangledName) : - m_mangledName(std::move(mangledName)), - m_reader(m_mangledName), - m_arch(nullptr), - m_platform(std::move(platform)), - m_view(nullptr) -{ -} - - -Demangle::Demangle(Ref view, _STD_STRING mangledName) : - m_mangledName(std::move(mangledName)), - m_reader(m_mangledName), - m_arch(nullptr), - m_platform(nullptr), - m_view(std::move(view)) -{ -} - - -Demangle::NestingGuard::NestingGuard(Demangle& demangler) : m_demangler(demangler) -{ - m_demangler.m_nestingDepth++; - if (m_demangler.m_nestingDepth > MAX_DEMANGLE_NESTING_DEPTH) - { - m_demangler.m_nestingDepth--; - throw DemangleException("Detected adversarial mangled string"); - } -} - - -Demangle::NestingGuard::~NestingGuard() -{ - m_demangler.m_nestingDepth--; -} - - -void Demangle::Reset(Architecture* arch, const _STD_STRING& mangledName) +void Demangle::Reset(Platform& platform, const _STD_STRING& mangledName) { m_mangledName = mangledName; m_reader.Reset(m_mangledName); m_backrefList.Clear(); - m_arch = arch; - m_platform = nullptr; - m_view = nullptr; + m_platform = std::ref(platform); m_templateParamDepth = 0; m_nestingDepth = 0; } @@ -328,13 +285,19 @@ void Demangle::RewriteTemplateBackrefName(NameList& typeName, const BackrefList& _STD_STRING Demangle::FormatTypeAndName(const DemangledTypeNode& type, const NameList& name) const { StringList nameSegments = FinalizeNameList(name); + Platform& platform = GetRenderingPlatform(); if (type.GetNameType() == OperatorReturnTypeNameType) { - Ref finalizedType = type.Finalize(m_platform.GetPtr()); + Ref finalizedType = type.Finalize(platform); if (finalizedType) return finalizedType->GetTypeAndName(QualifiedName(nameSegments)); } - return type.GetTypeAndName(nameSegments); + return type.GetTypeAndName(nameSegments, platform); +} + +Platform& Demangle::GetRenderingPlatform() const +{ + return m_platform; } DemangledTypeNode Demangle::DemangleReferencedSymbolValue(BackrefList& varList) @@ -369,7 +332,7 @@ DemangledTypeNode Demangle::DemangleAutoNonTypeTemplateParam(BackrefList& varLis DemangledTypeNode Demangle::DemangleVarType(BackrefList& varList, bool isReturn, bool includeImplicitThis, DemangledTypeNode::NodeRef* outTypeBackref, TypeBackrefMode typeBackrefMode) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); MSVC_TRACE("{}: '{}' - {}", __FUNCTION__, m_reader.GetRaw(), varList.nameList.size()); if (outTypeBackref) *outTypeBackref = nullptr; @@ -1354,7 +1317,7 @@ DemangledNamePart Demangle::DemangleTemplateInstantiationNameInLocalContext(Back void Demangle::DemangleTemplateParams(_STD_VECTOR& params, BackrefList& nameBackrefList, DemangledNamePart& out) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); params.clear(); const bool nestedTemplateContext = (m_templateParamDepth > 0); struct NameBackrefScopeGuard @@ -1656,9 +1619,10 @@ bool Demangle::FunctionTypeHasPointerSuffix(char functionType) } -_STD_STRING Demangle::FormatFunctionScopeSignature(const DemangledTypeNode& type, const NameList& scopeName) +_STD_STRING Demangle::FormatFunctionScopeSignature( + const DemangledTypeNode& type, const NameList& scopeName, Platform& platform) { - _STD_STRING out = type.GetTypeAndName(FinalizeNameList(scopeName)); + _STD_STRING out = type.GetTypeAndName(FinalizeNameList(scopeName), platform); while (!out.empty() && out.back() == ' ') out.pop_back(); return out; @@ -1689,7 +1653,8 @@ void Demangle::AppendLocalScope(NameList& nameList, BackrefList& nameBackrefList scopeFunctionType, FunctionTypeHasPointerSuffix(ft), nameBackrefList).type; PrependNameComponent(nameList, MakeNameSegment("`" + to_string(scopeOrdinal) + "'")); - PrependNameComponent(nameList, MakeNameSegment("`" + FormatFunctionScopeSignature(scopeType, scopeName) + "'")); + PrependNameComponent( + nameList, MakeNameSegment("`" + FormatFunctionScopeSignature(scopeType, scopeName, GetRenderingPlatform()) + "'")); } @@ -1759,7 +1724,7 @@ void Demangle::DemangleName(NameList& nameList, BackrefList& nameBackrefList, bool typeNameContext) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); // NameList is stored outermost-first for QualifiedName, but MSVC encodes // names leaf-first. Ordinary parsed components are prepended; constructor // and destructor branches recurse to parse the class scope, then append the @@ -1783,7 +1748,7 @@ void Demangle::DemangleName(NameList& nameList, } else if (m_reader.ConsumeIf("??")) { - if (m_nestingDepth + strippedNestedNamePrefixes >= MAX_DEMANGLE_NESTING_DEPTH) + if (m_nestingDepth + strippedNestedNamePrefixes >= MaxDemangleNestingDepth) throw DemangleException("Demangle nesting depth exceeded"); strippedNestedNamePrefixes++; continue; @@ -2120,7 +2085,7 @@ void Demangle::ApplySymbolFunctionContext(DemangledFunction& function, NameList& Demangle::DemangledFunction Demangle::DemangleFunction(BNNameType classFunctionType, bool pointerSuffix, BackrefList& nameBackrefList, int funcClass) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false; uint8_t suffix = 0; @@ -2440,7 +2405,7 @@ Demangle::DemangleContext Demangle::DemangleSymbol() Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); BNNameType classFunctionType = NoNameType; NameList varName; @@ -2611,59 +2576,23 @@ Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) return finishContext(); } -std::pair, QualifiedName> Demangle::Finalize(BinaryView* view, bool simplifyTemplates) +std::pair, QualifiedName> Demangle::Finalize(bool simplifyTemplates) { DemangleContext context = DemangleSymbol(); if (m_reader.Length() != 0) LogDebugF("Demangling Succeeded with trailing characters '{}' in '{}'", m_reader.GetRaw(), m_mangledName); - Ref platform = m_platform; - if (!platform && view) - platform = view->GetDefaultPlatform(); - - Architecture* arch = m_arch; -#ifdef BINARYNINJACORE_LIBRARY - if (!arch && platform) - arch = platform->GetArchitecture(); - if (!arch && view) - arch = view->GetDefaultArchitecture(); -#else - Ref viewArch; - Ref platformArch; - if (!arch && platform) - { - platformArch = platform->GetArchitecture(); - arch = platformArch.GetPtr(); - } - if (!arch && view) - { - viewArch = view->GetDefaultArchitecture(); - arch = viewArch.GetPtr(); - } -#endif - if (!arch) - throw DemangleException(); - - if (!platform) - platform = arch->GetStandalonePlatform(); - if (simplifyTemplates) { DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(context.type); DemangledTemplateSimplifier::SimplifyNameSegmentsInPlace(context.name); } - return {context.type.Finalize(platform.GetPtr()), QualifiedName(FinalizeNameList(context.name))}; -} - -std::pair, QualifiedName> Demangle::Finalize(bool simplifyTemplates) -{ - return Finalize(m_view.GetPtr(), simplifyTemplates); + return {context.type.Finalize(m_platform), QualifiedName(FinalizeNameList(context.name))}; } -template -static bool DemangleMSImpl(const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName, - DemangleBody&& demangleBody) +static bool DemangleMSWithPlatform(Platform& platform, const _STD_STRING& mangledName, Ref& outType, + QualifiedName& outVarName, bool simplifyTemplates) { outType = nullptr; if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) @@ -2671,7 +2600,9 @@ static bool DemangleMSImpl(const _STD_STRING& mangledName, Ref& outType, Q try { - auto result = demangleBody(); + thread_local Demangle demangle(platform, mangledName); + demangle.Reset(platform, mangledName); + auto result = demangle.Finalize(simplifyTemplates); outType = std::move(result.first); outVarName = std::move(result.second); return true; @@ -2688,111 +2619,11 @@ static bool DemangleMSImpl(const _STD_STRING& mangledName, Ref& outType, Q } } -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, const Ref& view) -{ - return DemangleMS(arch, mangledName, outType, outVarName, view, false); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, const Ref& view, bool simplifyTemplates) -{ - if (view) - { - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - Demangle demangle(arch, mangledName); - return demangle.Finalize(view.GetPtr(), simplifyTemplates); - }); - } - return DemangleMS(arch, mangledName, outType, outVarName, simplifyTemplates); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view) -{ - return DemangleMS(arch, mangledName, outType, outVarName, view, false); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view, bool simplifyTemplates) -{ - if (view) - return DemangleMS(arch, mangledName, outType, outVarName, Ref(view), simplifyTemplates); - return DemangleMS(arch, mangledName, outType, outVarName, simplifyTemplates); -} - -bool Demangle::DemangleMS(Platform* platform, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName) -{ - return DemangleMS(platform, mangledName, outType, outVarName, false); -} - -bool Demangle::DemangleMS(Platform* platform, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, bool simplifyTemplates) -{ - outType = nullptr; - if (!platform) - return false; - - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - Demangle demangle(Ref(platform), mangledName); - return demangle.Finalize(simplifyTemplates); - }); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName) -{ - return DemangleMS(arch, mangledName, outType, outVarName, false); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, bool simplifyTemplates) -{ - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - thread_local Demangle demangle(arch, mangledName); - demangle.Reset(arch, mangledName); - return demangle.Finalize(simplifyTemplates); - }); -} - - -bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, const Ref& view) -{ - return DemangleMS(mangledName, outType, outVarName, view, false); -} - -bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, const Ref& view, bool simplifyTemplates) -{ - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - // Can't use thread_local here — BinaryView overload needs platform/view state - Demangle demangle(view, mangledName); - return demangle.Finalize(simplifyTemplates); - }); -} - -bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view) -{ - return DemangleMS(mangledName, outType, outVarName, view, false); -} - -bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view, bool simplifyTemplates) -{ - outType = nullptr; - if (!view) - return false; - return DemangleMS(mangledName, outType, outVarName, Ref(view), simplifyTemplates); -} - class MSDemangler: public Demangler { public: - MSDemangler(): Demangler("MS") + MSDemangler(): Demangler("msvc") { } ~MSDemangler() override = default; @@ -2802,34 +2633,10 @@ class MSDemangler: public Demangler return !name.empty() && (name[0] == '?' || name[0] == '.'); } -#ifdef BINARYNINJACORE_LIBRARY - bool Demangle(Architecture* arch, const _STD_STRING& name, Ref& outType, QualifiedName& outVarName, - BinaryView* view) override -#else - virtual bool Demangle(Ref arch, const _STD_STRING& name, Ref& outType, QualifiedName& outVarName, - Ref view, bool simplify) override -#endif - { -#ifdef BINARYNINJACORE_LIBRARY - if (view) - return Demangle::DemangleMS(arch, name, outType, outVarName, view); - return Demangle::DemangleMS(arch, name, outType, outVarName); -#else - if (view) - return Demangle::DemangleMS(arch, name, outType, outVarName, view, simplify); - return Demangle::DemangleMS(arch, name, outType, outVarName, simplify); -#endif - } - -#ifdef BINARYNINJACORE_LIBRARY - bool DemangleWithOptions(Architecture* arch, const _STD_STRING& name, Ref& outType, - QualifiedName& outVarName, BinaryView* view, bool simplify) override + bool Demangle(const _STD_STRING& name, const DemanglerConfig& config, DemanglerResult& result) override { - if (view) - return Demangle::DemangleMS(arch, name, outType, outVarName, view, simplify); - return Demangle::DemangleMS(arch, name, outType, outVarName, simplify); + return DemangleMSWithPlatform(config.GetPlatform(), name, result.type, result.name, config.simplifyTemplates); } -#endif }; extern "C" diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h index 91882e1aa..edfc0bada 100644 --- a/demangler/msvc/demangle_msvc.h +++ b/demangler/msvc/demangle_msvc.h @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -#include +#include #include #include @@ -37,15 +37,7 @@ #endif #include "demangler/demangled_type_node.h" - -class DemangleException: public std::exception -{ - _STD_STRING m_message; -public: - DemangleException(_STD_STRING msg="Attempt to read beyond bounds or missing expected character"): m_message(std::move(msg)){} - [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } -}; - +#include "demangler/nesting_guard.h" class Demangle { @@ -241,25 +233,19 @@ class Demangle _STD_STRING m_mangledName; // Owns the string; Reader points into it Reader m_reader; BackrefList m_backrefList; - BN::Architecture* m_arch; - BN::Ref m_platform; - BN::Ref m_view; + std::reference_wrapper m_platform; size_t m_templateParamDepth = 0; size_t m_nestingDepth = 0; - class NestingGuard - { - Demangle& m_demangler; - public: - NestingGuard(Demangle& demangler); - ~NestingGuard(); - }; + using NestingGuard = DemangleNestingGuard; static void RewriteTemplateBackrefName(NameList& typeName, const BackrefList& nameBackrefList); static void PrependNameComponent(NameList& nameList, DemangledNamePart name); void AppendStringName(NameList& nameList, BackrefList& nameBackrefList); static void FinalizeConstructorTemplateName(NameList& nameList, size_t nameListSizeAtEntry, bool pending); static bool FunctionTypeHasPointerSuffix(char functionType); - static _STD_STRING FormatFunctionScopeSignature(const DemangledTypeNode& type, const NameList& scopeName); + static _STD_STRING FormatFunctionScopeSignature( + const DemangledTypeNode& type, const NameList& scopeName, BN::Platform& platform); + BN::Platform& GetRenderingPlatform() const; void AppendLocalScope(NameList& nameList, BackrefList& nameBackrefList, uint64_t scopeOrdinal, bool typeNameContext); bool TryAppendLocalScopeAt(NameList& nameList, BackrefList& nameBackrefList, const char* encodedNumberStart, bool typeNameContext); @@ -339,46 +325,14 @@ class Demangle DemangledTypeNode DemangleTypeInfoName(NameList& symbolName); DemangleContext DemangleDynamicInitFini(bool isDtor, BackrefList& backrefList); DemangleContext DemangleSymbol(BackrefList& backrefList); - std::pair, BN::QualifiedName> Finalize(BN::BinaryView* view, bool simplifyTemplates = false); public: - Demangle(BN::Architecture* arch, _STD_STRING mangledName); - Demangle(BN::Ref view, _STD_STRING mangledName); - Demangle(BN::Ref platform, _STD_STRING mangledName); + Demangle(BN::Platform& platform, _STD_STRING mangledName); + void Reset(BN::Platform& platform, const _STD_STRING& mangledName); Demangle(const Demangle&) = delete; Demangle(Demangle&&) = delete; Demangle& operator=(const Demangle&) = delete; Demangle& operator=(Demangle&&) = delete; - void Reset(BN::Architecture* arch, const _STD_STRING& mangledName); DemangleContext DemangleSymbol(); std::pair, BN::QualifiedName> Finalize(bool simplifyTemplates = false); - - // Be careful not to accidentally implicitly cast a BinaryView* to a bool - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, const BN::Ref& view); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, const BN::Ref& view, - bool simplifyTemplates); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, BN::BinaryView* view); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, BN::BinaryView* view, bool simplifyTemplates); - static bool DemangleMS(BN::Platform* platform, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName); - static bool DemangleMS(BN::Platform* platform, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, bool simplifyTemplates); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, bool simplifyTemplates); - - static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, const BN::Ref& view); - static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, const BN::Ref& view, - bool simplifyTemplates); - static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, BN::BinaryView* view); - static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, BN::BinaryView* view, bool simplifyTemplates); }; diff --git a/demangler/nesting_guard.h b/demangler/nesting_guard.h new file mode 100644 index 000000000..450ce7e67 --- /dev/null +++ b/demangler/nesting_guard.h @@ -0,0 +1,57 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// 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. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +class DemangleException: public std::exception +{ + _STD_STRING m_message; + +public: + DemangleException(_STD_STRING msg = "Attempt to read beyond bounds or missing expected character"): + m_message(std::move(msg)) + {} + + [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } +}; + +static constexpr size_t MaxDemangleNestingDepth = 1024; + +class DemangleNestingGuard +{ + size_t& m_nestingDepth; + +public: + explicit DemangleNestingGuard(size_t& nestingDepth) : m_nestingDepth(nestingDepth) + { + m_nestingDepth++; + if (m_nestingDepth > MaxDemangleNestingDepth) + { + m_nestingDepth--; + throw DemangleException("Detected adversarial mangled string"); + } + } + + ~DemangleNestingGuard() + { + m_nestingDepth--; + } + + DemangleNestingGuard(const DemangleNestingGuard&) = delete; + DemangleNestingGuard& operator=(const DemangleNestingGuard&) = delete; +}; diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index c15566dea..eeb54040f 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -39,7 +39,7 @@ use crate::PDBParserInstance; use binaryninja::architecture::{Architecture, ArchitectureExt, Register, RegisterId}; use binaryninja::binary_view::BinaryViewBase; use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; -use binaryninja::demangle::demangle_ms_with_view; +use binaryninja::demangle::demangle_ms; use binaryninja::rc::Ref; use binaryninja::types::{FunctionParameter, QualifiedName, StructureBuilder, Type, TypeClass}; use binaryninja::variable::{Variable, VariableSourceType}; @@ -1813,7 +1813,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { raw_name: &String, rva: Rva, ) -> Result<(Option>>, Option)> { - let (mut t, mut name) = match demangle_ms_with_view(&self.arch, raw_name, Some(self.bv)) { + let simplify_templates = self.settings.get_bool_with_opts( + "analysis.types.templateSimplifier", + &mut self.settings_query_opts.clone(), + ); + let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, simplify_templates) { Some((name, Some(t))) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name), Some((name, _)) => (None, name), _ => (None, QualifiedName::new(vec![raw_name.clone()])), diff --git a/plugins/rtti/itanium.cpp b/plugins/rtti/itanium.cpp index a2ddc59a4..1bd62f8ca 100644 --- a/plugins/rtti/itanium.cpp +++ b/plugins/rtti/itanium.cpp @@ -672,6 +672,7 @@ ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref &view, bool use { m_view = view; m_logger = view->CreateLogger("Itanium RTTI"); + m_simplifyTemplates = Settings::Instance()->Get("analysis.types.templateSimplifier", view); allowMangledClassNames = useMangled; checkWritableRData = checkRData; m_classInfo = {}; diff --git a/plugins/rtti/microsoft.cpp b/plugins/rtti/microsoft.cpp index ba0757cba..bba16ad98 100644 --- a/plugins/rtti/microsoft.cpp +++ b/plugins/rtti/microsoft.cpp @@ -368,7 +368,8 @@ std::vector MicrosoftRTTIProcessor::ProcessClassHierarchyDescript continue; } auto baseClassTypeDesc = TypeDescriptor(m_view, baseClassTypeDescAddr); - auto baseClassName = DemangleNameMS(m_view, allowMangledClassNames, baseClassTypeDesc.name); + auto baseClassName = DemangleNameMS( + m_view, allowMangledClassNames, baseClassTypeDesc.name, m_simplifyTemplates); if (!baseClassName.has_value()) { m_logger->LogWarnF("Skipping BaseClassDescriptor with mangled name {:#x}", baseClassTypeDescAddr); @@ -415,7 +416,7 @@ std::optional MicrosoftRTTIProcessor::ProcessRTTI(uint64_t coLocatorA // Get type descriptor then check to see if the class name was demangled. auto typeDescAddr = resolveAddr(coLocator->pTypeDescriptor); auto typeDesc = TypeDescriptor(m_view, typeDescAddr); - auto className = DemangleNameMS(m_view, allowMangledClassNames, typeDesc.name); + auto className = DemangleNameMS(m_view, allowMangledClassNames, typeDesc.name, m_simplifyTemplates); if (!className.has_value()) return std::nullopt; @@ -607,6 +608,7 @@ MicrosoftRTTIProcessor::MicrosoftRTTIProcessor(const Ref &view, bool { m_view = view; m_logger = view->CreateLogger("Microsoft RTTI"); + m_simplifyTemplates = Settings::Instance()->Get("analysis.types.templateSimplifier", view); allowMangledClassNames = useMangled; allowAnonymousClassNames = allowAnonymous; checkWritableRData = checkRData; diff --git a/plugins/rtti/rtti.cpp b/plugins/rtti/rtti.cpp index f713aef77..e5dfe9875 100644 --- a/plugins/rtti/rtti.cpp +++ b/plugins/rtti/rtti.cpp @@ -34,11 +34,12 @@ Ref RTTI::GetRealSymbol(BinaryView *view, uint64_t relocAddr, uint64_t s } -std::optional RTTI::DemangleNameMS(BinaryView* view, bool allowMangled, const std::string &mangledName) +std::optional RTTI::DemangleNameMS( + BinaryView* view, bool allowMangled, const std::string &mangledName, bool simplifyTemplates) { QualifiedName demangledName = {}; Ref outType = {}; - if (!DemangleMS(view->GetDefaultArchitecture(), mangledName, outType, demangledName, view)) + if (!DemangleMS(view->GetDefaultPlatform(), mangledName, outType, demangledName, simplifyTemplates)) return DemangleNameLLVM(allowMangled, mangledName); return NormalizeRTTIClassName(demangledName.GetString()); } @@ -62,7 +63,7 @@ std::optional RTTI::DemangleNameGNU3(BinaryView* view, bool allowMa if (adjustedMangledName.rfind("_Z", 0) != 0) adjustedMangledName = "_Z" + adjustedMangledName; - if (!DemangleGNU3(view->GetDefaultArchitecture(), adjustedMangledName, outType, demangledName, true)) + if (!DemangleGNU3(view->GetDefaultPlatform(), adjustedMangledName, outType, demangledName, true)) return allowMangled ? std::optional(mangledName) : std::nullopt; // Because we might have a generic name such as "PackageListGui::PackageListGui" returned, we must attempt to diff --git a/plugins/rtti/rtti.h b/plugins/rtti/rtti.h index 00b105eba..4a05a3a11 100644 --- a/plugins/rtti/rtti.h +++ b/plugins/rtti/rtti.h @@ -8,7 +8,8 @@ constexpr int RTTI_CONFIDENCE = 100; namespace BinaryNinja::RTTI { Ref GetRealSymbol(BinaryView *view, uint64_t relocAddr, uint64_t symAddr); - std::optional DemangleNameMS(BinaryView* view, bool allowMangled, const std::string &mangledName); + std::optional DemangleNameMS( + BinaryView* view, bool allowMangled, const std::string &mangledName, bool simplifyTemplates); std::optional DemangleNameGNU3(BinaryView* view, bool allowMangled, const std::string &mangledName); @@ -81,6 +82,7 @@ namespace BinaryNinja::RTTI { Ref m_view; Ref m_logger; + bool m_simplifyTemplates = false; std::map m_classInfo; std::map m_unhandledClassInfo; diff --git a/python/demangle.py b/python/demangle.py index c6cb0fcd9..11ee9f0d7 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -27,10 +27,33 @@ from . import binaryview from . import types from .log import log_error_for_exception -from .architecture import Architecture, CoreArchitecture +from .architecture import Architecture from .platform import Platform +from .settings import Settings from typing import Iterable, List, Optional, Union, Tuple, Any +_DEMANGLER_MSVC = "msvc" +_DEMANGLER_GNU3 = "gnu3" +_DEMANGLER_LLVM = "llvm" + + +def _demangler_config_for_context( + arch_or_platform: Union[Architecture, Platform], + view: Optional['binaryview.BinaryView'] = None, + simplify: bool = False +): + if isinstance(arch_or_platform, Architecture): + platform_obj = arch_or_platform.standalone_platform + elif isinstance(arch_or_platform, Platform): + platform_obj = arch_or_platform + else: + raise TypeError("Unexpected arch or platform type") + + config = core.BNGetDemanglerConfigForPlatform(platform_obj.handle if platform_obj else None, simplify) + if view is not None: + config.view = view.handle + return config + def get_qualified_name(names: Iterable[str]): """ @@ -82,27 +105,25 @@ def demangle_generic( raise TypeError("Unexpected arch or platform type") out_type = ctypes.POINTER(core.BNType)() - out_var_name = core.BNQualifiedName() - - view_handle = None - if view is not None: - view_handle = view.handle - - if not core.BNDemangleGeneric(arch.handle, mangled_name, out_type, out_var_name, view_handle, simplify): + result = core.BNDemanglerResult() + config = _demangler_config_for_context(archOrPlatform, view, simplify) + if not core.BNDemangle(mangled_name, config, result): return None, [mangled_name] try: - result_var_name = types.QualifiedName._from_core_struct(out_var_name) + if result.type: + out_type = core.BNNewTypeReference(result.type) + result_var_name = types.QualifiedName._from_core_struct(result.name) except UnicodeDecodeError: - core.BNFreeQualifiedName(out_var_name) if out_type: core.BNFreeType(out_type) + core.BNFreeDemanglerResult(result) return None, [mangled_name] - core.BNFreeQualifiedName(out_var_name) result_type = None if out_type: result_type = types.Type.create(handle=out_type) + core.BNFreeDemanglerResult(result) return result_type, result_var_name.name @@ -110,7 +131,7 @@ def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.Bi """ ``demangle_llvm`` demangles a mangled name using the LLVM demangler. - :param str mangled_name: a mangled (msvc/itanium/rust/dlang) name + :param str mangled_name: a mangled (msvc/gnu3/rust/dlang) name :param options: (optional) Whether to simplify demangled names : None falls back to user settings, a BinaryView uses that BinaryView's settings, or a boolean to set it directly :type options: Optional[Union[bool, BinaryView]] :return: returns demangled name or None on error @@ -121,116 +142,99 @@ def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.Bi ['public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)'] >>> """ - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if ( - isinstance(options, binaryview.BinaryView) and core.BNDemangleLLVMWithOptions( - mangled_name, ctypes.byref(outName), ctypes.byref(outSize), options.handle - ) - ) or ( - isinstance(options, bool) and core.BNDemangleLLVM( - mangled_name, ctypes.byref(outName), ctypes.byref(outSize), options - ) - ) or ( - options is None and core.BNDemangleLLVMWithOptions( - mangled_name, ctypes.byref(outName), ctypes.byref(outSize), None - ) - ): - for i in range(outSize.value): - try: - names.append(outName[i].decode('utf8')) # type: ignore - except UnicodeDecodeError: - names.append(outName[i].decode('charmap')) # type: ignore - core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) - return names - return None - - -def demangle_ms(archOrPlatform: Union[Architecture, Platform], mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = False): + view, simplify_templates = _simplify_from_compat_option(options) + config = core.BNGetDemanglerConfigForBinaryView(view.handle) if view is not None else core.BNGetDefaultDemanglerConfig() + config.simplifyTemplates = simplify_templates + + demangler = core.BNGetDemanglerByName(_DEMANGLER_LLVM) + if demangler is None: + return None + + result = core.BNDemanglerResult() + if not core.BNDemangleWithDemangler(demangler, mangled_name, config, result): + return None + + try: + return types.QualifiedName._from_core_struct(result.name).name + finally: + core.BNFreeDemanglerResult(result) + + +def _simplify_from_compat_option(simplify: Optional[Union[bool, binaryview.BinaryView]]): + if isinstance(simplify, binaryview.BinaryView): + return simplify, Settings().get_bool("analysis.types.templateSimplifier", simplify) + if simplify is None: + return None, Settings().get_bool("analysis.types.templateSimplifier") + if isinstance(simplify, bool): + return None, simplify + raise TypeError("simplify must be a bool") + + +def _demangle_type_and_name( + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: Optional[Union[bool, binaryview.BinaryView]], + demangler_name: str +): + view, simplify_templates = _simplify_from_compat_option(simplify) + + binaryninja._init_plugins() + demangler = core.BNGetDemanglerByName(demangler_name) + if demangler is None: + return (None, mangled_name) + + config = _demangler_config_for_context(arch_or_platform, view, simplify_templates) + result = core.BNDemanglerResult() + if not core.BNDemangleWithDemangler(demangler, mangled_name, config, result): + return (None, mangled_name) + + try: + result_type = None + if result.type: + result_type = types.Type.create(handle=core.BNNewTypeReference(result.type)) + result_var_name = types.QualifiedName._from_core_struct(result.name) + return (result_type, result_var_name.name) + finally: + core.BNFreeDemanglerResult(result) + + +def demangle_ms( + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: bool = False +): """ ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. - :param Union[Architecture, Platform] archOrPlatform: Architecture or Platform for the symbol. Required for pointer/integer sizes and calling conventions. + :param Union[Architecture, Platform] arch_or_platform: Architecture or Platform for the symbol. Required for pointer/integer sizes and calling conventions. :param str mangled_name: a mangled Microsoft Visual Studio C++ name - :param options: (optional) Whether to simplify demangled names : None falls back to user settings, a BinaryView uses that BinaryView's settings, or a boolean to set it directly - :type options: Optional[Union[bool, BinaryView]] + :param bool simplify: (optional) Whether to simplify demangled names :return: returns tuple of (Type, demangled_name) or (None, mangled_name) on error :rtype: Tuple[Optional[Type], Union[str, List[str]]] :Example: - >>> demangle_ms(Platform["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") (, ['Foobar', 'testf']) >>> """ - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - - demangle = core.BNDemangleMS - demangleWithOptions = core.BNDemangleMSWithOptions - - if isinstance(archOrPlatform, Platform): - demangle = core.BNDemangleMSPlatform - - if ( - isinstance(options, binaryview.BinaryView) and demangleWithOptions( - archOrPlatform.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options.handle - ) - ) or ( - isinstance(options, bool) and demangle( - archOrPlatform.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options - ) - ) or ( - options is None and demangleWithOptions( - archOrPlatform.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), None - ) - ): - for i in range(outSize.value): - names.append(outName[i].decode('utf8')) # type: ignore - core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) - if not handle: - return (None, names) - return (types.Type.create(handle), names) - return (None, mangled_name) - - -def demangle_gnu3(arch, mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = None): + return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, _DEMANGLER_MSVC) + + +def demangle_gnu3( + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: bool = False +): """ ``demangle_gnu3`` demangles a mangled name to a Type object. - :param Architecture arch: Architecture for the symbol. Required for pointer and integer sizes. + :param Union[Architecture, Platform] arch_or_platform: Architecture or Platform for the symbol. Required for pointer and integer sizes. :param str mangled_name: a mangled GNU3 name - :param options: (optional) Whether to simplify demangled names : None falls back to user settings, a BinaryView uses that BinaryView's settings, or a boolean to set it directly - :type options: Optional[Union[bool, BinaryView]] + :param bool simplify: (optional) Whether to simplify demangled names :return: returns tuple of (Type, demangled_name) or (None, mangled_name) on error :rtype: Tuple[Optional[Type], Union[str, List[str]]] """ - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if ( - isinstance(options, binaryview.BinaryView) and core.BNDemangleGNU3WithOptions( - arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options.handle - ) - ) or ( - isinstance(options, bool) and core.BNDemangleGNU3( - arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options - ) - ) or ( - options is None and core.BNDemangleGNU3WithOptions( - arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), None - ) - ): - for i in range(outSize.value): - names.append(outName[i].decode('utf8')) # type: ignore - core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) - if not handle: - return (None, names) - return (types.Type.create(handle), names) - return (None, mangled_name) + return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, _DEMANGLER_GNU3) class _DemanglerMetaclass(type): @@ -277,7 +281,7 @@ class Demangler(metaclass=_DemanglerMetaclass): The list of Demanglers can be queried: >>> list(Demangler) - [, ] + [, , ] """ name = None @@ -303,10 +307,11 @@ def register(cls): assert demangler.handle is None demangler._cb = core.BNDemanglerCallbacks() + demangler._cb.size = ctypes.sizeof(core.BNDemanglerCallbacks) demangler._cb.context = 0 demangler._cb.isMangledString = demangler._cb.isMangledString.__class__(demangler._is_mangled_string) demangler._cb.demangle = demangler._cb.demangle.__class__(demangler._demangle) - demangler._cb.freeVarName = demangler._cb.freeVarName.__class__(demangler._free_var_name) + demangler._cb.freeResult = demangler._cb.freeResult.__class__(demangler._free_result) demangler.handle = core.BNRegisterDemangler(cls.name, demangler._cb) cls._registered_demanglers.append(demangler) @@ -316,10 +321,10 @@ def promote(cls, demangler): Promote a demangler to the highest-priority position. >>> list(Demangler) - [, ] + [, , ] >>> Demangler.promote(list(Demangler)[0]) >>> list(Demangler) - [, ] + [, , ] :param demangler: Demangler to promote """ @@ -343,37 +348,50 @@ def _is_mangled_string(self, ctxt, name): log_error_for_exception("Unhandled Python exception in Demangler._is_mangled_string") return False - def _demangle(self, ctxt, arch, name, out_type, out_var_name, view, simplify): + def _demangle(self, ctxt, name, config, result): try: - api_arch = CoreArchitecture._from_cache(arch) + api_config = config.contents + api_platform = None + api_arch = None + if api_config.platform is not None: + api_platform = Platform(handle=core.BNNewPlatformReference(api_config.platform)) + api_arch = api_platform.arch api_view = None - if view is not None: - api_view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + if api_config.view is not None: + api_view = binaryview.BinaryView(handle=core.BNNewViewReference(api_config.view)) + if api_arch is None and api_view.platform is not None: + api_arch = api_view.platform.arch + + if api_arch is None: + return False - result = self.demangle(api_arch, core.pyNativeStr(name), api_view, simplify) - if result is None: + demangle_result = self.demangle(api_arch, core.pyNativeStr(name), api_view, api_config.simplifyTemplates) + if demangle_result is None: return False - type, var_name = result + type, var_name = demangle_result if not isinstance(var_name, types.QualifiedName): var_name = types.QualifiedName(var_name) - Demangler._cached_name = var_name._to_core_struct() + Demangler._cached_name = core.BNDemanglerResult() + Demangler._cached_name.name = var_name._to_core_struct() if type is not None: - out_type[0] = core.BNNewTypeReference(type.handle) + Demangler._cached_name.type = core.BNNewTypeReference(type.handle) else: - out_type[0] = None - out_var_name[0] = Demangler._cached_name + Demangler._cached_name.type = None + result[0] = Demangler._cached_name return True except Exception: log_error_for_exception("Unhandled Python exception in Demangler._demangle") return False - def _free_var_name(self, ctxt, name): + def _free_result(self, ctxt, result): try: + if result is not None and result.contents.type: + core.BNFreeType(result.contents.type) Demangler._cached_name = None except Exception: - log_error_for_exception("Unhandled Python exception in Demangler._free_var_name") + log_error_for_exception("Unhandled Python exception in Demangler._free_result") def is_mangled_string(self, name: str) -> bool: """ @@ -434,20 +452,15 @@ def is_mangled_string(self, name: str) -> bool: def demangle(self, arch: Architecture, name: str, view: Optional['binaryview.BinaryView'] = None, simplify: bool = False) -> Optional[Tuple[Optional['types.Type'], 'types.QualifiedName']]: - out_type = ctypes.POINTER(core.BNType)() - out_var_name = core.BNQualifiedName() + result = core.BNDemanglerResult() + config = _demangler_config_for_context(arch, view, simplify) - view_handle = None - if view is not None: - view_handle = view.handle - - if not core.BNDemanglerDemangleWithOptions( - self.handle, arch.handle, name, out_type, out_var_name, view_handle, simplify): + if not core.BNDemangleWithDemangler(self.handle, name, config, result): return None result_type = None - if out_type: - result_type = types.Type.create(handle=out_type) - result_var_name = types.QualifiedName._from_core_struct(out_var_name) - core.BNFreeQualifiedName(out_var_name) + if result.type: + result_type = types.Type.create(handle=core.BNNewTypeReference(result.type)) + result_var_name = types.QualifiedName._from_core_struct(result.name) + core.BNFreeDemanglerResult(result) return result_type, result_var_name diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index a6802a531..ef2209a3e 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -17,7 +17,7 @@ use binaryninjacore_sys::*; use std::ffi::{c_char, c_void}; -use crate::architecture::CoreArchitecture; +use crate::architecture::{ArchitectureExt, CoreArchitecture}; use crate::binary_view::BinaryView; use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::types::{QualifiedName, Type}; @@ -26,183 +26,121 @@ use crate::rc::*; pub type Result = std::result::Result; -pub fn demangle_generic( - arch: &CoreArchitecture, - mangled_name: &str, - view: Option<&BinaryView>, - simplify: bool, -) -> Option<(QualifiedName, Option>)> { - let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name = BNQualifiedName::default(); - let res = unsafe { - BNDemangleGeneric( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()), - simplify, - ) - }; - - if res { - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - Some((QualifiedName::from_owned_raw(out_name), out_type)) - } else { - None - } -} - -pub fn demangle_llvm(mangled_name: &str, simplify: bool) -> Option { - let mangled_name = mangled_name.to_cstr(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; - let res = unsafe { - BNDemangleLLVM( - mangled_name.as_ptr(), - &mut out_name, - &mut out_size, - simplify, - ) - }; +const DEMANGLER_MSVC: &str = "msvc"; +const DEMANGLER_GNU3: &str = "gnu3"; +const DEMANGLER_LLVM: &str = "llvm"; +fn collect_demangler_result( + res: bool, + result: &mut BNDemanglerResult, +) -> Option<(QualifiedName, Option>)> { match res { true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - Some(names.into()) + let out_type = match result.type_.is_null() { + true => None, + false => Some(unsafe { Type::ref_from_raw(BNNewTypeReference(result.type_)) }), + }; + let name = QualifiedName::from_raw(&result.name); + unsafe { BNFreeDemanglerResult(result) }; + + Some((name, out_type)) } false => None, } } -pub fn demangle_gnu3( +fn config_for_context( arch: &CoreArchitecture, - mangled_name: &str, + view: Option<&BinaryView>, simplify: bool, -) -> Option<(QualifiedName, Option>)> { - let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; - let res = unsafe { - BNDemangleGNU3( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - &mut out_size, - simplify, - ) +) -> BNDemanglerConfig { + let platform = arch.standalone_platform(); + let mut config = match view { + Some(v) => unsafe { BNGetDemanglerConfigForBinaryView(v.handle) }, + None => unsafe { + BNGetDemanglerConfigForPlatform( + platform + .as_ref() + .map(|p| p.handle) + .unwrap_or(std::ptr::null_mut()), + simplify, + ) + }, }; - - match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((names.into(), out_type)) - } - false => None, - } + config.simplifyTemplates = simplify; + config } -pub fn demangle_ms( +fn demangle_with_named_demangler( + demangler_name: &str, arch: &CoreArchitecture, mangled_name: &str, simplify: bool, ) -> Option<(QualifiedName, Option>)> { + let demangler_name = demangler_name.to_cstr(); + let demangler = unsafe { BNGetDemanglerByName(demangler_name.as_ptr()) }; + if demangler.is_null() { + return None; + } + let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; + let mut config = config_for_context(arch, None, simplify); + let mut result = BNDemanglerResult::default(); let res = unsafe { - BNDemangleMS( - arch.handle, + BNDemangleWithDemangler( + demangler, mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - &mut out_size, - simplify, + &mut config, + &mut result, ) }; - - match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((names.into(), out_type)) - } - false => None, - } + collect_demangler_result(res, &mut result) } -pub fn demangle_ms_with_view( +pub fn demangle_generic( arch: &CoreArchitecture, mangled_name: &str, view: Option<&BinaryView>, + simplify: bool, ) -> Option<(QualifiedName, Option>)> { let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; + let mut config = config_for_context(arch, view, simplify); + let mut result = BNDemanglerResult::default(); + let res = unsafe { BNDemangle(mangled_name.as_ptr(), &mut config, &mut result) }; + collect_demangler_result(res, &mut result) +} + +pub fn demangle_llvm(mangled_name: &str, simplify: bool) -> Option { + let demangler_name = DEMANGLER_LLVM.to_cstr(); + let demangler = unsafe { BNGetDemanglerByName(demangler_name.as_ptr()) }; + if demangler.is_null() { + return None; + } + + let mangled_name = mangled_name.to_cstr(); + let mut config = unsafe { BNGetDefaultDemanglerConfig() }; + config.simplifyTemplates = simplify; + let mut result = BNDemanglerResult::default(); let res = unsafe { - BNDemangleMSWithOptions( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - &mut out_size, - view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()), - ) + BNDemangleWithDemangler(demangler, mangled_name.as_ptr(), &mut config, &mut result) }; + collect_demangler_result(res, &mut result).map(|(name, _)| name) +} - match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; +pub fn demangle_gnu3( + arch: &CoreArchitecture, + mangled_name: &str, + simplify: bool, +) -> Option<(QualifiedName, Option>)> { + demangle_with_named_demangler(DEMANGLER_GNU3, arch, mangled_name, simplify) +} - Some((names.into(), out_type)) - } - false => None, - } +pub fn demangle_ms( + arch: &CoreArchitecture, + mangled_name: &str, + simplify: bool, +) -> Option<(QualifiedName, Option>)> { + demangle_with_named_demangler(DEMANGLER_MSVC, arch, mangled_name, simplify) } #[derive(PartialEq, Eq, Hash)] @@ -232,50 +170,23 @@ impl Demangler { arch: &CoreArchitecture, name: &str, view: Option<&BinaryView>, - ) -> Option<(QualifiedName, Option>)> { - self.demangle_with_options(arch, name, view, false) - } - - pub fn demangle_with_options( - &self, - arch: &CoreArchitecture, - name: &str, - view: Option<&BinaryView>, simplify: bool, ) -> Option<(QualifiedName, Option>)> { let name_bytes = name.to_cstr(); - let mut out_type = std::ptr::null_mut(); - let mut out_var_name = BNQualifiedName::default(); - - let view_ptr = match view { - Some(v) => v.handle, - None => std::ptr::null_mut(), - }; + let mut config = config_for_context(arch, view, simplify); + let mut result = BNDemanglerResult::default(); let res = unsafe { - BNDemanglerDemangleWithOptions( + BNDemangleWithDemangler( self.handle, - arch.handle, name_bytes.as_ref().as_ptr() as *const _, - &mut out_type, - &mut out_var_name, - view_ptr, - simplify, + &mut config, + &mut result, ) }; - match res { - true => { - let var_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((QualifiedName::from_owned_raw(out_var_name), var_type)) - } - false => None, - } + collect_demangler_result(res, &mut result) } pub fn name(&self) -> String { @@ -307,36 +218,42 @@ impl Demangler { } extern "C" fn cb_demangle( ctxt: *mut c_void, - arch: *mut BNArchitecture, name: *const c_char, - out_type: *mut *mut BNType, - out_var_name: *mut BNQualifiedName, - view: *mut BNBinaryView, - simplify: bool, + config: *const BNDemanglerConfig, + result: *mut BNDemanglerResult, ) -> bool where C: CustomDemangler, { ffi_wrap!("CustomDemangler::cb_demangle", unsafe { + if config.is_null() || result.is_null() { + return false; + } + let cmd = &*(ctxt as *const C); - let arch = CoreArchitecture::from_raw(arch); let Some(name) = raw_to_string(name) else { return false; }; - let view = match view.is_null() { - false => Some(BinaryView::from_raw(view).to_owned()), + let config = &*config; + if config.platform.is_null() { + return false; + } + + let arch = CoreArchitecture::from_raw(BNGetPlatformArchitecture(config.platform)); + let view = match config.view.is_null() { + false => Some(BinaryView::from_raw(config.view).to_owned()), true => None, }; - match cmd.demangle(&arch, &name, view, simplify) { + match cmd.demangle(&arch, &name, view, config.simplifyTemplates) { Some((name, ty)) => { // NOTE: Leaked to the caller, who must pick the ref up. - *out_type = match ty { + (*result).type_ = match ty { Some(t) => Ref::into_raw(t).handle, None => std::ptr::null_mut(), }; - // NOTE: Leaked to be freed with `cb_free_var_name`. - *out_var_name = QualifiedName::into_raw(name); + // NOTE: Leaked to be freed with `cb_free_result`. + (*result).name = QualifiedName::into_raw(name); true } None => false, @@ -344,10 +261,16 @@ impl Demangler { }) } - extern "C" fn cb_free_var_name(_ctxt: *mut c_void, name: *mut BNQualifiedName) { - ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe { - // TODO: What is the point of this free callback? - QualifiedName::free_raw(*name) + extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNDemanglerResult) { + ffi_wrap!("CustomDemangler::cb_free_result", unsafe { + if result.is_null() { + return; + } + if !(*result).type_.is_null() { + BNFreeType((*result).type_); + (*result).type_ = std::ptr::null_mut(); + } + QualifiedName::free_raw((*result).name); }) } @@ -356,10 +279,11 @@ impl Demangler { let ctxt = Box::into_raw(Box::new(demangler)); let callbacks = BNDemanglerCallbacks { + size: std::mem::size_of::(), context: ctxt as *mut c_void, isMangledString: Some(cb_is_mangled_string::), demangle: Some(cb_demangle::), - freeVarName: Some(cb_free_var_name), + freeResult: Some(cb_free_result), }; unsafe { diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index faca775c8..48162ecb4 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -6,10 +6,16 @@ use binaryninja::demangle::{ use binaryninja::headless::Session; use binaryninja::rc::Ref; use binaryninja::types::{QualifiedName, Type}; +use std::sync::OnceLock; + +fn session() -> &'static Session { + static SESSION: OnceLock = OnceLock::new(); + SESSION.get_or_init(|| Session::new().expect("Failed to initialize session")) +} #[test] fn test_demangler_simple() { - let _session = Session::new().expect("Failed to initialize session"); + let _session = session(); let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86_64 exists"); // Example LLVM-style mangled name let llvm_mangled = "_Z3fooi"; // "foo(int)" in LLVM mangling @@ -41,7 +47,7 @@ fn test_demangler_simple() { #[test] fn test_custom_demangler() { - let _session = Session::new().expect("Failed to initialize session"); + let _session = session(); struct TestDemangler; impl CustomDemangler for TestDemangler { diff --git a/ui/linearview.h b/ui/linearview.h index d0036aaf3..4addf93e6 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -233,6 +233,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub BinaryNinja::FunctionViewType m_ilViewType, m_prevILViewType = InvalidILViewType; HexEditorHighlightState m_highlightState; bool m_singleFunctionView = false; + bool m_simplifyTemplates = false; InstructionEdit* m_instrEdit = nullptr; @@ -311,7 +312,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub StructureRef defineInnerName(TypeRef type, uint64_t offset, uint64_t size, std::set& seen); StructureRef defineInnerIntegerSize(TypeRef type, uint64_t offset, uint64_t size, std::set& seen); StructureRef defineInnerSign(TypeRef type, uint64_t offset, uint64_t size, std::set& seen); - TypeRef getPointerTypeAndName(ArchitectureRef arch, uint64_t addr, std::string& name); + TypeRef getPointerTypeAndName(uint64_t addr, std::string& name); std::string getVariableName(uint64_t addr); BinaryNinja::Ref createLinearViewObject(); diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index bfb5c68b2..7e8558468 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2621,7 +2621,7 @@ void ElfView::DefineElfSymbol(BNSymbolType type, const string& incomingName, uin { QualifiedName demangledName; Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, m_simplifyTemplates)) + if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, this, m_simplifyTemplates)) { shortName = demangledName.GetString(); fullName = shortName; diff --git a/view/kernelcache/api/kernelcache.cpp b/view/kernelcache/api/kernelcache.cpp index 30f406569..8ded84b87 100644 --- a/view/kernelcache/api/kernelcache.cpp +++ b/view/kernelcache/api/kernelcache.cpp @@ -39,7 +39,7 @@ std::pair> CacheSymbol::DemangledName(BinaryView &view) c QualifiedName qname; Ref outType = nullptr; std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) + if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) shortName = qname.GetString(); return {shortName, outType}; } diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp index 6ff88ad1f..709ca5ff0 100644 --- a/view/kernelcache/core/KernelCache.cpp +++ b/view/kernelcache/core/KernelCache.cpp @@ -10,7 +10,7 @@ std::pair> CacheSymbol::DemangledName(BinaryView &view) c QualifiedName qname; Ref outType; std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) + if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) shortName = qname.GetString(); return { shortName, outType }; } diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index 761391880..0d17b6856 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -2725,7 +2725,7 @@ Ref MachoView::DefineMachoSymbol( { QualifiedName demangledName; Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, nullptr, m_simplifyTemplates)) + if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, nullptr, m_simplifyTemplates)) { shortName = demangledName.GetString(); fullName = shortName; diff --git a/view/pe/coffview.cpp b/view/pe/coffview.cpp index a36aff64a..238dc4240 100644 --- a/view/pe/coffview.cpp +++ b/view/pe/coffview.cpp @@ -1531,7 +1531,7 @@ void COFFView::AddCOFFSymbol(BNSymbolType type, const string& dll, const string& { QualifiedName demangledName; Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, m_simplifyTemplates)) + if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, this, m_simplifyTemplates)) { shortName = demangledName.GetString(); fullName = shortName; diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp index 6af6283ea..e7ef38d17 100644 --- a/view/pe/peview.cpp +++ b/view/pe/peview.cpp @@ -3567,7 +3567,7 @@ void PEView::AddPESymbol(BNSymbolType type, const string& dll, const string& nam { QualifiedName demangledName; Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, m_simplifyTemplates)) + if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, this, m_simplifyTemplates)) { shortName = demangledName.GetString(); fullName = shortName; diff --git a/view/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp index ba4d85aa6..7417f2707 100644 --- a/view/sharedcache/api/sharedcache.cpp +++ b/view/sharedcache/api/sharedcache.cpp @@ -148,7 +148,7 @@ std::pair> CacheSymbol::DemangledName(BinaryView &view) c QualifiedName qname; Ref outType = nullptr; std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) + if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) shortName = qname.GetString(); return {shortName, outType}; } diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index f9761968b..2d4f6d0dd 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -13,7 +13,7 @@ std::pair> CacheSymbol::DemangledName(BinaryView &view) c QualifiedName qname; Ref outType; std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) + if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) shortName = qname.GetString(); return { shortName, outType }; } From 77bfdccb9bd4e1116b5283618ef72058658f0cbb Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Tue, 30 Jun 2026 13:07:23 -0400 Subject: [PATCH 06/14] Fix GNU3 demangler pack expansion return types --- demangler/gnu3/demangle_gnu3.cpp | 93 +++++++++++++++++++++++--------- demangler/gnu3/demangle_gnu3.h | 30 ++++++++++- 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 6af770709..47f23afc0 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -523,9 +523,17 @@ DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(DemangledTypeNode&& type) } +DemangleGNU3::NodeRef DemangleGNU3::PushTemplateParamPack(ParamList args) +{ + NodeRef ref = NodeRef::TemplateParamPack(std::move(args)); + m_templateSubstitute.push_back(ref); + return ref; +} + + void DemangleGNU3::PushEmptyTemplateParamSubstitution() { - m_templateSubstitute.push_back(NodeRef::EmptyTemplatePack()); + m_templateSubstitute.push_back(NodeRef::TemplateParamPack({})); } @@ -663,6 +671,8 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) { DemangledTypeNode param = DemangleType(); NodeRef paramRef = m_lastTypeRef; + if (AppendTemplateParamPackExpansion(params, paramRef, true)) + continue; if (param.GetClass() == VoidTypeClass) continue; MyLogDebug("Var_%d - %s\n", i++, param.GetString().c_str()); @@ -780,6 +790,14 @@ DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* o DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef) { NodeRef entry = DemangleTemplateSubstitutionEntry(outTypeRef); + if (entry.IsTemplateParamPack()) + { + if (outTypeRef) + *outTypeRef = entry; + if (entry.emptyTemplatePack) + return DemangledTypeNode::VoidType(); + return *entry; + } if (entry.emptyTemplatePack || !entry) throw DemangleException(); return *entry; @@ -893,7 +911,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() // In forward-ref mode (cv conversion operator type parsing), do not consume // trailing IE — it belongs to the enclosing nested-name and will be // processed by DemangleNestedName's 'I' case, which resolves forward refs. - substitute = !m_permitForwardTemplateRefs; + substitute = !m_permitForwardTemplateRefs && !typeRef.IsTemplateParamPack(); if (!m_permitForwardTemplateRefs && m_reader.Peek() == 'I') { m_reader.Consume(); @@ -1032,7 +1050,14 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'p': { DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); + NodeRef innerRef = (m_lastTypeRef || m_lastTypeRef.IsTemplateParamPack()) ? + m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); + if (innerRef.IsTemplateParamPack()) + { + typeRef = NodeRef::TemplateParamPackExpansion(*innerRef.templatePack); + type = typeRef ? *typeRef : DemangledTypeNode::VoidType(); + break; + } type = DemangledTypeNode::PostfixType(innerRef, "..."); break; } @@ -2264,44 +2289,47 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) } -bool DemangleGNU3::TryDemangleTemplateParamPackExpansion(DemangledTypeNode& type, bool& emptyPack) +bool DemangleGNU3::TryDemangleTemplateParamExpressionPackExpansion(string& expr, bool& emptyPack) { - if (m_reader.Length() < 3 || m_reader.PeekString(3) != "DpT") + if (m_reader.Length() < 3 || m_reader.PeekString(3) != "spT") return false; - m_reader.Consume(3); // "DpT" + m_reader.Consume(3); // "spT" NodeRef entry = DemangleTemplateSubstitutionEntry(); if (entry.emptyTemplatePack) { emptyPack = true; - PushEmptyTypeSubstitution(); return true; } if (!entry) throw DemangleException(); - type = *entry; - PushType(type); + expr = entry->GetString(); return true; } -bool DemangleGNU3::TryDemangleTemplateParamExpressionPackExpansion(string& expr, bool& emptyPack) +bool DemangleGNU3::AppendTemplateParamPackExpansion(ParamList& params, const NodeRef& expansion, bool functionParameter) { - if (m_reader.Length() < 3 || m_reader.PeekString(3) != "spT") + if (!expansion.IsTemplateParamPackExpansion()) return false; - m_reader.Consume(3); // "spT" - NodeRef entry = DemangleTemplateSubstitutionEntry(); - if (entry.emptyTemplatePack) + if (expansion.emptyTemplatePack) { - emptyPack = true; + PushEmptyTypeSubstitution(); return true; } - if (!entry) - throw DemangleException(); - expr = entry->GetString(); + for (const auto& arg : *expansion.templatePack) + { + if (!arg.type) + throw DemangleException(); + NodeRef paramRef = PushType(*arg.type); + m_lastTypeRef = paramRef; + if (functionParameter && !m_functionSubstitute.empty()) + m_functionSubstitute.back().push_back(paramRef); + params.push_back({"", paramRef}); + } return true; } @@ -2349,9 +2377,15 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) case 'J': { size_t prevTemplateSize = m_templateSubstitute.size(); + size_t prevArgSize = args.size(); DemangleTemplateArgs(args, hadNonTypeArg); - if (m_topLevel && m_templateSubstitute.size() == prevTemplateSize) - PushEmptyTemplateParamSubstitution(); + if (m_topLevel) + { + ParamList packArgs(args.begin() + prevArgSize, args.end()); + while (m_templateSubstitute.size() > prevTemplateSize) + m_templateSubstitute.pop_back(); + PushTemplateParamPack(std::move(packArgs)); + } break; } case 'T': @@ -2376,11 +2410,9 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) m_reader.UnRead(); topLevel = m_topLevel; m_topLevel = false; - bool emptyPack = false; - if (!TryDemangleTemplateParamPackExpansion(tmp, emptyPack)) - tmp = DemangleType(); + tmp = DemangleType(); m_topLevel = topLevel; - if (emptyPack) + if (AppendTemplateParamPackExpansion(args, m_lastTypeRef, false)) return true; tmpRef = DemangledTypeNode::CreateShared(std::move(tmp)); args.push_back({"", tmpRef}); @@ -3163,12 +3195,19 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (simplifyTemplates) DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); varName = type.RenderTypeNameSegments(m_platform); + BNNameType nameType = type.GetNameType(); if (m_isOperatorOverload || - type.GetNameType() == ConstructorNameType || - type.GetNameType() == DestructorNameType) + nameType == ConstructorNameType || + nameType == DestructorNameType || + nameType == OperatorDeleteNameType || + nameType == OperatorDeleteArrayNameType) { returnType = DemangledTypeNode::VoidType(); } + else if (nameType == OperatorNewNameType || nameType == OperatorNewArrayNameType) + { + returnType = DemangledTypeNode::PointerType(DemangledTypeNode::VoidType(), false, false, PointerReferenceType); + } else if (nameRequiresReturnType) { returnType = DemangleType(); @@ -3210,6 +3249,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif break; DemangledTypeNode param = DemangleType(); NodeRef paramRef = m_lastTypeRef; + if (AppendTemplateParamPackExpansion(params, paramRef, true)) + continue; if (param.GetClass() == VoidTypeClass) { if (m_reader.Peek() == 'E') diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 31e5cbe4b..258add635 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -91,7 +91,9 @@ class DemangleGNU3 struct NodeRef { TypeNodeRef type; + std::shared_ptr templatePack; bool emptyTemplatePack = false; + bool templatePackExpansion = false; NodeRef() = default; NodeRef(std::nullptr_t) {} @@ -104,7 +106,32 @@ class DemangleGNU3 return ref; } + static NodeRef TemplateParamPack(ParamList args) + { + NodeRef ref; + ref.templatePack = std::make_shared(std::move(args)); + ref.emptyTemplatePack = ref.templatePack->empty(); + for (auto& arg : *ref.templatePack) + { + if (arg.type) + { + ref.type = arg.type; + break; + } + } + return ref; + } + + static NodeRef TemplateParamPackExpansion(ParamList args) + { + NodeRef ref = TemplateParamPack(std::move(args)); + ref.templatePackExpansion = true; + return ref; + } + explicit operator bool() const { return type != nullptr; } + bool IsTemplateParamPack() const { return templatePack != nullptr; } + bool IsTemplateParamPackExpansion() const { return templatePackExpansion; } DemangledTypeNode& operator*() const { return *type; } DemangledTypeNode* operator->() const { return type.get(); } operator TypeNodeRef() const { return type; } @@ -154,7 +181,6 @@ class DemangleGNU3 _STD_STRING DemangleExpression(DemangledTypeNode* outNode = nullptr); _STD_STRING DemanglePrimaryExpression(); NodeRef DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef = nullptr); - bool TryDemangleTemplateParamPackExpansion(DemangledTypeNode& type, bool& emptyPack); bool TryDemangleTemplateParamExpressionPackExpansion(_STD_STRING& expr, bool& emptyPack); DemangledTypeNode DemangleName(); DemangledTypeNode DemangleLocalName(); @@ -171,6 +197,7 @@ class DemangleGNU3 NodeRef PushTemplateType(NodeRef type); NodeRef PushTemplateType(const DemangledTypeNode& type); NodeRef PushTemplateType(DemangledTypeNode&& type); + NodeRef PushTemplateParamPack(ParamList args); void PushEmptyTemplateParamSubstitution(); NodeRef PushType(NodeRef type); NodeRef PushType(const DemangledTypeNode& type); @@ -178,6 +205,7 @@ class DemangleGNU3 void PushEmptyTypeSubstitution(); NodeRef GetTypeRef(size_t ref); const DemangledTypeNode& GetType(size_t ref); + bool AppendTemplateParamPackExpansion(ParamList& params, const NodeRef& expansion, bool functionParameter); DemangledTypeNode CreateUnknownType(const StringList& s); DemangledTypeNode CreateUnknownType(const _STD_STRING& s); From e5f4d5145e01039fc0d875bdd7baed6d09a12caf Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Tue, 30 Jun 2026 16:00:50 -0400 Subject: [PATCH 07/14] Fix crash caused by cycles in gnu3 demangler --- demangler/demangled_type_node.cpp | 75 +++++++++++++++++++++++++++++++ demangler/demangled_type_node.h | 3 ++ demangler/gnu3/demangle_gnu3.cpp | 5 +++ 3 files changed, 83 insertions(+) diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index 7aa274df5..755f7e3d6 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -19,6 +19,7 @@ #include "base/assertions.h" #include #include +#include #ifdef BINARYNINJACORE_LIBRARY using namespace BinaryNinjaCore; @@ -885,6 +886,80 @@ bool DemangledTypeNode::HasTemplateArguments() const } +bool DemangledTypeNode::ContainsNodeRef(const NodeRef& target) const +{ + if (!target) + return false; + + std::unordered_set visited; + std::function containsNode; + std::function containsRef; + + containsRef = [&](const NodeRef& ref) { + if (!ref) + return false; + if (ref == target) + return true; + return containsNode(ref.get()); + }; + + auto containsName = [&](const DemangledQualifiedName& name) { + for (const auto& part : name) + { + if (containsRef(part.m_baseTypeSuffix)) + return true; + for (const auto& arg : part.m_templateArgs) + { + if (containsRef(arg.type)) + return true; + } + } + return false; + }; + + auto containsParams = [&](const vector& params) { + for (const auto& param : params) + { + if (containsRef(param.type)) + return true; + } + return false; + }; + + containsNode = [&](const DemangledTypeNode* node) { + if (!node) + return false; + if (node == target.get()) + return true; + if (!visited.insert(node).second) + return false; + + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType) || containsName(payload->ownerName); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType); + if (auto payload = std::get_if(&node->m_payload)) + { + return containsRef(payload->returnType) || containsRef(payload->implicitThisParameterType) || + containsParams(payload->params); + } + if (auto payload = std::get_if(&node->m_payload)) + return containsName(payload->name); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType) || containsRef(payload->suffixType); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->leftType) || containsRef(payload->rightType); + return false; + }; + + return containsNode(this); +} + + bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) const { if (m_nameType != other.m_nameType || m_pointerSuffixBits != other.m_pointerSuffixBits || diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index 34d348bf2..76c79e9e3 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -94,6 +94,8 @@ class DemangledNamePart static Ref CreateSharedCopy(const DemangledNamePart& part); private: + friend class DemangledTypeNode; + _STD_STRING m_base; std::shared_ptr m_baseTypeSuffix; _STD_VECTOR m_templateArgs; @@ -174,6 +176,7 @@ class DemangledTypeNode void SetParenthesizedMemberPointer(bool parenthesized); StringList RenderTypeNameSegments(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; bool IsStructurallyEqual(const DemangledTypeNode& other) const; + bool ContainsNodeRef(const NodeRef& target) const; bool MutateChildTypes(const std::function& mutator); bool MutateQualifiedNames(const std::function& mutator); diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 47f23afc0..96b9757d5 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -710,6 +710,11 @@ void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode&, const ParamLis continue; if (ref.index >= args.size() || !args[ref.index].type) throw DemangleException(); + if (args[ref.index].type->ContainsNodeRef(ref.typeRef)) + { + LogWarnF("Rejecting GNU3 demangle: recursive forward template reference T{}_", ref.index); + throw DemangleException("Detected recursive forward template reference"); + } *ref.typeRef = *args[ref.index].type; } m_pendingForwardRefs.clear(); From c277e15c94bae7f82628179f357b460e8f282371 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Fri, 3 Jul 2026 10:46:17 -0400 Subject: [PATCH 08/14] Demangler API Cleanup --- binaryninjaapi.h | 102 ++--- binaryninjacore.h | 11 +- demangle.cpp | 251 +++++------ demangler/demangled_template_simplifier.cpp | 31 +- demangler/gnu3/demangle_gnu3.cpp | 14 +- demangler/gnu3/demangle_gnu3.h | 3 + demangler/msvc/demangle_msvc.cpp | 48 +-- demangler/msvc/demangle_msvc.h | 17 +- plugins/workflow_swift/src/lib.rs | 4 +- python/demangle.py | 308 ++++++++------ python/types.py | 1 - rust/examples/demangler.rs | 49 +-- rust/src/binary_view.rs | 5 +- rust/src/demangle.rs | 399 +++++++++++------- rust/tests/demangler.rs | 102 ++--- type.cpp | 15 +- view/elf/elfview.cpp | 23 +- view/kernelcache/api/kernelcache.cpp | 16 - view/kernelcache/api/kernelcacheapi.h | 3 - view/kernelcache/core/KernelCache.cpp | 19 +- view/kernelcache/core/KernelCache.h | 2 - view/macho/machoview.cpp | 25 +- view/pe/coffview.cpp | 8 +- view/pe/peview.cpp | 8 +- view/sharedcache/api/sharedcache.cpp | 16 - view/sharedcache/api/sharedcacheapi.h | 3 - view/sharedcache/core/SharedCache.cpp | 19 +- view/sharedcache/core/SharedCache.h | 2 - .../workflow/SharedCacheWorkflow.cpp | 15 +- 29 files changed, 798 insertions(+), 721 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 5d15c647d..8c1897aba 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2433,23 +2433,6 @@ namespace BinaryNinja { */ Ref Load(Ref rawData, bool updateAnalysis, ProgressFunction progress, Ref options = new Metadata(MetadataType::KeyValueDataType), bool isDatabase = false); - /*! Attempt to demangle a mangled name, trying all relevant demanglers and using whichever one accepts it - - \see Demangler::Demangle for a discussion on which demangler will be used. - - \param[in] platform Platform for the symbol. Used for pointer/integer sizes and calling conventions. - \param[in] mangledName a mangled Microsoft Visual Studio C++ name - \param[out] outType Pointer to Type to output - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view (Optional) view of the binary containing the mangled name - \param[in] simplify (Optional) Whether to simplify demangled names. - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleGeneric(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - Ref view = nullptr, bool simplify = false); - /*! Demangles using LLVM's demangler \param[in] mangledName a mangled (msvc/gnu3/rust/dlang) name @@ -2459,18 +2442,7 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, bool simplify = false); - - /*! Demangles using LLVM's demangler - - \param[in] mangledName a mangled (msvc/gnu3/rust/dlang) name - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view View to check the analysis.types.templateSimplifier for - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, BinaryView* view); + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, bool simplify = true); /*! Demangles a Microsoft Visual Studio C++ name @@ -2483,8 +2455,16 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleMS(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - bool simplify = false); + bool DemangleMS(const Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify = true); + + /*! Determines if a symbol name is a mangled Microsoft Visual Studio C++ name + + \param[in] mangledName a potentially mangled name + + \ingroup demangle + */ + bool IsMSVCMangledString(const std::string& mangledName); /*! Demangles a GNU3 name @@ -2497,8 +2477,8 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleGNU3(Platform* platform, const std::string& mangledName, Ref& outType, - QualifiedName& outVarName, bool simplify = false); + bool DemangleGNU3(const Platform* platform, const std::string& mangledName, Ref& outType, + QualifiedName& outVarName, bool simplify = true); /*! Determines if a symbol name is a mangled GNU3 name @@ -4786,18 +4766,21 @@ namespace BinaryNinja { static DemanglerConfig Default(); static DemanglerConfig ForPlatform(Platform* platform, bool simplifyTemplates = false); static DemanglerConfig ForBinaryView(BinaryView* view); + static DemanglerConfig FromAPIObject(const BNDemanglerConfig* config); - BNDemanglerConfig GetAPIObject() const; + Platform& GetPlatform() const; + BNDemanglerConfig ToAPIObject() const; }; struct DemanglerResult { QualifiedName name; Ref type; - }; - std::optional TryDemangle( - const std::string& mangledName, const DemanglerConfig& config = DemanglerConfig::Default()); + static DemanglerResult FromAPIObject(const BNDemanglerResult* result); + static DemanglerResult FromAPIObjectAndFree(BNDemanglerResult* result); + BNDemanglerResult ToAPIObject() const; + }; /*! @@ -22488,33 +22471,46 @@ namespace BinaryNinja { std::string m_nameForRegister; protected: - explicit Demangler(const std::string& name); + explicit Demangler(std::string demanglerName); Demangler(BNDemangler* demangler); virtual ~Demangler() = default; - static bool IsMangledStringCallback(void* ctxt, const char* name); - static bool DemangleCallback(void* ctxt, const char* name, const BNDemanglerConfig* config, + static bool IsMangledStringCallback(void* ctxt, const char* mangledName); + static bool DemangleCallback(void* ctxt, const char* mangledName, const BNDemanglerConfig* config, BNDemanglerResult* result); static void FreeResultCallback(void* ctxt, BNDemanglerResult* result); public: - /*! Register a custom Demangler. Newly registered demanglers will get priority over + /*! Register a custom Demangler. Newly registered demanglers get priority over previously registered demanglers and built-in demanglers. + + \return True if registration succeeded; false if the demangler was invalid. */ - static void Register(Demangler* demangler); + static bool Register(Demangler* demangler); /*! Get the list of currently registered demanglers, sorted by lowest to highest priority. \return List of demanglers */ static std::vector> GetList(); - static Ref GetByName(const std::string& name); + static Ref GetByName(const std::string& demanglerName); /*! Promote a demangler to the highest-priority position. \param demangler Demangler to promote + \return True if promotion succeeded; false if the demangler was invalid or not registered. */ - static void Promote(Ref demangler); + static bool Promote(const Ref& demangler); + + /*! + Attempt to demangle a mangled name, trying all relevant demanglers and using whichever one accepts it. + + \param[in] mangledName Raw mangled name + \param[in] config Platform/view/options used while demangling + \return Demangled type/name if successful + */ + static std::optional DemangleAny( + const std::string& mangledName, const Config& config = DemanglerConfig::Default()); std::string GetName() const; @@ -22522,36 +22518,30 @@ namespace BinaryNinja { The most recently registered demangler that claims a name is a mangled string (returns true from this function), and then returns a value from Demangle will - determine the result of a call to DemangleGeneric. Returning True from this + determine the result of a call to DemangleAny. Returning True from this does not require the demangler to succeed the call to Demangle, but simply implies that it may succeed. - \param name Raw mangled name string + \param mangledName Raw mangled name string \return True if the demangler thinks it can handle the name */ - virtual bool IsMangledString(const std::string& name) = 0; + virtual bool IsMangledString(const std::string& mangledName) = 0; /*! Demangle a raw name into a Type and QualifiedName. - Any unresolved named types referenced by the resulting Type will be created as - empty structures or void typedefs in the view, if the result is used on - a data structure in the view. Given this, the call to Demangle should NOT - cause any side-effects creating types in the view trying to resolve this - and instead just return a type with unresolved named type references. - The most recently registered demangler that claims a name is a mangled string (returns true from IsMangledString), and then returns a value from - this function will determine the result of a call to DemangleGeneric. + this function will determine the result of a call to DemangleAny. If this call returns None, the next most recently used demangler(s) will be tried instead. If the mangled name has no type information, but a name is still possible to extract, this function may return a successful result with outType=nullptr, which will be accepted. - \param name Raw mangled name + \param mangledName Raw mangled name \param config Platform/view/options used while demangling \return Demangled type/name if successful */ - virtual std::optional Demangle(const std::string& name, const Config& config) = 0; + virtual std::optional Demangle(const std::string& mangledName, const Config& config) = 0; }; /*! diff --git a/binaryninjacore.h b/binaryninjacore.h index f18c3d750..cc2e3e314 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -8270,7 +8270,7 @@ extern "C" BINARYNINJACOREAPI BNDemanglerConfig BNGetDemanglerConfigForBinaryView(BNBinaryView* view); BINARYNINJACOREAPI bool BNDemangle(const char* name, const BNDemanglerConfig* config, BNDemanglerResult* result); - BINARYNINJACOREAPI bool BNDemangleWithDemangler(BNDemangler* demangler, const char* name, + BINARYNINJACOREAPI bool BNDemangleWithDemangler(const BNDemangler* demangler, const char* name, const BNDemanglerConfig* config, BNDemanglerResult* result); BINARYNINJACOREAPI void BNFreeDemanglerResult(BNDemanglerResult* result); @@ -8278,9 +8278,12 @@ extern "C" BINARYNINJACOREAPI BNDemangler** BNGetDemanglerList(size_t* count); BINARYNINJACOREAPI void BNFreeDemanglerList(BNDemangler** demanglers); BINARYNINJACOREAPI BNDemangler* BNGetDemanglerByName(const char* name); - BINARYNINJACOREAPI char* BNGetDemanglerName(BNDemangler* demangler); - BINARYNINJACOREAPI void BNPromoteDemangler(BNDemangler* demangler); - BINARYNINJACOREAPI bool BNIsDemanglerMangledName(BNDemangler* demangler, const char* name); + BINARYNINJACOREAPI BNDemangler* BNGetMSVCDemangler(void); + BINARYNINJACOREAPI BNDemangler* BNGetGNU3Demangler(void); + BINARYNINJACOREAPI BNDemangler* BNGetLLVMDemangler(void); + BINARYNINJACOREAPI char* BNGetDemanglerName(const BNDemangler* demangler); + BINARYNINJACOREAPI bool BNPromoteDemangler(const BNDemangler* demangler); + BINARYNINJACOREAPI bool BNIsDemanglerMangledName(const BNDemangler* demangler, const char* name); // Plugin repository APIs BINARYNINJACOREAPI char** BNPluginGetApis(BNPlugin* p, size_t* count); diff --git a/demangle.cpp b/demangle.cpp index 4d2586642..11a421d4f 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -1,166 +1,167 @@ #include "binaryninjaapi.h" #include +#include using namespace std; using namespace BinaryNinja; -namespace BinaryNinja { - static void StoreDemanglerResult(BNDemanglerResult& apiResult, DemanglerResult& result) +namespace { + std::optional DemangleWithDemangler( + const BNDemangler* demangler, const Platform* platform, const std::string& mangledName, bool simplify) { - result.name = QualifiedName::FromAPIObject(&apiResult.name); - if (apiResult.type) - { - result.type = new Type(apiResult.type); - apiResult.type = nullptr; - } - else - { - result.type = nullptr; - } + if (!demangler) + return std::nullopt; + + BNDemanglerConfig apiConfig(platform ? platform->GetObject() : nullptr, nullptr, simplify); + BNDemanglerResult apiResult = {}; + if (!BNDemangleWithDemangler(demangler, mangledName.c_str(), &apiConfig, &apiResult)) + return std::nullopt; + + return DemanglerResult::FromAPIObjectAndFree(&apiResult); } - static DemanglerConfig DemanglerConfigFromAPIObject(const BNDemanglerConfig* config) + bool StoreDemangleResult(std::optional&& result, Ref& outType, QualifiedName& outVarName) + { + if (!result) + return false; + outType = std::move(result->type); + outVarName = result->name; + return true; + } +} + +namespace BinaryNinja +{ + DemanglerConfig DemanglerConfig::FromAPIObject(const BNDemanglerConfig* config) { DemanglerConfig result; if (!config) - return result; + return Default(); - result.platform = config->platform ? new CorePlatform(BNNewPlatformReference(config->platform)) : nullptr; + result.platform = config->platform ? new CorePlatform(BNNewPlatformReference(config->platform)) : Default().platform; result.view = config->view ? new BinaryView(BNNewViewReference(config->view)) : nullptr; result.simplifyTemplates = config->simplifyTemplates; return result; } + DemanglerConfig DemanglerConfig::Default() { BNDemanglerConfig config = BNGetDefaultDemanglerConfig(); - return DemanglerConfigFromAPIObject(&config); + static auto cfg = FromAPIObject(&config); + return cfg; } DemanglerConfig DemanglerConfig::ForPlatform(Platform* platform, bool simplifyTemplates) { - BNDemanglerConfig config = BNGetDemanglerConfigForPlatform(platform ? platform->GetObject() : nullptr, - simplifyTemplates); - return DemanglerConfigFromAPIObject(&config); + return {platform ? platform : Default().platform.GetPtr(), nullptr, simplifyTemplates}; } DemanglerConfig DemanglerConfig::ForBinaryView(BinaryView* view) { - BNDemanglerConfig config = BNGetDemanglerConfigForBinaryView(view ? view->GetObject() : nullptr); - return DemanglerConfigFromAPIObject(&config); - } + if (!view) + return Default(); - BNDemanglerConfig DemanglerConfig::GetAPIObject() const - { - return { - platform ? platform->GetObject() : nullptr, - view ? view->GetObject() : nullptr, - simplifyTemplates, - }; - } - - static BNDemanglerConfig DemanglerConfigForContext(Platform* platform, BinaryView* view, bool simplify) - { - BNDemanglerConfig config; - if (view) + Ref platform = view->GetDefaultPlatform(); + if (!platform) { - config = BNGetDemanglerConfigForBinaryView(view->GetObject()); - config.simplifyTemplates = simplify; - if (platform) - config.platform = platform->GetObject(); - return config; + if (auto arch = view->GetDefaultArchitecture()) + platform = arch->GetStandalonePlatform(); } + if (!platform) + platform = Default().platform; - return BNGetDemanglerConfigForPlatform(platform ? platform->GetObject() : nullptr, simplify); + return {platform, view, + Settings::Instance()->Get("analysis.types.templateSimplifier", view)}; } - static bool DemangleWithNamedDemangler(const char* demanglerName, Platform* platform, const std::string& mangledName, - Ref& outType, QualifiedName& outVarName, BinaryView* view, bool simplify) + Platform& DemanglerConfig::GetPlatform() const { - BNDemangler* demangler = BNGetDemanglerByName(demanglerName); - if (!demangler) - return false; - - BNDemanglerConfig apiConfig = DemanglerConfigForContext(platform, view, simplify); - BNDemanglerResult apiResult = {}; - if (!BNDemangleWithDemangler(demangler, mangledName.c_str(), &apiConfig, &apiResult)) - return false; - - DemanglerResult result; - StoreDemanglerResult(apiResult, result); - BNFreeDemanglerResult(&apiResult); - outType = result.type; - outVarName = result.name; - return true; + if (platform) + return *platform; + return *Default().platform; } - std::optional TryDemangle(const std::string& mangledName, const DemanglerConfig& config) + BNDemanglerConfig DemanglerConfig::ToAPIObject() const { - BNDemanglerConfig apiConfig = config.GetAPIObject(); - BNDemanglerResult apiResult = {}; - if (!BNDemangle(mangledName.c_str(), &apiConfig, &apiResult)) - return std::nullopt; + return { + GetPlatform().GetObject(), + view ? view->GetObject() : nullptr, + simplifyTemplates, + }; + } + DemanglerResult DemanglerResult::FromAPIObject(const BNDemanglerResult* apiResult) + { DemanglerResult result; - StoreDemanglerResult(apiResult, result); - BNFreeDemanglerResult(&apiResult); + if (!apiResult) + return result; + + result.name = QualifiedName::FromAPIObject(&apiResult->name); + if (apiResult->type) + result.type = new Type(BNNewTypeReference(apiResult->type)); + else + result.type = nullptr; return result; } - bool DemangleGeneric(Platform* platform, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view, bool simplify) + DemanglerResult DemanglerResult::FromAPIObjectAndFree(BNDemanglerResult* apiResult) { - BNDemanglerConfig apiConfig = DemanglerConfigForContext(platform, view, simplify); - BNDemanglerResult apiResult = {}; - if (!BNDemangle(name.c_str(), &apiConfig, &apiResult)) - return false; - - DemanglerResult result; - StoreDemanglerResult(apiResult, result); - BNFreeDemanglerResult(&apiResult); - outType = result.type; - outVarName = result.name; - return true; + DemanglerResult result = FromAPIObject(apiResult); + BNFreeDemanglerResult(apiResult); + return result; } - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, - BinaryView* view) + BNDemanglerResult DemanglerResult::ToAPIObject() const { - const bool simplify = Settings::Instance()->Get("analysis.types.templateSimplifier", view); - return DemangleLLVM(mangledName, outVarName, simplify); + return { + name.GetAPIObject(), + type ? BNNewTypeReference(type->m_object) : nullptr, + }; } bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, bool simplify) { Ref outType; - return DemangleWithNamedDemangler(BN_DEMANGLER_LLVM, nullptr, mangledName, outType, outVarName, nullptr, - simplify); + return StoreDemangleResult( + DemangleWithDemangler(BNGetLLVMDemangler(), nullptr, mangledName, simplify), outType, outVarName); } - bool DemangleMS(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + + bool DemangleMS(const Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, bool simplify) { - return DemangleWithNamedDemangler(BN_DEMANGLER_MSVC, platform, mangledName, outType, outVarName, nullptr, - simplify); + return StoreDemangleResult( + DemangleWithDemangler(BNGetMSVCDemangler(), platform, mangledName, simplify), outType, + outVarName); } - bool DemangleGNU3(Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + + bool DemangleGNU3(const Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, bool simplify) { outVarName.clear(); - return DemangleWithNamedDemangler(BN_DEMANGLER_GNU3, platform, mangledName, outType, outVarName, nullptr, - simplify); + return StoreDemangleResult( + DemangleWithDemangler(BNGetGNU3Demangler(), platform, mangledName, simplify), outType, + outVarName); + } + + + bool IsMSVCMangledString(const std::string& mangledName) + { + BNDemangler* demangler = BNGetMSVCDemangler(); + return demangler && BNIsDemanglerMangledName(demangler, mangledName.c_str()); } bool IsGNU3MangledString(const std::string& mangledName) { - BNDemangler* demangler = BNGetDemanglerByName(BN_DEMANGLER_GNU3); + BNDemangler* demangler = BNGetGNU3Demangler(); return demangler && BNIsDemanglerMangledName(demangler, mangledName.c_str()); } - Demangler::Demangler(const std::string& name): m_nameForRegister(name) + Demangler::Demangler(std::string demanglerName): m_nameForRegister(std::move(demanglerName)) { } @@ -169,34 +170,25 @@ namespace BinaryNinja { m_object = demangler; } - bool Demangler::IsMangledStringCallback(void* ctxt, const char* name) + bool Demangler::IsMangledStringCallback(void* ctxt, const char* mangledName) { - Demangler* demangler = (Demangler*)ctxt; - return demangler->IsMangledString(name); + auto demangler = static_cast(ctxt); + return demangler->IsMangledString(mangledName); } - bool Demangler::DemangleCallback(void* ctxt, const char* name, const BNDemanglerConfig* config, + bool Demangler::DemangleCallback(void* ctxt, const char* mangledName, const BNDemanglerConfig* config, BNDemanglerResult* result) { - Demangler* demangler = (Demangler*)ctxt; + auto demangler = static_cast(ctxt); - if (!name || !result) + if (!mangledName || !result) return false; - auto demangleResult = demangler->Demangle(name, DemanglerConfigFromAPIObject(config)); + auto demangleResult = demangler->Demangle(mangledName, DemanglerConfig::FromAPIObject(config)); if (!demangleResult) return false; - if (demangleResult->type) - { - result->type = BNNewTypeReference(demangleResult->type->m_object); - } - else - { - result->type = nullptr; - } - result->name = demangleResult->name.GetAPIObject(); - + *result = demangleResult->ToAPIObject(); return true; } @@ -205,15 +197,30 @@ namespace BinaryNinja { BNFreeDemanglerResult(result); } - void Demangler::Register(Demangler* demangler) + bool Demangler::Register(Demangler* demangler) { + if (!demangler) + return false; + BNDemanglerCallbacks cb = {}; cb.size = sizeof(cb); - cb.context = (void*)demangler; + cb.context = reinterpret_cast(demangler); cb.isMangledString = IsMangledStringCallback; cb.demangle = DemangleCallback; cb.freeResult = FreeResultCallback; - demangler->m_object = BNRegisterDemangler(demangler->m_nameForRegister.c_str(), &cb); + BNDemangler* object = BNRegisterDemangler(demangler->m_nameForRegister.c_str(), &cb); + if (!object) + return false; + + demangler->m_object = object; + return true; + } + + bool Demangler::Promote(const Ref& demangler) + { + if (!demangler || !demangler->m_object) + return false; + return BNPromoteDemangler(demangler->m_object); } std::vector> Demangler::GetList() @@ -228,17 +235,22 @@ namespace BinaryNinja { return result; } - Ref Demangler::GetByName(const std::string& name) + Ref Demangler::GetByName(const std::string& demanglerName) { - BNDemangler* result = BNGetDemanglerByName(name.c_str()); + BNDemangler* result = BNGetDemanglerByName(demanglerName.c_str()); if (!result) return nullptr; return new CoreDemangler(result); } - void Demangler::Promote(Ref demangler) + std::optional Demangler::DemangleAny(const std::string& mangledName, const Config& config) { - BNPromoteDemangler(demangler->m_object); + BNDemanglerConfig apiConfig = config.ToAPIObject(); + BNDemanglerResult apiResult = {}; + if (!BNDemangle(mangledName.c_str(), &apiConfig, &apiResult)) + return std::nullopt; + + return Result::FromAPIObjectAndFree(&apiResult); } std::string Demangler::GetName() const @@ -260,16 +272,13 @@ namespace BinaryNinja { std::optional CoreDemangler::Demangle(const std::string& name, const Config& config) { - BNDemanglerConfig apiConfig = config.GetAPIObject(); + BNDemanglerConfig apiConfig = config.ToAPIObject(); BNDemanglerResult apiResult = {}; bool success = BNDemangleWithDemangler(m_object, name.c_str(), &apiConfig, &apiResult); if (!success) return std::nullopt; - Result result; - StoreDemanglerResult(apiResult, result); - BNFreeDemanglerResult(&apiResult); - return result; + return Result::FromAPIObjectAndFree(&apiResult); } } // namespace BinaryNinja diff --git a/demangler/demangled_template_simplifier.cpp b/demangler/demangled_template_simplifier.cpp index ca17d0488..e17641269 100644 --- a/demangler/demangled_template_simplifier.cpp +++ b/demangler/demangled_template_simplifier.cpp @@ -65,32 +65,6 @@ namespace return s; } - static std::string_view ExtractLeadingTypeKeyword(std::string_view& s) - { - s = TrimSpaces(s); - if (StartsWith(s, "class ")) - { - s = s.substr(6); - return "class "; - } - if (StartsWith(s, "struct ")) - { - s = s.substr(7); - return "struct "; - } - if (StartsWith(s, "union ")) - { - s = s.substr(6); - return "union "; - } - if (StartsWith(s, "enum ")) - { - s = s.substr(5); - return "enum "; - } - return {}; - } - static string RemoveSpaces(std::string_view s) { string out; @@ -2461,9 +2435,8 @@ DemangledTemplateSimplifier::SimplifyNameResult DemangledTemplateSimplifier::Sim BN::QualifiedName DemangledTemplateSimplifier::SimplifyQualifiedName(const BN::QualifiedName& name) { - string rendered = name.GetString(); - std::string_view stripped(rendered.data(), rendered.size()); - (void)ExtractLeadingTypeKeyword(stripped); + auto renderedName = name.GetString(); + auto stripped = StripLeadingTypeKeyword(std::string_view(renderedName.data(), renderedName.size())); DemangledQualifiedName parsed = ParseCompatibilityName(stripped); SimplifyNameSegmentsInPlace(parsed); return BN::QualifiedName(RenderSegments(parsed)); diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 96b9757d5..0845b4d16 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -3501,7 +3501,6 @@ bool DemangleGNU3Static::DemangleStringGNU3(Architecture* arch, const string& na return DemangleStringGNU3(GetDemanglerFallbackPlatform(), name, outType, outVarName, simplifyTemplates); } - // ===== Explicit template instantiation ===== @@ -3520,10 +3519,14 @@ class GNU3Demangler: public Demangler return DemangleGNU3Static::IsGNU3MangledString(name); } - virtual bool Demangle(const string& name, const DemanglerConfig& config, DemanglerResult& result) override + virtual std::optional Demangle(const string& name, const Config& config) override { - return DemangleGNU3Static::DemangleStringGNU3(config.GetPlatform(), name, result.type, result.name, - config.simplifyTemplates); + Result result; + Platform& platform = config.GetPlatform(); + if (!DemangleGNU3Static::DemangleStringGNU3(platform, name, result.type, result.name, + config.simplifyTemplates)) + return std::nullopt; + return result; } }; @@ -3543,7 +3546,6 @@ extern "C" #endif { static GNU3Demangler* demangler = new GNU3Demangler(); - Demangler::Register(demangler); - return true; + return Demangler::Register(demangler); } } diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 258add635..8c49e751e 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -19,6 +19,9 @@ // XXX: Compiled directly into the core for performance reasons // Will still work fine compiled independently, just at about a // 50-100% performance penalty due to FFI overhead +// TODO: the above assessment needs to be re-evaluated after the +// refactor to use DemangledTypeNode and the simplifier refactor +// its very possible this performance penalty is completely gone #ifdef BINARYNINJACORE_LIBRARY #include "qualifiedname.h" #include "type.h" diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index 52ab12e0d..26aea6f02 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -240,19 +240,19 @@ void Demangle::BackrefContextSwitch::Swap(BackrefList& left, BackrefList& right) -Demangle::Demangle(Platform& platform, _STD_STRING mangledName) : +Demangle::Demangle(const DemanglerConfig& config, _STD_STRING mangledName) : m_mangledName(std::move(mangledName)), m_reader(m_mangledName), - m_platform(platform) + m_config(config) { } -void Demangle::Reset(Platform& platform, const _STD_STRING& mangledName) +void Demangle::Reset(const DemanglerConfig& config, const _STD_STRING& mangledName) { m_mangledName = mangledName; m_reader.Reset(m_mangledName); m_backrefList.Clear(); - m_platform = std::ref(platform); + m_config = config; m_templateParamDepth = 0; m_nestingDepth = 0; } @@ -288,8 +288,7 @@ _STD_STRING Demangle::FormatTypeAndName(const DemangledTypeNode& type, const Nam Platform& platform = GetRenderingPlatform(); if (type.GetNameType() == OperatorReturnTypeNameType) { - Ref finalizedType = type.Finalize(platform); - if (finalizedType) + if (Ref finalizedType = type.Finalize(platform)) return finalizedType->GetTypeAndName(QualifiedName(nameSegments)); } return type.GetTypeAndName(nameSegments, platform); @@ -297,7 +296,7 @@ _STD_STRING Demangle::FormatTypeAndName(const DemangledTypeNode& type, const Nam Platform& Demangle::GetRenderingPlatform() const { - return m_platform; + return m_config.GetPlatform(); } DemangledTypeNode Demangle::DemangleReferencedSymbolValue(BackrefList& varList) @@ -2576,50 +2575,46 @@ Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) return finishContext(); } -std::pair, QualifiedName> Demangle::Finalize(bool simplifyTemplates) +DemanglerResult Demangle::Finalize() { DemangleContext context = DemangleSymbol(); if (m_reader.Length() != 0) LogDebugF("Demangling Succeeded with trailing characters '{}' in '{}'", m_reader.GetRaw(), m_mangledName); - if (simplifyTemplates) + if (m_config.simplifyTemplates) { DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(context.type); DemangledTemplateSimplifier::SimplifyNameSegmentsInPlace(context.name); } - return {context.type.Finalize(m_platform), QualifiedName(FinalizeNameList(context.name))}; + DemanglerResult result; + result.type = context.type.Finalize(m_config.GetPlatform()); + result.name = QualifiedName(FinalizeNameList(context.name)); + return result; } -static bool DemangleMSWithPlatform(Platform& platform, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, bool simplifyTemplates) +static std::optional DemangleMSWithConfig(const DemanglerConfig& config, const _STD_STRING& mangledName) { - outType = nullptr; if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) - return false; + return std::nullopt; try { - thread_local Demangle demangle(platform, mangledName); - demangle.Reset(platform, mangledName); - auto result = demangle.Finalize(simplifyTemplates); - outType = std::move(result.first); - outVarName = std::move(result.second); - return true; + thread_local Demangle demangle(config, mangledName); + demangle.Reset(config, mangledName); + return demangle.Finalize(); } catch (DemangleException& e) { LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); - return false; } catch (std::exception& e) { LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); - return false; } + return std::nullopt; } - class MSDemangler: public Demangler { public: @@ -2633,9 +2628,9 @@ class MSDemangler: public Demangler return !name.empty() && (name[0] == '?' || name[0] == '.'); } - bool Demangle(const _STD_STRING& name, const DemanglerConfig& config, DemanglerResult& result) override + std::optional Demangle(const string& name, const Config& config) override { - return DemangleMSWithPlatform(config.GetPlatform(), name, result.type, result.name, config.simplifyTemplates); + return DemangleMSWithConfig(config, name); } }; @@ -2654,7 +2649,6 @@ extern "C" #endif { static auto demangler = new MSDemangler(); - Demangler::Register(demangler); - return true; + return Demangler::Register(demangler); } } diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h index edfc0bada..4927974db 100644 --- a/demangler/msvc/demangle_msvc.h +++ b/demangler/msvc/demangle_msvc.h @@ -15,15 +15,16 @@ #pragma once #include #include -#include // XXX: Compiled directly into the core for performance reasons // Will still work fine compiled independently, just at about a // 50-100% performance penalty due to FFI overhead +// TODO: the above assessment needs to be re-evaluated after the +// refactor to use DemangledTypeNode and the simplifier refactor +// its very possible this performance penalty is completely gone #ifdef BINARYNINJACORE_LIBRARY #include "qualifiedname.h" #include "type.h" -#include "architecture.h" #include "binaryview.h" #include "demangle.h" #define BN BinaryNinjaCore @@ -233,7 +234,7 @@ class Demangle _STD_STRING m_mangledName; // Owns the string; Reader points into it Reader m_reader; BackrefList m_backrefList; - std::reference_wrapper m_platform; + BN::DemanglerConfig m_config; size_t m_templateParamDepth = 0; size_t m_nestingDepth = 0; using NestingGuard = DemangleNestingGuard; @@ -245,11 +246,11 @@ class Demangle static bool FunctionTypeHasPointerSuffix(char functionType); static _STD_STRING FormatFunctionScopeSignature( const DemangledTypeNode& type, const NameList& scopeName, BN::Platform& platform); - BN::Platform& GetRenderingPlatform() const; + [[nodiscard]] BN::Platform& GetRenderingPlatform() const; void AppendLocalScope(NameList& nameList, BackrefList& nameBackrefList, uint64_t scopeOrdinal, bool typeNameContext); bool TryAppendLocalScopeAt(NameList& nameList, BackrefList& nameBackrefList, const char* encodedNumberStart, bool typeNameContext); - _STD_STRING FormatTypeAndName(const DemangledTypeNode& type, const NameList& name) const; + [[nodiscard]] _STD_STRING FormatTypeAndName(const DemangledTypeNode& type, const NameList& name) const; enum class TypeBackrefMode { RecordTopLevel, @@ -327,12 +328,12 @@ class Demangle DemangleContext DemangleSymbol(BackrefList& backrefList); public: - Demangle(BN::Platform& platform, _STD_STRING mangledName); - void Reset(BN::Platform& platform, const _STD_STRING& mangledName); + Demangle(const BN::DemanglerConfig& config, _STD_STRING mangledName); + void Reset(const BN::DemanglerConfig& config, const _STD_STRING& mangledName); Demangle(const Demangle&) = delete; Demangle(Demangle&&) = delete; Demangle& operator=(const Demangle&) = delete; Demangle& operator=(Demangle&&) = delete; DemangleContext DemangleSymbol(); - std::pair, BN::QualifiedName> Finalize(bool simplifyTemplates = false); + BN::DemanglerResult Finalize(); }; diff --git a/plugins/workflow_swift/src/lib.rs b/plugins/workflow_swift/src/lib.rs index d199bf089..c8af5d26e 100644 --- a/plugins/workflow_swift/src/lib.rs +++ b/plugins/workflow_swift/src/lib.rs @@ -30,7 +30,5 @@ pub extern "C" fn CorePluginInit() -> bool { }"#, ); - Demangler::register("Swift", SwiftDemangler); - - true + Demangler::register("Swift", SwiftDemangler).is_some() } diff --git a/python/demangle.py b/python/demangle.py index 11ee9f0d7..977e68930 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -19,7 +19,6 @@ # IN THE SOFTWARE. import ctypes -import traceback # Binary Ninja components import binaryninja @@ -30,29 +29,85 @@ from .architecture import Architecture from .platform import Platform from .settings import Settings -from typing import Iterable, List, Optional, Union, Tuple, Any +from typing import Callable, Iterable, List, Optional, Union, Any, NamedTuple -_DEMANGLER_MSVC = "msvc" -_DEMANGLER_GNU3 = "gnu3" -_DEMANGLER_LLVM = "llvm" +class DemangleResult(NamedTuple): + """ + Tuple-compatible demangle result. + """ -def _demangler_config_for_context( - arch_or_platform: Union[Architecture, Platform], - view: Optional['binaryview.BinaryView'] = None, - simplify: bool = False -): - if isinstance(arch_or_platform, Architecture): - platform_obj = arch_or_platform.standalone_platform - elif isinstance(arch_or_platform, Platform): - platform_obj = arch_or_platform - else: - raise TypeError("Unexpected arch or platform type") + type: Optional['types.Type'] + name: Union['types.QualifiedName', List[str], str] + + +def _demangle_result_from_core_and_free( + result: core.BNDemanglerResult, + qualified_name: bool = False +) -> DemangleResult: + out_type = None + try: + if result.type: + out_type = core.BNNewTypeReference(result.type) + result_var_name = types.QualifiedName._from_core_struct(result.name) + + result_type = None + if out_type: + result_type = types.Type.create(handle=out_type) + out_type = None + result_name = result_var_name if qualified_name else result_var_name.name + return DemangleResult(result_type, result_name) + finally: + if out_type: + core.BNFreeType(out_type) + core.BNFreeDemanglerResult(result) + + +class DemanglerConfig: + """ + Configuration used by demangler APIs. + """ - config = core.BNGetDemanglerConfigForPlatform(platform_obj.handle if platform_obj else None, simplify) - if view is not None: - config.view = view.handle - return config + def __init__( + self, + arch_or_platform: Optional[Union[Architecture, Platform]] = None, + view: Optional['binaryview.BinaryView'] = None, + simplify: bool = False + ): + if isinstance(arch_or_platform, Architecture): + platform_obj = arch_or_platform.standalone_platform + elif isinstance(arch_or_platform, Platform): + platform_obj = arch_or_platform + elif arch_or_platform is None: + platform_obj = view.platform if view is not None else None + else: + raise TypeError("Unexpected arch or platform type") + + self.platform = platform_obj + self.view = view + self.simplify_templates = simplify + + def _to_core_struct(self) -> core.BNDemanglerConfig: + config = core.BNDemanglerConfig() + config.platform = self.platform.handle if self.platform is not None else None + config.view = self.view.handle if self.view is not None else None + config.simplifyTemplates = self.simplify_templates + return config + + @classmethod + def _from_core_struct(cls, config: core.BNDemanglerConfig) -> 'DemanglerConfig': + if hasattr(config, "contents"): + config = config.contents + + platform = None + if config.platform: + platform = Platform(handle=core.BNNewPlatformReference(config.platform)) + + view = None + if config.view: + view = binaryview.BinaryView(handle=core.BNNewViewReference(config.view)) + + return cls(platform, view, config.simplifyTemplates) def get_qualified_name(names: Iterable[str]): @@ -65,20 +120,54 @@ def get_qualified_name(names: Iterable[str]): :rtype: str :Example: - >>> type, name = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - >>> get_qualified_name(name) + >>> result = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + >>> get_qualified_name(result.name) 'Foobar::testf' >>> """ return "::".join(names) +def demangle_any( + mangled_name: str, + config: DemanglerConfig +) -> Optional[DemangleResult]: + """ + ``demangle_any`` demangles a mangled symbol name using a prebuilt demangler config. + + :param str mangled_name: a mangled symbol name + :param DemanglerConfig config: Platform/view/options used while demangling + :return: returns a DemangleResult with type and name fields, or None on error. DemangleResult can be unpacked as (type, name). + :rtype: Optional[DemangleResult] + :Example: + + >>> config = DemanglerConfig(Architecture["x86_64"]) + >>> result = demangle_any("?testf@Foobar@@SA?AW4foo@1@W421@@Z", config) + >>> result.type + + >>> result.name + ['Foobar', 'testf'] + """ + if not isinstance(config, DemanglerConfig): + raise TypeError("config must be a DemanglerConfig") + + result = core.BNDemanglerResult() + api_config = config._to_core_struct() + if not core.BNDemangle(mangled_name, api_config, result): + return None + + try: + return _demangle_result_from_core_and_free(result) + except UnicodeDecodeError: + return None + + def demangle_generic( archOrPlatform: Union[Architecture, Platform], mangled_name: str, view: Optional['binaryview.BinaryView'] = None, simplify: bool = False -) -> Optional[Tuple[Optional['types.Type'], List[str]]]: +) -> DemangleResult: """ ``demangle_generic`` demangles a mangled symbol name to a Type object. @@ -86,45 +175,18 @@ def demangle_generic( :param str mangled_name: a mangled symbol name :param view: (optional) view of the binary containing the mangled name :param simplify: (optional) Whether to simplify demangled names - :return: returns tuple of (Optional[Type], demangled_name) or None on error - :rtype: Tuple + :return: returns a DemangleResult with type and name fields. DemangleResult can be unpacked as (type, name). + :rtype: DemangleResult :Example: >>> demangle_generic(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - (, ['Foobar', 'testf']) + DemangleResult(type=, name=['Foobar', 'testf']) >>> demangle_generic(Architecture["x86_64"], "__ZN20ArmCallingConvention27GetIntegerArgumentRegistersEv") - (, ['ArmCallingConvention', 'GetIntegerArgumentRegisters']) + DemangleResult(type=, name=['ArmCallingConvention', 'GetIntegerArgumentRegisters']) >>> """ - arch = None - if isinstance(archOrPlatform, Architecture): - arch = archOrPlatform - elif isinstance(archOrPlatform, Platform): - arch = archOrPlatform.arch - else: - raise TypeError("Unexpected arch or platform type") - - out_type = ctypes.POINTER(core.BNType)() - result = core.BNDemanglerResult() - config = _demangler_config_for_context(archOrPlatform, view, simplify) - if not core.BNDemangle(mangled_name, config, result): - return None, [mangled_name] - - try: - if result.type: - out_type = core.BNNewTypeReference(result.type) - result_var_name = types.QualifiedName._from_core_struct(result.name) - except UnicodeDecodeError: - if out_type: - core.BNFreeType(out_type) - core.BNFreeDemanglerResult(result) - return None, [mangled_name] - - result_type = None - if out_type: - result_type = types.Type.create(handle=out_type) - core.BNFreeDemanglerResult(result) - return result_type, result_var_name.name + config = DemanglerConfig(archOrPlatform, view, simplify) + return demangle_any(mangled_name, config) or DemangleResult(None, [mangled_name]) def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = None) -> Optional[List[str]]: @@ -143,10 +205,10 @@ def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.Bi >>> """ view, simplify_templates = _simplify_from_compat_option(options) - config = core.BNGetDemanglerConfigForBinaryView(view.handle) if view is not None else core.BNGetDefaultDemanglerConfig() - config.simplifyTemplates = simplify_templates + config = DemanglerConfig(view=view, simplify=simplify_templates)._to_core_struct() - demangler = core.BNGetDemanglerByName(_DEMANGLER_LLVM) + binaryninja._init_plugins() + demangler = core.BNGetLLVMDemangler() if demangler is None: return None @@ -154,10 +216,7 @@ def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.Bi if not core.BNDemangleWithDemangler(demangler, mangled_name, config, result): return None - try: - return types.QualifiedName._from_core_struct(result.name).name - finally: - core.BNFreeDemanglerResult(result) + return _demangle_result_from_core_and_free(result).name def _simplify_from_compat_option(simplify: Optional[Union[bool, binaryview.BinaryView]]): @@ -171,70 +230,63 @@ def _simplify_from_compat_option(simplify: Optional[Union[bool, binaryview.Binar def _demangle_type_and_name( - arch_or_platform: Union[Architecture, Platform], - mangled_name: str, - simplify: Optional[Union[bool, binaryview.BinaryView]], - demangler_name: str -): + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: Optional[Union[bool, binaryview.BinaryView]], + demangler_getter: Callable[[], Any] +) -> DemangleResult: view, simplify_templates = _simplify_from_compat_option(simplify) binaryninja._init_plugins() - demangler = core.BNGetDemanglerByName(demangler_name) + demangler = demangler_getter() if demangler is None: - return (None, mangled_name) + return DemangleResult(None, mangled_name) - config = _demangler_config_for_context(arch_or_platform, view, simplify_templates) + config = DemanglerConfig(arch_or_platform, view, simplify_templates)._to_core_struct() result = core.BNDemanglerResult() if not core.BNDemangleWithDemangler(demangler, mangled_name, config, result): - return (None, mangled_name) + return DemangleResult(None, mangled_name) - try: - result_type = None - if result.type: - result_type = types.Type.create(handle=core.BNNewTypeReference(result.type)) - result_var_name = types.QualifiedName._from_core_struct(result.name) - return (result_type, result_var_name.name) - finally: - core.BNFreeDemanglerResult(result) + return _demangle_result_from_core_and_free(result) def demangle_ms( arch_or_platform: Union[Architecture, Platform], mangled_name: str, simplify: bool = False -): +) -> DemangleResult: """ ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. :param Union[Architecture, Platform] arch_or_platform: Architecture or Platform for the symbol. Required for pointer/integer sizes and calling conventions. :param str mangled_name: a mangled Microsoft Visual Studio C++ name :param bool simplify: (optional) Whether to simplify demangled names - :return: returns tuple of (Type, demangled_name) or (None, mangled_name) on error - :rtype: Tuple[Optional[Type], Union[str, List[str]]] + :return: returns a DemangleResult with type and name fields, or DemangleResult(None, mangled_name) on error + :rtype: DemangleResult :Example: >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - (, ['Foobar', 'testf']) + DemangleResult(type=, name=['Foobar', 'testf']) >>> """ - return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, _DEMANGLER_MSVC) + return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, core.BNGetMSVCDemangler) def demangle_gnu3( arch_or_platform: Union[Architecture, Platform], mangled_name: str, simplify: bool = False -): +) -> DemangleResult: """ ``demangle_gnu3`` demangles a mangled name to a Type object. :param Union[Architecture, Platform] arch_or_platform: Architecture or Platform for the symbol. Required for pointer and integer sizes. :param str mangled_name: a mangled GNU3 name :param bool simplify: (optional) Whether to simplify demangled names - :return: returns tuple of (Type, demangled_name) or (None, mangled_name) on error - :rtype: Tuple[Optional[Type], Union[str, List[str]]] + :return: returns a DemangleResult with type and name fields, or DemangleResult(None, mangled_name) on error + :rtype: DemangleResult """ - return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, _DEMANGLER_GNU3) + return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, core.BNGetGNU3Demangler) class _DemanglerMetaclass(type): @@ -278,6 +330,8 @@ class Demangler(metaclass=_DemanglerMetaclass): Pluggable name demangling interface. See :py:func:`register` and :py:func:`demangle` for details on the process of this interface. + Custom Demangler subclasses can be registered and promoted at runtime. + The list of Demanglers can be queried: >>> list(Demangler) @@ -300,6 +354,8 @@ def register(cls): """ Register a custom Demangler. Newly registered demanglers will get priority over previously registered demanglers and built-in demanglers. + + :return: True if registration succeeded; False if the demangler was invalid. """ demangler = cls() @@ -313,22 +369,23 @@ def register(cls): demangler._cb.demangle = demangler._cb.demangle.__class__(demangler._demangle) demangler._cb.freeResult = demangler._cb.freeResult.__class__(demangler._free_result) demangler.handle = core.BNRegisterDemangler(cls.name, demangler._cb) + if not demangler.handle: + return False + cls._registered_demanglers.append(demangler) + return True @classmethod def promote(cls, demangler): """ Promote a demangler to the highest-priority position. - >>> list(Demangler) - [, , ] - >>> Demangler.promote(list(Demangler)[0]) - >>> list(Demangler) - [, , ] - :param demangler: Demangler to promote + :return: True if promotion succeeded; False if the demangler was invalid or not registered. """ - core.BNPromoteDemangler(demangler.handle) + if demangler is None or demangler.handle is None: + return False + return core.BNPromoteDemangler(demangler.handle) def __eq__(self, other): if not isinstance(other, Demangler): @@ -350,22 +407,9 @@ def _is_mangled_string(self, ctxt, name): def _demangle(self, ctxt, name, config, result): try: - api_config = config.contents - api_platform = None - api_arch = None - if api_config.platform is not None: - api_platform = Platform(handle=core.BNNewPlatformReference(api_config.platform)) - api_arch = api_platform.arch - api_view = None - if api_config.view is not None: - api_view = binaryview.BinaryView(handle=core.BNNewViewReference(api_config.view)) - if api_arch is None and api_view.platform is not None: - api_arch = api_view.platform.arch - - if api_arch is None: - return False + api_config = DemanglerConfig._from_core_struct(config) - demangle_result = self.demangle(api_arch, core.pyNativeStr(name), api_view, api_config.simplifyTemplates) + demangle_result = self.demangle(core.pyNativeStr(name), api_config) if demangle_result is None: return False type, var_name = demangle_result @@ -410,16 +454,15 @@ def is_mangled_string(self, name: str) -> bool: def demangle( self, - arch: Architecture, name: str, - view: Optional['binaryview.BinaryView'] = None, - simplify: bool = False - ) -> Optional[Tuple['types.Type', 'types.QualifiedName']]: + config: DemanglerConfig + ) -> Optional[DemangleResult]: """ Demangle a raw name into a Type and QualifiedName. - The result of this function is a (Type, QualifiedName) tuple for the demangled - name's details. + The result of this function is a DemangleResult with Type and QualifiedName + fields for the demangled name's details. DemangleResult can be unpacked as + (type, name). Any unresolved named types referenced by the resulting Type will be created as empty structures or void typedefs in the view, if the result is used on @@ -433,34 +476,33 @@ def demangle( If this call returns None, the next most recently used demangler(s) will be tried instead. If the mangled name has no type information, but a name is still possible to extract, - this function may return a successful (None, ) result, which will be accepted. + this function may return a successful DemangleResult(None, ), which will be accepted. - :param arch: Architecture for context in which the name exists, eg for pointer sizes :param name: Raw mangled name - :param view: (Optional) BinaryView context in which the name exists, eg for type lookup - :param simplify: Whether to simplify templates while demangling - :return: Tuple of (Type, Name) if successful, None if not. Type may be None if only - a demangled name can be recovered from the raw name. + :param config: Platform/view/options used while demangling + :return: DemangleResult with type and name fields if successful, None if not. + Type may be None if only a demangled name can be recovered from the raw name. """ raise NotImplementedError() + @staticmethod + def demangle_any(name: str, config: DemanglerConfig) -> Optional[DemangleResult]: + """ + Demangle a raw name using a prebuilt DemanglerConfig. + """ + return demangle_any(name, config) + class CoreDemangler(Demangler): def is_mangled_string(self, name: str) -> bool: return core.BNIsDemanglerMangledName(self.handle, name) - def demangle(self, arch: Architecture, name: str, view: Optional['binaryview.BinaryView'] = None, - simplify: bool = False) -> Optional[Tuple[Optional['types.Type'], 'types.QualifiedName']]: + def demangle(self, name: str, config: DemanglerConfig) -> Optional[DemangleResult]: result = core.BNDemanglerResult() - config = _demangler_config_for_context(arch, view, simplify) + api_config = config._to_core_struct() - if not core.BNDemangleWithDemangler(self.handle, name, config, result): + if not core.BNDemangleWithDemangler(self.handle, name, api_config, result): return None - result_type = None - if result.type: - result_type = types.Type.create(handle=core.BNNewTypeReference(result.type)) - result_var_name = types.QualifiedName._from_core_struct(result.name) - core.BNFreeDemanglerResult(result) - return result_type, result_var_name + return _demangle_result_from_core_and_free(result, qualified_name=True) diff --git a/python/types.py b/python/types.py index 04ec5e780..a4e36fc22 100644 --- a/python/types.py +++ b/python/types.py @@ -202,7 +202,6 @@ def escape(name: QualifiedNameType, escaping: TokenEscapingType) -> str: def unescape(name: QualifiedNameType, escaping: TokenEscapingType) -> str: return core.BNUnescapeTypeName(str(QualifiedName(name)), escaping) - @dataclass(frozen=True) class TypeReferenceSource: name: QualifiedName diff --git a/rust/examples/demangler.rs b/rust/examples/demangler.rs index 4e686596f..15a970c7d 100644 --- a/rust/examples/demangler.rs +++ b/rust/examples/demangler.rs @@ -1,7 +1,5 @@ -use binaryninja::architecture::CoreArchitecture; -use binaryninja::binary_view::BinaryView; -use binaryninja::demangle::{CustomDemangler, Demangler}; -use binaryninja::rc::Ref; +use binaryninja::architecture::{ArchitectureExt, CoreArchitecture}; +use binaryninja::demangle::{CustomDemangler, Demangler, DemanglerConfig, DemanglerResult}; use binaryninja::tracing::TracingLogListener; use binaryninja::types::{QualifiedName, Type}; @@ -12,16 +10,16 @@ impl CustomDemangler for TestDemangler { name == "test_name" || name == "test_name2" } - fn demangle( - &self, - _arch: &CoreArchitecture, - name: &str, - _view: Option>, - _simplify: bool, - ) -> Option<(QualifiedName, Option>)> { + fn demangle(&self, name: &str, _config: &DemanglerConfig) -> Option { match name { - "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), - "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), + "test_name" => Some(DemanglerResult::new( + QualifiedName::from(vec!["test_name"]), + Some(Type::bool()), + )), + "test_name2" => Some(DemanglerResult::new( + QualifiedName::from(vec!["test_name2", "aaa"]), + None, + )), _ => None, } } @@ -31,14 +29,18 @@ fn main() { tracing_subscriber::fmt::init(); let _listener = TracingLogListener::new().register(); + tracing::info!("Registering demangler..."); + assert!(Demangler::register("Test", TestDemangler)); + // This loads all the core architecture, platform, etc plugins let _headless_session = binaryninja::headless::Session::new().expect("Failed to initialize session"); - tracing::info!("Registering demangler..."); - Demangler::register("Test", TestDemangler); - let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86 exists"); + let platform = placeholder_arch + .standalone_platform() + .expect("x86 standalone platform exists"); + let config = DemanglerConfig::for_platform(&platform, false); for d in Demangler::list().iter() { tracing::info!("{}", d.name()); @@ -47,17 +49,8 @@ fn main() { " \"__ZN1AC2Ei\" is mangled? {}", d.is_mangled_string("__ZN1AC2Ei") ); - tracing::info!( - " \"__ZN1AC2Ei\" : {:?}", - d.demangle(&placeholder_arch, "__ZN1AC2Ei", None) - ); - tracing::info!( - " \"test_name\" : {:?}", - d.demangle(&placeholder_arch, "test_name", None) - ); - tracing::info!( - " \"test_name2\" : {:?}", - d.demangle(&placeholder_arch, "test_name2", None) - ); + tracing::info!(" \"__ZN1AC2Ei\" : {:?}", d.demangle("__ZN1AC2Ei", &config)); + tracing::info!(" \"test_name\" : {:?}", d.demangle("test_name", &config)); + tracing::info!(" \"test_name2\" : {:?}", d.demangle("test_name2", &config)); } } diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 3d1c243d5..94ebaf215 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -1468,7 +1468,10 @@ impl BinaryView { let name_handle = unsafe { let id_str = BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name); - BNDefineAnalysisType(self.handle, id_str, &mut raw_name, type_obj.handle) + let name_handle = + BNDefineAnalysisType(self.handle, id_str, &mut raw_name, type_obj.handle); + BNFreeString(id_str); + name_handle }; QualifiedName::free_raw(raw_name); QualifiedName::from_owned_raw(name_handle) diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index ef2209a3e..5fb874690 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -16,131 +16,273 @@ use binaryninjacore_sys::*; use std::ffi::{c_char, c_void}; +use std::ptr; use crate::architecture::{ArchitectureExt, CoreArchitecture}; use crate::binary_view::BinaryView; -use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::types::{QualifiedName, Type}; - +use crate::platform::Platform; +use crate::qualified_name::QualifiedName; use crate::rc::*; +use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::types::Type; pub type Result = std::result::Result; -const DEMANGLER_MSVC: &str = "msvc"; -const DEMANGLER_GNU3: &str = "gnu3"; -const DEMANGLER_LLVM: &str = "llvm"; +fn msvc_demangler() -> *mut BNDemangler { + unsafe { BNGetMSVCDemangler() } +} -fn collect_demangler_result( - res: bool, - result: &mut BNDemanglerResult, -) -> Option<(QualifiedName, Option>)> { - match res { - true => { - let out_type = match result.type_.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(BNNewTypeReference(result.type_)) }), - }; - let name = QualifiedName::from_raw(&result.name); - unsafe { BNFreeDemanglerResult(result) }; - - Some((name, out_type)) +fn gnu3_demangler() -> *mut BNDemangler { + unsafe { BNGetGNU3Demangler() } +} + +fn llvm_demangler() -> *mut BNDemangler { + unsafe { BNGetLLVMDemangler() } +} + +/// Platform, view, and simplification options used by demangler APIs. +#[derive(Clone, Debug)] +pub struct DemanglerConfig { + pub platform: Option>, + pub view: Option>, + pub simplify_templates: bool, +} + +impl DemanglerConfig { + pub fn new( + platform: Option<&Platform>, + view: Option<&BinaryView>, + simplify_templates: bool, + ) -> Self { + Self { + platform: platform.map(|platform| platform.to_owned()), + view: view.map(|view| view.to_owned()), + simplify_templates, + } + } + + fn for_architecture(arch: &CoreArchitecture, simplify_templates: bool) -> Self { + Self { + platform: arch.standalone_platform(), + view: None, + simplify_templates, + } + } + + fn for_architecture_with_view( + arch: &CoreArchitecture, + view: Option<&BinaryView>, + simplify_templates: bool, + ) -> Self { + Self { + platform: arch.standalone_platform(), + view: view.map(|view| view.to_owned()), + simplify_templates, + } + } + + pub fn for_platform(platform: &Platform, simplify_templates: bool) -> Self { + Self { + platform: Some(platform.to_owned()), + view: None, + simplify_templates, + } + } + + pub fn for_binary_view(view: &BinaryView, simplify_templates: bool) -> Self { + let platform = view.default_platform().or_else(|| { + view.default_arch() + .and_then(|arch| arch.standalone_platform()) + }); + Self { + platform, + view: Some(view.to_owned()), + simplify_templates, + } + } + + pub fn from_api_object(config: &BNDemanglerConfig) -> Self { + let platform = match config.platform.is_null() { + true => None, + false => { + Some(unsafe { Platform::ref_from_raw(BNNewPlatformReference(config.platform)) }) + } + }; + let view = match config.view.is_null() { + true => None, + false => Some(unsafe { BinaryView::ref_from_raw(BNNewViewReference(config.view)) }), + }; + Self { + platform, + view, + simplify_templates: config.simplifyTemplates, + } + } + + unsafe fn from_api_object_ptr(config: *const BNDemanglerConfig) -> Self { + match config.is_null() { + true => Self::default(), + false => Self::from_api_object(unsafe { &*config }), + } + } + + pub fn to_api_object(&self) -> BNDemanglerConfig { + BNDemanglerConfig { + platform: self + .platform + .as_ref() + .map(|platform| platform.handle) + .unwrap_or(ptr::null_mut()), + view: self + .view + .as_ref() + .map(|view| view.handle) + .unwrap_or(ptr::null_mut()), + simplifyTemplates: self.simplify_templates, } - false => None, } } -fn config_for_context( - arch: &CoreArchitecture, - view: Option<&BinaryView>, - simplify: bool, -) -> BNDemanglerConfig { - let platform = arch.standalone_platform(); - let mut config = match view { - Some(v) => unsafe { BNGetDemanglerConfigForBinaryView(v.handle) }, - None => unsafe { - BNGetDemanglerConfigForPlatform( - platform - .as_ref() - .map(|p| p.handle) - .unwrap_or(std::ptr::null_mut()), - simplify, - ) - }, - }; - config.simplifyTemplates = simplify; - config +impl Default for DemanglerConfig { + fn default() -> Self { + Self { + platform: None, + view: None, + simplify_templates: false, + } + } } -fn demangle_with_named_demangler( - demangler_name: &str, - arch: &CoreArchitecture, +/// Demangled name and optional type recovered from a mangled name. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DemanglerResult { + pub name: QualifiedName, + pub ty: Option>, +} + +/// Compatibility alias matching the Python API name. +pub type DemangleResult = DemanglerResult; + +impl DemanglerResult { + pub fn new(name: impl Into, ty: Option>) -> Self { + Self { + name: name.into(), + ty, + } + } + + pub fn from_api_object(result: &BNDemanglerResult) -> Self { + let ty = match result.type_.is_null() { + true => None, + false => Some(unsafe { Type::ref_from_raw(BNNewTypeReference(result.type_)) }), + }; + Self { + name: QualifiedName::from_raw(&result.name), + ty, + } + } + + pub fn from_api_object_and_free(result: &mut BNDemanglerResult) -> Self { + let demangler_result = Self::from_api_object(result); + unsafe { BNFreeDemanglerResult(result) }; + demangler_result + } + + pub fn to_api_object(&self) -> BNDemanglerResult { + BNDemanglerResult { + name: QualifiedName::into_raw(self.name.clone()), + type_: self + .ty + .as_ref() + .map(|ty| unsafe { BNNewTypeReference(ty.handle) }) + .unwrap_or(ptr::null_mut()), + } + } + + pub fn into_tuple(self) -> (QualifiedName, Option>) { + (self.name, self.ty) + } +} + +impl From<(QualifiedName, Option>)> for DemanglerResult { + fn from(value: (QualifiedName, Option>)) -> Self { + Self { + name: value.0, + ty: value.1, + } + } +} + +impl From for (QualifiedName, Option>) { + fn from(value: DemanglerResult) -> Self { + value.into_tuple() + } +} + +fn collect_demangler_result(res: bool, result: &mut BNDemanglerResult) -> Option { + match res { + true => Some(DemanglerResult::from_api_object_and_free(result)), + false => None, + } +} + +fn demangle_with_demangler( + demangler: *mut BNDemangler, mangled_name: &str, - simplify: bool, -) -> Option<(QualifiedName, Option>)> { - let demangler_name = demangler_name.to_cstr(); - let demangler = unsafe { BNGetDemanglerByName(demangler_name.as_ptr()) }; + config: &DemanglerConfig, +) -> Option { if demangler.is_null() { return None; } let mangled_name = mangled_name.to_cstr(); - let mut config = config_for_context(arch, None, simplify); + let api_config = config.to_api_object(); let mut result = BNDemanglerResult::default(); let res = unsafe { - BNDemangleWithDemangler( - demangler, - mangled_name.as_ptr(), - &mut config, - &mut result, - ) + BNDemangleWithDemangler(demangler, mangled_name.as_ptr(), &api_config, &mut result) }; collect_demangler_result(res, &mut result) } +pub fn demangle_any(mangled_name: &str, config: &DemanglerConfig) -> Option { + let mangled_name = mangled_name.to_cstr(); + let api_config = config.to_api_object(); + let mut result = BNDemanglerResult::default(); + let res = unsafe { BNDemangle(mangled_name.as_ptr(), &api_config, &mut result) }; + collect_demangler_result(res, &mut result) +} + pub fn demangle_generic( arch: &CoreArchitecture, mangled_name: &str, view: Option<&BinaryView>, simplify: bool, -) -> Option<(QualifiedName, Option>)> { - let mangled_name = mangled_name.to_cstr(); - let mut config = config_for_context(arch, view, simplify); - let mut result = BNDemanglerResult::default(); - let res = unsafe { BNDemangle(mangled_name.as_ptr(), &mut config, &mut result) }; - collect_demangler_result(res, &mut result) +) -> Option { + let config = DemanglerConfig::for_architecture_with_view(arch, view, simplify); + demangle_any(mangled_name, &config) } pub fn demangle_llvm(mangled_name: &str, simplify: bool) -> Option { - let demangler_name = DEMANGLER_LLVM.to_cstr(); - let demangler = unsafe { BNGetDemanglerByName(demangler_name.as_ptr()) }; - if demangler.is_null() { - return None; - } - - let mangled_name = mangled_name.to_cstr(); - let mut config = unsafe { BNGetDefaultDemanglerConfig() }; - config.simplifyTemplates = simplify; - let mut result = BNDemanglerResult::default(); - let res = unsafe { - BNDemangleWithDemangler(demangler, mangled_name.as_ptr(), &mut config, &mut result) - }; - collect_demangler_result(res, &mut result).map(|(name, _)| name) + let config = DemanglerConfig::new(None, None, simplify); + demangle_with_demangler(llvm_demangler(), mangled_name, &config).map(|result| result.name) } pub fn demangle_gnu3( arch: &CoreArchitecture, mangled_name: &str, simplify: bool, -) -> Option<(QualifiedName, Option>)> { - demangle_with_named_demangler(DEMANGLER_GNU3, arch, mangled_name, simplify) +) -> Option { + let config = DemanglerConfig::for_architecture(arch, simplify); + demangle_with_demangler(gnu3_demangler(), mangled_name, &config) } pub fn demangle_ms( arch: &CoreArchitecture, mangled_name: &str, simplify: bool, -) -> Option<(QualifiedName, Option>)> { - demangle_with_named_demangler(DEMANGLER_MSVC, arch, mangled_name, simplify) +) -> Option { + let config = DemanglerConfig::for_architecture(arch, simplify); + demangle_with_demangler(msvc_demangler(), mangled_name, &config) } #[derive(PartialEq, Eq, Hash)] @@ -165,28 +307,8 @@ impl Demangler { unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) } } - pub fn demangle( - &self, - arch: &CoreArchitecture, - name: &str, - view: Option<&BinaryView>, - simplify: bool, - ) -> Option<(QualifiedName, Option>)> { - let name_bytes = name.to_cstr(); - - let mut config = config_for_context(arch, view, simplify); - let mut result = BNDemanglerResult::default(); - - let res = unsafe { - BNDemangleWithDemangler( - self.handle, - name_bytes.as_ref().as_ptr() as *const _, - &mut config, - &mut result, - ) - }; - - collect_demangler_result(res, &mut result) + pub fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option { + demangle_with_demangler(self.handle, name, config) } pub fn name(&self) -> String { @@ -203,7 +325,7 @@ impl Demangler { } } - pub fn register(name: &str, demangler: C) -> Self { + pub fn register(name: &str, demangler: C) -> bool { extern "C" fn cb_is_mangled_string(ctxt: *mut c_void, name: *const c_char) -> bool where C: CustomDemangler, @@ -226,7 +348,7 @@ impl Demangler { C: CustomDemangler, { ffi_wrap!("CustomDemangler::cb_demangle", unsafe { - if config.is_null() || result.is_null() { + if result.is_null() { return false; } @@ -234,26 +356,11 @@ impl Demangler { let Some(name) = raw_to_string(name) else { return false; }; - let config = &*config; - if config.platform.is_null() { - return false; - } + let config = DemanglerConfig::from_api_object_ptr(config); - let arch = CoreArchitecture::from_raw(BNGetPlatformArchitecture(config.platform)); - let view = match config.view.is_null() { - false => Some(BinaryView::from_raw(config.view).to_owned()), - true => None, - }; - - match cmd.demangle(&arch, &name, view, config.simplifyTemplates) { - Some((name, ty)) => { - // NOTE: Leaked to the caller, who must pick the ref up. - (*result).type_ = match ty { - Some(t) => Ref::into_raw(t).handle, - None => std::ptr::null_mut(), - }; - // NOTE: Leaked to be freed with `cb_free_result`. - (*result).name = QualifiedName::into_raw(name); + match cmd.demangle(&name, &config) { + Some(demangle_result) => { + *result = demangle_result.to_api_object(); true } None => false, @@ -263,14 +370,9 @@ impl Demangler { extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNDemanglerResult) { ffi_wrap!("CustomDemangler::cb_free_result", unsafe { - if result.is_null() { - return; + if !result.is_null() { + BNFreeDemanglerResult(result); } - if !(*result).type_.is_null() { - BNFreeType((*result).type_); - (*result).type_ = std::ptr::null_mut(); - } - QualifiedName::free_raw((*result).name); }) } @@ -278,26 +380,35 @@ impl Demangler { let name_ptr = name.as_ptr(); let ctxt = Box::into_raw(Box::new(demangler)); - let callbacks = BNDemanglerCallbacks { + let callbacks = Box::into_raw(Box::new(BNDemanglerCallbacks { size: std::mem::size_of::(), context: ctxt as *mut c_void, isMangledString: Some(cb_is_mangled_string::), demangle: Some(cb_demangle::), freeResult: Some(cb_free_result), - }; - - unsafe { - Demangler::from_raw(BNRegisterDemangler( - name_ptr, - Box::leak(Box::new(callbacks)), - )) + })); + + let handle = unsafe { BNRegisterDemangler(name_ptr, callbacks) }; + if handle.is_null() { + unsafe { + drop(Box::from_raw(ctxt)); + drop(Box::from_raw(callbacks)); + } + false + } else { + true } } - pub fn promote(demangler: &Demangler) { - unsafe { - BNPromoteDemangler(demangler.handle); + pub fn demangle_any(name: &str, config: &DemanglerConfig) -> Option { + demangle_any(name, config) + } + + pub fn promote(demangler: &Demangler) -> bool { + if demangler.handle.is_null() { + return false; } + unsafe { BNPromoteDemangler(demangler.handle) } } } @@ -324,11 +435,5 @@ unsafe impl CoreArrayProviderInner for Demangler { pub trait CustomDemangler: 'static + Sync { fn is_mangled_string(&self, name: &str) -> bool; - fn demangle( - &self, - arch: &CoreArchitecture, - name: &str, - view: Option>, - simplify: bool, - ) -> Option<(QualifiedName, Option>)>; + fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option; } diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index 48162ecb4..c312d8ba4 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -1,16 +1,51 @@ -use binaryninja::architecture::CoreArchitecture; -use binaryninja::binary_view::BinaryView; +use binaryninja::architecture::{ArchitectureExt, CoreArchitecture}; use binaryninja::demangle::{ - demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, CustomDemangler, Demangler, + demangle_any, demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, CustomDemangler, + Demangler, DemanglerConfig, DemanglerResult, }; use binaryninja::headless::Session; -use binaryninja::rc::Ref; use binaryninja::types::{QualifiedName, Type}; use std::sync::OnceLock; +struct TestDemangler; + +impl CustomDemangler for TestDemangler { + fn is_mangled_string(&self, name: &str) -> bool { + name == "test_name" || name == "test_name2" + } + + fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option { + match name { + "test_name" => Some(DemanglerResult::new( + QualifiedName::from(vec![if config.simplify_templates { + "test_name_simplified" + } else { + "test_name" + }]), + Some(Type::bool()), + )), + "test_name2" => Some(DemanglerResult::new( + QualifiedName::from(vec!["test_name2", "aaa"]), + None, + )), + _ => None, + } + } +} + +fn register_custom_demangler() { + static CUSTOM_DEMANGLER: OnceLock<()> = OnceLock::new(); + CUSTOM_DEMANGLER.get_or_init(|| { + assert!(Demangler::register("Test", TestDemangler)); + }); +} + fn session() -> &'static Session { static SESSION: OnceLock = OnceLock::new(); - SESSION.get_or_init(|| Session::new().expect("Failed to initialize session")) + SESSION.get_or_init(|| { + register_custom_demangler(); + Session::new().expect("Failed to initialize session") + }) } #[test] @@ -24,23 +59,21 @@ fn test_demangler_simple() { // Example GNU-style mangled name let gnu_mangled = "_Z3bari"; // "bar(int)" in GNU mangling - let (gnu_demangled_name, gnu_demangled_type) = - demangle_gnu3(&placeholder_arch, gnu_mangled, true).unwrap(); - assert_eq!(gnu_demangled_name, "bar".into()); + let gnu_demangled = demangle_gnu3(&placeholder_arch, gnu_mangled, true).unwrap(); + assert_eq!(gnu_demangled.name, "bar".into()); // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( - gnu_demangled_type.unwrap().to_string(), + gnu_demangled.ty.unwrap().to_string(), "int64_t(int32_t)".to_string() ); // Example MSVC-style mangled name let msvc_mangled = "?baz@@YAHH@Z"; // "int __cdecl baz(int)" in MSVC mangling - let (msvc_demangled_name, msvc_demangled_type) = - demangle_ms(&placeholder_arch, msvc_mangled, true).unwrap(); - assert_eq!(msvc_demangled_name, "baz".into()); + let msvc_demangled = demangle_ms(&placeholder_arch, msvc_mangled, true).unwrap(); + assert_eq!(msvc_demangled.name, "baz".into()); // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( - msvc_demangled_type.unwrap().to_string(), + msvc_demangled.ty.unwrap().to_string(), "int32_t __cdecl(int32_t)".to_string() ); } @@ -48,43 +81,16 @@ fn test_demangler_simple() { #[test] fn test_custom_demangler() { let _session = session(); - struct TestDemangler; - - impl CustomDemangler for TestDemangler { - fn is_mangled_string(&self, name: &str) -> bool { - name == "test_name" || name == "test_name2" - } - - fn demangle( - &self, - _arch: &CoreArchitecture, - name: &str, - _view: Option>, - simplify: bool, - ) -> Option<(QualifiedName, Option>)> { - match name { - "test_name" => Some(( - QualifiedName::from(vec![if simplify { - "test_name_simplified" - } else { - "test_name" - }]), - Some(Type::bool()), - )), - "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), - _ => None, - } - } - } - - Demangler::register("Test", TestDemangler); - let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86_64 exists"); + let platform = placeholder_arch + .standalone_platform() + .expect("x86_64 standalone platform exists"); + let config = DemanglerConfig::for_platform(&platform, true); - let demangled = demangle_generic(&placeholder_arch, "test_name", None, true).unwrap(); + let demangled = demangle_any("test_name", &config).unwrap(); assert_eq!( demangled, - ( + DemanglerResult::new( QualifiedName::from(vec!["test_name_simplified"]), Some(Type::bool()) ) @@ -92,11 +98,11 @@ fn test_custom_demangler() { let unsimplified = demangle_generic(&placeholder_arch, "test_name", None, false).unwrap(); assert_eq!( unsimplified, - (QualifiedName::from(vec!["test_name"]), Some(Type::bool())) + DemanglerResult::new(QualifiedName::from(vec!["test_name"]), Some(Type::bool())) ); let demangled2 = demangle_generic(&placeholder_arch, "test_name2", None, true).unwrap(); assert_eq!( demangled2, - (QualifiedName::from(vec!["test_name2", "aaa"]), None) + DemanglerResult::new(QualifiedName::from(vec!["test_name2", "aaa"]), None) ); } diff --git a/type.cpp b/type.cpp index ff4f8eb02..f24c3b43f 100644 --- a/type.cpp +++ b/type.cpp @@ -20,6 +20,7 @@ #include "binaryninjaapi.h" #include +#include using namespace BinaryNinja; using namespace std; @@ -349,21 +350,21 @@ QualifiedName QualifiedName::operator+(const QualifiedName& other) const BNQualifiedName QualifiedName::GetAPIObject() const { BNQualifiedName result; - result.nameCount = m_name.size(); result.join = BNAllocString(m_join.c_str()); - result.name = new char*[m_name.size()]; - for (size_t i = 0; i < m_name.size(); i++) - result.name[i] = BNAllocString(m_name[i].c_str()); + std::vector namePtrs; + namePtrs.reserve(m_name.size()); + for (const auto& name : m_name) + namePtrs.push_back(name.c_str()); + result.nameCount = namePtrs.size(); + result.name = BNAllocStringList(namePtrs.data(), namePtrs.size()); return result; } void QualifiedName::FreeAPIObject(BNQualifiedName* name) { - for (size_t i = 0; i < name->nameCount; i++) - BNFreeString(name->name[i]); + BNFreeStringList(name->name, name->nameCount); BNFreeString(name->join); - delete[] name->name; } diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 7e8558468..58cb45896 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -808,6 +808,7 @@ bool ElfView::Init() if (!platform) platform = entryPointArch->GetStandalonePlatform(); + m_plat = platform; SetDefaultPlatform(platform); GetParentView()->SetDefaultPlatform(platform); @@ -2617,19 +2618,17 @@ void ElfView::DefineElfSymbol(BNSymbolType type, const string& incomingName, uin string shortName = rawName; string fullName = rawName; Confidence> typeRef = symbolTypeRef; - if (m_arch) + + DemanglerConfig config {m_plat, this, m_simplifyTemplates}; + if (auto demangled = Demangler::DemangleAny(rawName, config)) { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, this, m_simplifyTemplates)) - { - shortName = demangledName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !m_plat->GetFunctionByName(rawName)) + typeRef = demangledType; } if (!typeRef && m_arch && (m_arch->GetName() == "hexagon" || m_arch->GetName() == "tms320c6x")) diff --git a/view/kernelcache/api/kernelcache.cpp b/view/kernelcache/api/kernelcache.cpp index 8ded84b87..8fc3a3e0f 100644 --- a/view/kernelcache/api/kernelcache.cpp +++ b/view/kernelcache/api/kernelcache.cpp @@ -34,22 +34,6 @@ CacheSymbol SymbolFromApi(BNKernelCacheSymbol apiSymbol) return symbol; } -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType = nullptr; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return {shortName, outType}; -} - -Ref CacheSymbol::GetBNSymbol(BinaryView &view) const -{ - auto [shortName, _] = DemangledName(view); - return new Symbol(type, shortName, shortName, name, address, nullptr); -} - std::string KernelCacheAPI::GetSymbolTypeAsString(const BNSymbolType &type) { // NOTE: We currently only use the function and data symbol for cache symbols. diff --git a/view/kernelcache/api/kernelcacheapi.h b/view/kernelcache/api/kernelcacheapi.h index aceb30a5d..ed43b8d31 100644 --- a/view/kernelcache/api/kernelcacheapi.h +++ b/view/kernelcache/api/kernelcacheapi.h @@ -275,9 +275,6 @@ namespace KernelCacheAPI { BNSymbolType type; uint64_t address; std::string name; - - std::pair> DemangledName(BinaryNinja::BinaryView &view) const; - BinaryNinja::Ref GetBNSymbol(BinaryNinja::BinaryView& view) const; }; std::string GetSymbolTypeAsString(const BNSymbolType& type); diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp index 709ca5ff0..0d7513e5e 100644 --- a/view/kernelcache/core/KernelCache.cpp +++ b/view/kernelcache/core/KernelCache.cpp @@ -5,19 +5,16 @@ using namespace BinaryNinja; -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return { shortName, outType }; -} - std::pair, Ref> CacheSymbol::GetBNSymbolAndType(BinaryView& view) const { - auto [shortName, demangledType] = DemangledName(view); + Ref demangledType; + std::string shortName = name; + DemanglerConfig config {view.GetDefaultPlatform(), &view, true}; + if (auto demangled = Demangler::DemangleAny(name, config)) + { + shortName = demangled->name.GetString(); + demangledType = demangled->type; + } auto symbol = new Symbol(type, shortName, shortName, name, address, nullptr); return {symbol, demangledType}; } diff --git a/view/kernelcache/core/KernelCache.h b/view/kernelcache/core/KernelCache.h index 389742259..38e2da558 100644 --- a/view/kernelcache/core/KernelCache.h +++ b/view/kernelcache/core/KernelCache.h @@ -27,8 +27,6 @@ struct CacheSymbol CacheSymbol(CacheSymbol&& other) noexcept = default; CacheSymbol& operator=(CacheSymbol&& other) noexcept = default; - std::pair> DemangledName(BinaryNinja::BinaryView& view) const; - // NOTE: you should really only call this when adding the symbol to the view. std::pair, BinaryNinja::Ref> GetBNSymbolAndType(BinaryNinja::BinaryView& view) const; }; diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index 0d17b6856..d47ff6029 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -2061,6 +2061,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ if (header.m_entryPoints.size() > 0 && !platformSetByUser) platform = platform->GetAssociatedPlatformByAddress(header.m_entryPoints[0]); + m_plat = platform; SetDefaultPlatform(platform); SetDefaultArchitecture(platform->GetArchitecture()); @@ -2721,19 +2722,17 @@ Ref MachoView::DefineMachoSymbol( string fullName = rawName; Ref typeRef = symbolTypeRef; - if (m_arch) - { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, nullptr, m_simplifyTemplates)) - { - shortName = demangledName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } + DemanglerConfig config {m_plat, this, m_simplifyTemplates}; + auto demangled = Demangler::DemangleAny(rawName, config); + if (demangled) + { + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !m_plat->GetFunctionByName(rawName)) + typeRef = demangledType; } if ((type == ExternalSymbol || type == ImportAddressSymbol) diff --git a/view/pe/coffview.cpp b/view/pe/coffview.cpp index 238dc4240..3c6c34a99 100644 --- a/view/pe/coffview.cpp +++ b/view/pe/coffview.cpp @@ -1529,11 +1529,11 @@ void COFFView::AddCOFFSymbol(BNSymbolType type, const string& dll, const string& if (m_arch && name.size() > 0) { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, this, m_simplifyTemplates)) + DemanglerConfig config {GetDefaultPlatform(), this, m_simplifyTemplates}; + if (auto demangled = Demangler::DemangleAny(rawName, config)) { - shortName = demangledName.GetString(); + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); fullName = shortName; if (demangledType) fullName += demangledType->GetStringAfterName(); diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp index e7ef38d17..dd245699c 100644 --- a/view/pe/peview.cpp +++ b/view/pe/peview.cpp @@ -3565,11 +3565,11 @@ void PEView::AddPESymbol(BNSymbolType type, const string& dll, const string& nam if (m_arch && name.size() > 0) { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(GetDefaultPlatform(), rawName, demangledType, demangledName, this, m_simplifyTemplates)) + DemanglerConfig config {GetDefaultPlatform(), this, m_simplifyTemplates}; + if (auto demangled = Demangler::DemangleAny(rawName, config)) { - shortName = demangledName.GetString(); + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); fullName = shortName; if (demangledType) fullName += demangledType->GetStringAfterName(); diff --git a/view/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp index 7417f2707..423b8a776 100644 --- a/view/sharedcache/api/sharedcache.cpp +++ b/view/sharedcache/api/sharedcache.cpp @@ -143,22 +143,6 @@ std::string SharedCacheAPI::GetRegionTypeAsString(const BNSharedCacheRegionType } } -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType = nullptr; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return {shortName, outType}; -} - -Ref CacheSymbol::GetBNSymbol(BinaryView &view) const -{ - auto [shortName, _] = DemangledName(view); - return new Symbol(type, shortName, shortName, name, address, binding); -} - std::string SharedCacheAPI::GetSymbolTypeAsString(const BNSymbolType &type) { // NOTE: We currently only use the function and data symbol for cache symbols. diff --git a/view/sharedcache/api/sharedcacheapi.h b/view/sharedcache/api/sharedcacheapi.h index dc80b9883..e77ea87a6 100644 --- a/view/sharedcache/api/sharedcacheapi.h +++ b/view/sharedcache/api/sharedcacheapi.h @@ -291,9 +291,6 @@ namespace SharedCacheAPI { BNSymbolBinding binding = NoBinding; uint64_t address; std::string name; - - std::pair> DemangledName(BinaryNinja::BinaryView &view) const; - BinaryNinja::Ref GetBNSymbol(BinaryNinja::BinaryView& view) const; }; std::string GetSymbolTypeAsString(const BNSymbolType& type); diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index 2d4f6d0dd..df85b980a 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -8,19 +8,16 @@ using namespace BinaryNinja; -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultPlatform(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return { shortName, outType }; -} - std::pair, Ref> CacheSymbol::GetBNSymbolAndType(BinaryView& view) const { - auto [shortName, demangledType] = DemangledName(view); + Ref demangledType; + std::string shortName = name; + DemanglerConfig config {view.GetDefaultPlatform(), &view, true}; + if (auto demangled = Demangler::DemangleAny(name, config)) + { + shortName = demangled->name.GetString(); + demangledType = demangled->type; + } auto symbol = new Symbol(type, shortName, shortName, name, address, binding); return {symbol, demangledType}; } diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 8ecd8f1de..88ef8f57d 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -27,8 +27,6 @@ struct CacheSymbol CacheSymbol(CacheSymbol&& other) noexcept = default; CacheSymbol& operator=(CacheSymbol&& other) noexcept = default; - std::pair> DemangledName(BinaryNinja::BinaryView& view) const; - // NOTE: you should really only call this when adding the symbol to the view. std::pair, BinaryNinja::Ref> GetBNSymbolAndType(BinaryNinja::BinaryView& view) const; }; diff --git a/view/sharedcache/workflow/SharedCacheWorkflow.cpp b/view/sharedcache/workflow/SharedCacheWorkflow.cpp index ff42e0c5b..7d0664026 100644 --- a/view/sharedcache/workflow/SharedCacheWorkflow.cpp +++ b/view/sharedcache/workflow/SharedCacheWorkflow.cpp @@ -85,8 +85,10 @@ void IdentifyStub(BinaryView& view, const SharedCacheController& controller, uin if (!symbol.has_value()) return; - // TODO: The demangled type here is almost always wrong so we omit it for now. - auto [demangledName, demangledType] = symbol->DemangledName(view); + std::string demangledName = symbol->name; + DemanglerConfig config {view.GetDefaultPlatform(), &view, true}; + if (auto demangled = Demangler::DemangleAny(symbol->name, config)) + demangledName = demangled->name.GetString(); auto rawName = STUB_PREFIX + symbol->name; auto shortName = STUB_PREFIX + demangledName; @@ -95,8 +97,6 @@ void IdentifyStub(BinaryView& view, const SharedCacheController& controller, uin { // NOTE: The type library name is expected to be the image name currently. // Try and pull the type from the associated type library (if there is one) - // TODO: The demangled type here is missing a param - // Ref selectedType = demangledType; Ref selectedType = nullptr; if (const auto image = controller.GetImageContaining(symbolAddr)) { @@ -270,7 +270,12 @@ void AnalyzeStandardFunction(Ref func, Ref mlil const auto symbol = controller.GetSymbolAt(symbolAddr); if (!symbol.has_value()) return false; - view->DefineAutoSymbol(symbol->GetBNSymbol(*view)); + std::string shortName = symbol->name; + DemanglerConfig config {view->GetDefaultPlatform(), view, true}; + if (auto demangled = Demangler::DemangleAny(symbol->name, config)) + shortName = demangled->name.GetString(); + view->DefineAutoSymbol( + new Symbol(symbol->type, shortName, shortName, symbol->name, symbol->address, symbol->binding)); return true; }; From fa382f8c582684399966ab5e15d5c4a90f5a4296 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Fri, 3 Jul 2026 18:01:48 -0400 Subject: [PATCH 09/14] Fix demangler API consumer builds --- plugins/idb_import/CMakeLists.txt | 1 + plugins/pdb-ng/CMakeLists.txt | 1 + plugins/pdb-ng/src/symbol_parser.rs | 6 ++++-- plugins/warp/src/convert/symbol.rs | 19 +++++++---------- plugins/workflow_objc/CMakeLists.txt | 1 + plugins/workflow_swift/CMakeLists.txt | 1 + plugins/workflow_swift/src/demangler/mod.rs | 23 ++++++++++----------- plugins/workflow_swift/src/lib.rs | 2 +- rust/src/rc.rs | 8 ------- 9 files changed, 28 insertions(+), 34 deletions(-) diff --git a/plugins/idb_import/CMakeLists.txt b/plugins/idb_import/CMakeLists.txt index 735ac3308..aca4635e7 100644 --- a/plugins/idb_import/CMakeLists.txt +++ b/plugins/idb_import/CMakeLists.txt @@ -12,6 +12,7 @@ file(GLOB API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/* ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs) if(CMAKE_BUILD_TYPE MATCHES Debug) diff --git a/plugins/pdb-ng/CMakeLists.txt b/plugins/pdb-ng/CMakeLists.txt index 50f0f2af8..5c2681fc6 100644 --- a/plugins/pdb-ng/CMakeLists.txt +++ b/plugins/pdb-ng/CMakeLists.txt @@ -60,6 +60,7 @@ file(GLOB API_SOURCES CONFIGURE_DEPENDS ${BN_API_SOURCE_DIR}/rust/binaryninjacore-sys/Cargo.toml ${BN_API_SOURCE_DIR}/rust/binaryninjacore-sys/src/* ${BN_API_SOURCE_DIR}/rust/Cargo.toml + ${BN_API_SOURCE_DIR}/rust/src/*.rs ${BN_API_SOURCE_DIR}/rust/src/*/*.rs) set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_FILE_PATH ${OUTPUT_FILE_PATH}) diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index eeb54040f..765c1741f 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -1818,8 +1818,10 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &mut self.settings_query_opts.clone(), ); let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, simplify_templates) { - Some((name, Some(t))) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name), - Some((name, _)) => (None, name), + Some(result) => ( + result.ty.map(|ty| Conf::new(ty, DEMANGLE_CONFIDENCE)), + result.name, + ), _ => (None, QualifiedName::new(vec![raw_name.clone()])), }; diff --git a/plugins/warp/src/convert/symbol.rs b/plugins/warp/src/convert/symbol.rs index 5d794d30f..dfb3f10ea 100644 --- a/plugins/warp/src/convert/symbol.rs +++ b/plugins/warp/src/convert/symbol.rs @@ -1,4 +1,5 @@ use binaryninja::binary_view::BinaryView; +use binaryninja::demangle::{demangle_any, DemanglerConfig}; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::Symbol as BNSymbol; use binaryninja::symbol::SymbolType as BNSymbolType; @@ -75,17 +76,13 @@ pub fn to_bn_symbol_at_address(view: &BinaryView, symbol: &Symbol, addr: u64) -> let raw_name = symbol.name.as_str(); let mut symbol_builder = BNSymbol::builder(symbol_type, &symbol.name, addr); // Demangle symbol name (short is with simplifications). - if let Some(arch) = view.default_arch() { - if let Some((full_name, _)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - symbol_builder = symbol_builder.full_name(full_name); - } - if let Some((short_name, _)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - symbol_builder = symbol_builder.short_name(short_name); - } + let full_config = DemanglerConfig::for_binary_view(view, false); + if let Some(result) = demangle_any(raw_name, &full_config) { + symbol_builder = symbol_builder.full_name(result.name); + } + let short_config = DemanglerConfig::for_binary_view(view, true); + if let Some(result) = demangle_any(raw_name, &short_config) { + symbol_builder = symbol_builder.short_name(result.name); } symbol_builder.create() } diff --git a/plugins/workflow_objc/CMakeLists.txt b/plugins/workflow_objc/CMakeLists.txt index 0d4262fd4..187b98549 100644 --- a/plugins/workflow_objc/CMakeLists.txt +++ b/plugins/workflow_objc/CMakeLists.txt @@ -81,6 +81,7 @@ file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs) find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin) diff --git a/plugins/workflow_swift/CMakeLists.txt b/plugins/workflow_swift/CMakeLists.txt index d9b027f32..ff9a59de6 100644 --- a/plugins/workflow_swift/CMakeLists.txt +++ b/plugins/workflow_swift/CMakeLists.txt @@ -81,6 +81,7 @@ file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs) find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin) diff --git a/plugins/workflow_swift/src/demangler/mod.rs b/plugins/workflow_swift/src/demangler/mod.rs index 75ddea326..415a397a2 100644 --- a/plugins/workflow_swift/src/demangler/mod.rs +++ b/plugins/workflow_swift/src/demangler/mod.rs @@ -2,12 +2,10 @@ mod function_type; mod name; mod type_reconstruction; -use binaryninja::architecture::CoreArchitecture; use binaryninja::binary_view::BinaryView; -use binaryninja::demangle::CustomDemangler; -use binaryninja::rc::Ref; +use binaryninja::demangle::{CustomDemangler, DemanglerConfig, DemanglerResult}; use binaryninja::settings::{QueryOptions, Settings}; -use binaryninja::types::{QualifiedName, Type}; +use binaryninja::types::QualifiedName; fn should_extract_types(view: Option<&BinaryView>) -> bool { let mut opts = match view { @@ -32,26 +30,27 @@ impl CustomDemangler for SwiftDemangler { fn demangle( &self, - arch: &CoreArchitecture, name: &str, - view: Option>, - _simplify: bool, - ) -> Option<(QualifiedName, Option>)> { + config: &DemanglerConfig, + ) -> Option { let ctx = swift_demangler::Context::new(); let symbol = swift_demangler::Symbol::parse(&ctx, name)?; - if should_extract_types(view.as_deref()) { - let ty = function_type::build_function_type(&symbol, arch); + if should_extract_types(config.view.as_deref()) { + let ty = config + .platform + .as_ref() + .and_then(|platform| function_type::build_function_type(&symbol, &platform.arch())); let qname = if ty.is_some() { name::build_short_name(&symbol) } else { None } .unwrap_or_else(|| QualifiedName::from(symbol.display())); - Some((qname, ty)) + Some(DemanglerResult::new(qname, ty)) } else { let qname = QualifiedName::from(symbol.display()); - Some((qname, None)) + Some(DemanglerResult::new(qname, None)) } } } diff --git a/plugins/workflow_swift/src/lib.rs b/plugins/workflow_swift/src/lib.rs index c8af5d26e..911770ae1 100644 --- a/plugins/workflow_swift/src/lib.rs +++ b/plugins/workflow_swift/src/lib.rs @@ -30,5 +30,5 @@ pub extern "C" fn CorePluginInit() -> bool { }"#, ); - Demangler::register("Swift", SwiftDemangler).is_some() + Demangler::register("Swift", SwiftDemangler) } diff --git a/rust/src/rc.rs b/rust/src/rc.rs index 7635455ac..327560d6b 100644 --- a/rust/src/rc.rs +++ b/rust/src/rc.rs @@ -318,14 +318,6 @@ where #[allow(private_bounds)] impl ArrayGuard

{ - pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self { - Self { - contents: raw, - count, - context, - } - } - #[inline] pub fn len(&self) -> usize { self.count From 0a4fe63e504cfc92a3f38c0c86c8e27768004134 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Tue, 14 Jul 2026 10:01:36 -0400 Subject: [PATCH 10/14] Add BNSimplifyDemangledTemplateName API --- binaryninjaapi.h | 2 ++ binaryninjacore.h | 2 ++ demangle.cpp | 15 +++++++++++++++ python/demangle.py | 23 +++++++++++++++++++++++ rust/src/demangle.rs | 13 +++++++++++++ rust/tests/demangler.rs | 15 +++++++++++++-- 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 8c1897aba..2ee7b016c 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2488,6 +2488,8 @@ namespace BinaryNinja { */ bool IsGNU3MangledString(const std::string& mangledName); + QualifiedName SimplifyDemangledTemplateName(const QualifiedName& name); + /*! \ingroup mainthread */ diff --git a/binaryninjacore.h b/binaryninjacore.h index cc2e3e314..035df4b71 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -8273,6 +8273,8 @@ extern "C" BINARYNINJACOREAPI bool BNDemangleWithDemangler(const BNDemangler* demangler, const char* name, const BNDemanglerConfig* config, BNDemanglerResult* result); BINARYNINJACOREAPI void BNFreeDemanglerResult(BNDemanglerResult* result); + BINARYNINJACOREAPI bool BNSimplifyDemangledTemplateName( + const BNQualifiedName* name, BNQualifiedName* result); BINARYNINJACOREAPI BNDemangler* BNRegisterDemangler(const char* name, const BNDemanglerCallbacks* callbacks); BINARYNINJACOREAPI BNDemangler** BNGetDemanglerList(size_t* count); diff --git a/demangle.cpp b/demangle.cpp index 11a421d4f..ac07c4c85 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -160,6 +160,21 @@ namespace BinaryNinja return demangler && BNIsDemanglerMangledName(demangler, mangledName.c_str()); } + QualifiedName SimplifyDemangledTemplateName(const QualifiedName& name) + { + BNQualifiedName apiName = name.GetAPIObject(); + BNQualifiedName apiResult = {}; + if (!BNSimplifyDemangledTemplateName(&apiName, &apiResult)) + { + QualifiedName::FreeAPIObject(&apiName); + return name; + } + + QualifiedName result = QualifiedName::FromAPIObject(&apiResult); + QualifiedName::FreeAPIObject(&apiName); + BNFreeQualifiedName(&apiResult); + return result; + } Demangler::Demangler(std::string demanglerName): m_nameForRegister(std::move(demanglerName)) { diff --git a/python/demangle.py b/python/demangle.py index 977e68930..1de5db99e 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -289,6 +289,29 @@ def demangle_gnu3( return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, core.BNGetGNU3Demangler) +def simplify_demangled_template_name( + name: Union[str, Iterable[str], types.QualifiedName]) -> types.QualifiedName: + """ + ``simplify_demangled_template_name`` simplifies standard-library template spelling in + an already demangled qualified name. + """ + if isinstance(name, types.QualifiedName): + qualified_name = name + elif isinstance(name, str): + qualified_name = types.QualifiedName(name) + else: + qualified_name = types.QualifiedName(list(name)) + + api_name = qualified_name._to_core_struct() + result = core.BNQualifiedName() + if not core.BNSimplifyDemangledTemplateName(ctypes.byref(api_name), ctypes.byref(result)): + return qualified_name + try: + return types.QualifiedName._from_core_struct(result) + finally: + core.BNFreeQualifiedName(result) + + class _DemanglerMetaclass(type): def __iter__(self): binaryninja._init_plugins() diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 5fb874690..fc76ff7fc 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -285,6 +285,19 @@ pub fn demangle_ms( demangle_with_demangler(msvc_demangler(), mangled_name, &config) } +pub fn simplify_demangled_template_name(name: impl Into) -> QualifiedName { + let name = name.into(); + let mut raw_name = QualifiedName::into_raw(name.clone()); + let mut result = BNQualifiedName::default(); + let ok = unsafe { BNSimplifyDemangledTemplateName(&mut raw_name, &mut result) }; + QualifiedName::free_raw(raw_name); + if ok { + QualifiedName::from_owned_raw(result) + } else { + name + } +} + #[derive(PartialEq, Eq, Hash)] pub struct Demangler { pub(crate) handle: *mut BNDemangler, diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index c312d8ba4..6ce2f59d0 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -1,7 +1,7 @@ use binaryninja::architecture::{ArchitectureExt, CoreArchitecture}; use binaryninja::demangle::{ - demangle_any, demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, CustomDemangler, - Demangler, DemanglerConfig, DemanglerResult, + demangle_any, demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, + simplify_demangled_template_name, CustomDemangler, Demangler, DemanglerConfig, DemanglerResult, }; use binaryninja::headless::Session; use binaryninja::types::{QualifiedName, Type}; @@ -78,6 +78,17 @@ fn test_demangler_simple() { ); } +#[test] +fn test_simplify_demangled_template_name() { + let _session = session(); + + let simplified = simplify_demangled_template_name( + "std::basic_string, std::allocator >", + ); + assert_eq!(simplified, QualifiedName::from(vec!["std", "string"])); + assert_eq!(simplified.to_string(), "std::string"); +} + #[test] fn test_custom_demangler() { let _session = session(); From ad521ac26937201ff6a1fb2f9d04328dd2b1c215 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Sun, 5 Jul 2026 13:00:02 -0400 Subject: [PATCH 11/14] Convert GNU3 debug indentation to RAII --- demangler/demangled_log.h | 77 +++++++++++++++++++ demangler/gnu3/CMakeLists.txt | 1 + demangler/gnu3/demangle_gnu3.cpp | 127 +++++++++++++------------------ demangler/msvc/CMakeLists.txt | 1 + demangler/msvc/demangle_msvc.cpp | 35 ++++++++- 5 files changed, 164 insertions(+), 77 deletions(-) create mode 100644 demangler/demangled_log.h diff --git a/demangler/demangled_log.h b/demangler/demangled_log.h new file mode 100644 index 000000000..bdc0e6926 --- /dev/null +++ b/demangler/demangled_log.h @@ -0,0 +1,77 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// 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. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "demangler/demangled_type_node.h" + + +class DemangleLogIndentationScope; + +class DemangleLogIndentation +{ +public: + [[nodiscard]] static _STD_STRING Prefix() + { + return _STD_STRING(Depth(), ' '); + } + + [[nodiscard]] static _STD_STRING Apply(const char* format) + { + _STD_STRING indentedFormat = Prefix(); + indentedFormat += format; + return indentedFormat; + } + +private: + friend class DemangleLogIndentationScope; + + static size_t& Depth() + { + static thread_local size_t depth = 0; + return depth; + } + + static void Push() + { + Depth()++; + } + + static void Pop() + { + size_t& depth = Depth(); + if (depth > 0) + depth--; + } +}; + + +class DemangleLogIndentationScope +{ +public: + DemangleLogIndentationScope() + { + DemangleLogIndentation::Push(); + } + + ~DemangleLogIndentationScope() + { + DemangleLogIndentation::Pop(); + } + + DemangleLogIndentationScope(const DemangleLogIndentationScope&) = delete; + DemangleLogIndentationScope(DemangleLogIndentationScope&&) = delete; + DemangleLogIndentationScope& operator=(const DemangleLogIndentationScope&) = delete; + DemangleLogIndentationScope& operator=(DemangleLogIndentationScope&&) = delete; +}; diff --git a/demangler/gnu3/CMakeLists.txt b/demangler/gnu3/CMakeLists.txt index d39244d0c..05cfa3f64 100644 --- a/demangler/gnu3/CMakeLists.txt +++ b/demangler/gnu3/CMakeLists.txt @@ -6,6 +6,7 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.cpp *.c *.h + ../demangled_log.h ../demangled_template_simplifier.cpp ../demangled_template_simplifier.h ../demangled_type_node.cpp diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 0845b4d16..d4cffe1dd 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -16,6 +16,7 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_gnu3.h" +#include "demangler/demangled_log.h" #include "demangler/demangled_template_simplifier.h" #include #include @@ -41,25 +42,22 @@ static BNTypeClass GetFinalizedTypeClass(const Ref& type) #endif } -#define hash(x,y) (64 * x + y) +#define hash(x,y) (64 * (x) + (y)) -#undef GNUDEMANGLE_DEBUG -#ifdef GNUDEMANGLE_DEBUG // This makes it not thread safe! -static string _indent = ""; -#define indent() _indent += " "; -#define dedent() do {if (_indent.size() > 0) _indent = _indent.substr(1);}while(0); - -void MyLogDebug(const char* fmt, ...) +#ifdef GNUDEMANGLE_DEBUG +void LogWithIndentation(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(0, DebugLog, "", 0, (_indent + fmt).c_str(), args); + _STD_STRING indentedFormat = DemangleLogIndentation::Apply(fmt); + PerformLog(0, DebugLog, "", 0, indentedFormat.c_str(), args); va_end(args); } + +#define LOG_INDENTATION_SCOPE DemangleLogIndentationScope logIndentationScope #else -#define indent() -#define dedent() -#define MyLogDebug(...) do {} while(0) +#define LogWithIndentation(...) do {} while(0) +#define LOG_INDENTATION_SCOPE do {} while(0) #endif static size_t TotalStringSize(const StringList& v) @@ -394,7 +392,7 @@ DemangleGNU3::DemangleGNU3(Platform& platform, const string& mangledName) : m_inLocalName(false), m_nestingDepth(0) { - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); } void DemangleGNU3::Reset(Platform& platform, const string& mangledName) @@ -639,10 +637,9 @@ void DemangleGNU3::DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct) string DemangleGNU3::DemangleSourceName() { - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); m_lastName = m_reader.ReadString(DemangleNumber()); - dedent(); return m_lastName; } @@ -650,8 +647,8 @@ string DemangleGNU3::DemangleSourceName() DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) { NestingGuard nestingGuard(m_nestingDepth); - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); bool old_isparam; if (m_reader.Peek() == 'Y') { @@ -675,7 +672,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) continue; if (param.GetClass() == VoidTypeClass) continue; - MyLogDebug("Var_%d - %s\n", i++, param.GetString().c_str()); + LogWithIndentation("Var_%d - %s\n", i++, param.GetString().c_str()); if (!paramRef) paramRef = DemangledTypeNode::CreateShared(std::move(param)); m_functionSubstitute.back().push_back(paramRef); @@ -694,8 +691,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) if (cnst || vltl) PushType(newType); - MyLogDebug("After %s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); - dedent(); + LogWithIndentation("After %s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); return newType; } @@ -723,8 +719,8 @@ void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode&, const ParamLis DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef) { - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); if (outTypeRef) *outTypeRef = nullptr; size_t number = 0; @@ -754,7 +750,6 @@ DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* o { throw DemangleException(); } - dedent(); if (number < m_templateSubstitute.size()) { @@ -812,8 +807,8 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef DemangledTypeNode DemangleGNU3::DemangleType() { NestingGuard nestingGuard(m_nestingDepth); - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); m_lastTypeRef = nullptr; DemangledTypeNode type; NodeRef typeRef = nullptr; @@ -1086,7 +1081,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() break; } default: - MyLogDebug("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); throw DemangleException(); } break; @@ -1155,8 +1150,6 @@ DemangledTypeNode DemangleGNU3::DemangleType() if (substitute) typeRef = PushType(type); m_lastTypeRef = typeRef; - - dedent(); return type; } @@ -1173,8 +1166,8 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) static const StringList stdStringName(vector{"std", "string"}); static const StringList stdName(vector{"std"}); - indent() - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); char elm; elm = m_reader.Read(); StringList name; @@ -1223,8 +1216,6 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) { throw DemangleException(); } - - dedent(); NodeRef ref; if (number < m_substitute.size()) { @@ -1253,7 +1244,6 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) return resolved; } m_lastName = name.back(); - dedent(); return CreateUnknownType(name); } @@ -1300,8 +1290,8 @@ int64_t DemangleGNU3::DemangleNumber() string DemangleGNU3::DemanglePrimaryExpression() { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); char elm1 = '\0'; string out; StringList tmpList; @@ -1322,7 +1312,6 @@ string DemangleGNU3::DemanglePrimaryExpression() m_topLevel = oldTopLevel; m_templateSubstitute = std::move(savedTemplateSubstitute); out += t.GetTypeAndName(tmpList, m_platform); - dedent() return out; } // LZE: function address template arg (GCC/Clang, without leading underscore) @@ -1337,7 +1326,6 @@ string DemangleGNU3::DemanglePrimaryExpression() m_topLevel = oldTopLevel; m_templateSubstitute = std::move(savedTemplateSubstitute2); out += t2.GetTypeAndName(tmpList, m_platform); - dedent(); return out; } switch (m_reader.Read()) @@ -1384,8 +1372,6 @@ string DemangleGNU3::DemanglePrimaryExpression() } if (m_reader.Read() != 'E') throw DemangleException(); - - dedent(); return out; } @@ -1413,8 +1399,8 @@ string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op, DemangledTy string DemangleGNU3::DemangleBinaryExpression(const string& op, DemangledTypeNode* outNode) { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode lhsNode; DemangledTypeNode rhsNode; const string lhsExpr = DemangleExpression(outNode ? &lhsNode : nullptr); @@ -1431,7 +1417,6 @@ string DemangleGNU3::DemangleBinaryExpression(const string& op, DemangledTypeNod DemangledTypeNode::CreateShared(std::move(lhsNode)), op, DemangledTypeNode::CreateShared(std::move(rhsNode))); } - dedent(); return lhs + " " + op + " " + rhs; } @@ -1450,8 +1435,8 @@ string DemangleGNU3::DemangleTypeString() string DemangleGNU3::DemangleExpressionList() { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); string expr; bool first = true; m_functionSubstitute.push_back({}); @@ -1466,15 +1451,14 @@ string DemangleGNU3::DemangleExpressionList() } m_functionSubstitute.pop_back(); m_reader.Consume(); - dedent(); return expr; } DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() { - indent() - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode outType; char elm1 = m_reader.Read(); @@ -1699,7 +1683,6 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() string lastName = LastTypeNameSegmentBase(outType); m_lastName = lastName.empty() ? tag : lastName; } - dedent(); return outType; } @@ -1712,8 +1695,8 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() // ::= dn # destructor or pseudo-destructor; // # e.g. ~X or ~X - indent() - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); StringList out; if (m_reader.Length() > 1) { @@ -1754,15 +1737,14 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() } } } - dedent(); return out; } DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); // ::= [ ] # T:: or T:: // ::= # decltype(p):: // ::= @@ -1806,14 +1788,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() { throw DemangleException(); } - dedent(); return type; } string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) { - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); /* ::= ::= @@ -2425,7 +2406,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) } if (m_topLevel && tmpValid) { - MyLogDebug("Adding template ref: %s\n", tmpRef ? tmpRef->GetString().c_str() : ""); + LogWithIndentation("Adding template ref: %s\n", tmpRef ? tmpRef->GetString().c_str() : ""); PushTemplateType(tmpRef); } return true; @@ -2435,8 +2416,8 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) { NestingGuard nestingGuard(m_nestingDepth); - indent(); - MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); const string lastName = m_lastName; while (m_reader.Peek() != 'E') { @@ -2445,7 +2426,6 @@ void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) } m_reader.Consume(); m_lastName = lastName; - dedent(); return; } @@ -2483,8 +2463,8 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo ::= */ - indent(); - MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{}); bool cnst = false, vltl = false, rstrct = false; bool ref = false; @@ -2586,7 +2566,7 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo } PushType(type); } - MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); } m_reader.Consume(); @@ -2603,7 +2583,6 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo type.AddPointerSuffix(rvalueRef?LvalueSuffix:ReferenceSuffix); PushType(type); } - dedent(); return type; } @@ -2611,8 +2590,8 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo DemangledTypeNode DemangleGNU3::DemangleLocalName() { NestingGuard nestingGuard(m_nestingDepth); - indent(); - MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode type; StringList varName; // The local function has its own template scope. Save the outer template @@ -2680,7 +2659,6 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() DemangleNumberAsString(); } } - dedent(); return type; } @@ -2688,8 +2666,8 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() DemangledTypeNode DemangleGNU3::DemangleName() { NestingGuard nestingGuard(m_nestingDepth); - indent(); - MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); /* ::= ::= @@ -2769,7 +2747,6 @@ DemangledTypeNode DemangleGNU3::DemangleName() ApplyTemplateArgs(type, std::move(args)); } } - dedent(); return type; } @@ -2777,8 +2754,8 @@ DemangledTypeNode DemangleGNU3::DemangleName() DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplifyTemplates) { NestingGuard nestingGuard(m_nestingDepth); - indent(); - MyLogDebug("%s: %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode returnType; NodeRef returnTypeRef = nullptr; bool isReturnTypeUnknown = false; @@ -3249,7 +3226,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif } m_isParameter = true; - MyLogDebug("Var: %s\n", m_reader.GetRaw().c_str()); + LogWithIndentation("Var: %s\n", m_reader.GetRaw().c_str()); if (m_reader.PeekString(2) == "@@") break; DemangledTypeNode param = DemangleType(); @@ -3298,10 +3275,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); // PrintTables(); - MyLogDebug("Done: %s%s%s\n", type.GetStringBeforeName(m_platform).c_str(), JoinNameSegments(varName).c_str(), + LogWithIndentation("Done: %s%s%s\n", type.GetStringBeforeName(m_platform).c_str(), JoinNameSegments(varName).c_str(), type.GetStringAfterName(m_platform).c_str()); - - dedent(); return type; } diff --git a/demangler/msvc/CMakeLists.txt b/demangler/msvc/CMakeLists.txt index 7e635d6c5..1b12bad05 100644 --- a/demangler/msvc/CMakeLists.txt +++ b/demangler/msvc/CMakeLists.txt @@ -6,6 +6,7 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.cpp *.c *.h + ../demangled_log.h ../demangled_type_node.cpp ../demangled_type_node.h ../nesting_guard.h diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index 26aea6f02..67241bb69 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -16,6 +16,7 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_msvc.h" +#include "demangler/demangled_log.h" #include "demangler/demangled_template_simplifier.h" #include "base/unicode.h" #ifdef BINARYNINJACORE_LIBRARY @@ -66,9 +67,18 @@ static _STD_STRING FormatEncodedNumberLiteral(uint64_t magnitude, bool negative) // Define MSVC_DEMANGLE_DEBUG to enable trace logging #ifdef MSVC_DEMANGLE_DEBUG -#define MSVC_TRACE(...) LogTraceF(__VA_ARGS__) +template +void MsvcTraceWithIndentation(fmt::format_string format, Args&&... args) +{ + LogTraceF("{}{}", DemangleLogIndentation::Prefix(), + fmt::format(format, std::forward(args)...)); +} + +#define MSVC_TRACE(...) MsvcTraceWithIndentation(__VA_ARGS__) +#define MSVC_TRACE_SCOPE DemangleLogIndentationScope msvcTraceIndentationScope #else #define MSVC_TRACE(...) do {} while(0) +#define MSVC_TRACE_SCOPE do {} while(0) #endif _STD_STRING Demangle::Reader::ReadString(size_t count) @@ -301,6 +311,7 @@ Platform& Demangle::GetRenderingPlatform() const DemangledTypeNode Demangle::DemangleReferencedSymbolValue(BackrefList& varList) { + MSVC_TRACE_SCOPE; // Match LLVM's TemplateParameterReferenceNode parsing: referenced-symbol // non-type template arguments are parsed in the active backref context, so // later template arguments may refer to names/types introduced inside the @@ -316,6 +327,7 @@ DemangledTypeNode Demangle::DemangleReferencedSymbolValue(BackrefList& varList) DemangledTypeNode Demangle::DemangleAutoNonTypeTemplateParam(BackrefList& varList) { + MSVC_TRACE_SCOPE; if (m_reader.ConsumeIf('0')) { return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{DecodeEncodedNumberLiteral()}); @@ -332,6 +344,7 @@ DemangledTypeNode Demangle::DemangleVarType(BackrefList& varList, bool isReturn, bool includeImplicitThis, DemangledTypeNode::NodeRef* outTypeBackref, TypeBackrefMode typeBackrefMode) { NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}' - {}", __FUNCTION__, m_reader.GetRaw(), varList.nameList.size()); if (outTypeBackref) *outTypeBackref = nullptr; @@ -766,6 +779,7 @@ DemangledTypeNode Demangle::DemangleVarType(BackrefList& varList, bool isReturn, Demangle::EncodedNumber Demangle::DecodeEncodedNumber() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool negative = m_reader.ConsumeIf('?'); if (m_reader.Length() == 0) @@ -825,6 +839,7 @@ _STD_STRING Demangle::DecodeEncodedNumberLiteral() char Demangle::DemangleChar() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // Basic char is just the char if (!m_reader.ConsumeIf('?')) @@ -925,6 +940,7 @@ char Demangle::DemangleChar() void Demangle::DemangleVariableList(_STD_VECTOR& paramList, BackrefList& varList, bool typeBackrefs) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; uint8_t suffix = 0; @@ -1132,6 +1148,7 @@ void Demangle::DemangleNameTypeRtti(BNNameType& classFunctionType, void Demangle::DemangleTypeNameLookup(_STD_STRING& out, BNNameType& functionType) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); switch (m_reader.Read()) { @@ -1267,6 +1284,7 @@ void Demangle::DemangleTypeNameLookup(_STD_STRING& out, BNNameType& functionType DemangledNamePart Demangle::DemangleTemplateInstantiationName(BackrefList& nameBackrefList) { + MSVC_TRACE_SCOPE; DemangledNamePart out; MSVC_TRACE("DemangleTemplateInstantiationName: '{}'", m_reader.GetRaw()); if (!m_reader.ConsumeIf("?$")) @@ -1289,6 +1307,7 @@ DemangledNamePart Demangle::DemangleTemplateInstantiationName(BackrefList& nameB DemangledNamePart Demangle::DemangleTemplateInstantiationNameInLocalContext(BackrefList& nameBackrefList) { + MSVC_TRACE_SCOPE; DemangledNamePart out; BNNameType dummyFunctionType = NoNameType; MSVC_TRACE("DemangleTemplateInstantiationNameInLocalContext: '{}'", m_reader.GetRaw()); @@ -1317,6 +1336,7 @@ DemangledNamePart Demangle::DemangleTemplateInstantiationNameInLocalContext(Back void Demangle::DemangleTemplateParams(_STD_VECTOR& params, BackrefList& nameBackrefList, DemangledNamePart& out) { NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; params.clear(); const bool nestedTemplateContext = (m_templateParamDepth > 0); struct NameBackrefScopeGuard @@ -1358,6 +1378,7 @@ void Demangle::DemangleTemplateParams(_STD_VECTOR& par DemangledNamePart Demangle::DemangleUnqualifiedSymbolName(BackrefList& nameBackrefList, BNNameType& classFunctionType, bool& backrefEligible) { + MSVC_TRACE_SCOPE; backrefEligible = true; DemangledNamePart out; _STD_STRING text; @@ -1385,6 +1406,7 @@ DemangledNamePart Demangle::DemangleUnqualifiedSymbolName(BackrefList& nameBackr DemangledTypeNode Demangle::DemangleString(NameList& symbolName) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // ??_C@_@ if (!m_reader.ConsumeIf('_')) @@ -1533,6 +1555,7 @@ DemangledTypeNode Demangle::DemangleString(NameList& symbolName) DemangledTypeNode Demangle::DemangleTypeInfoName(NameList& symbolName) { + MSVC_TRACE_SCOPE; if (m_reader.Read() != '?') throw DemangleException("Unknown raw name type"); bool _const = false; @@ -1724,6 +1747,7 @@ void Demangle::DemangleName(NameList& nameList, bool typeNameContext) { NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; // NameList is stored outermost-first for QualifiedName, but MSVC encodes // names leaf-first. Ordinary parsed components are prepended; constructor // and destructor branches recurse to parse the class scope, then append the @@ -1907,6 +1931,7 @@ void Demangle::DemangleName(NameList& nameList, BNCallingConventionName Demangle::DemangleCallingConvention() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); switch (m_reader.Read()) { @@ -1944,6 +1969,7 @@ void Demangle::ConsumeExtendedModifierPrefix() uint8_t Demangle::DemanglePointerSuffix() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); uint8_t suffix = 0; if (m_reader.PeekOr() == '@') @@ -1971,6 +1997,7 @@ uint8_t Demangle::DemanglePointerSuffix() void Demangle::DemangleModifiers(bool& _const, bool& _volatile, bool &isMember) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // Always write the out params, even when `@` marks the no-modifiers case. _const = false; @@ -2085,6 +2112,7 @@ Demangle::DemangledFunction Demangle::DemangleFunction(BNNameType classFunctionT BackrefList& nameBackrefList, int funcClass) { NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false; uint8_t suffix = 0; @@ -2195,6 +2223,7 @@ Demangle::DemangledFunction Demangle::DemangleFunction(BNNameType classFunctionT DemangledTypeNode Demangle::DemangleData(BackrefList& varList) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; DemangledTypeNode newType = DemangleVarType(varList, false); @@ -2217,6 +2246,7 @@ DemangledTypeNode Demangle::DemangleData(BackrefList& varList) DemangledTypeNode Demangle::DemangleRTTI(BNNameType nameType, const NameList& symbolName) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; if (m_reader.Length() > 0) @@ -2234,6 +2264,7 @@ DemangledTypeNode Demangle::DemangleRTTI(BNNameType nameType, const NameList& sy DemangledTypeNode Demangle::DemangleVTable(BackrefList& nameBackrefList, NameList& symbolName) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; DemangleModifiers(_const, _volatile, isMember); @@ -2274,6 +2305,7 @@ DemangledTypeNode Demangle::DemangleVTable(BackrefList& nameBackrefList, NameLis // backtick pair: `dynamic initializer for `int foo''. Demangle::DemangleContext Demangle::DemangleDynamicInitFini(bool isDtor, BackrefList& backrefList) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // /d2FH4 may replace a long wrapped target with an MD5 name (??@@). @@ -2405,6 +2437,7 @@ Demangle::DemangleContext Demangle::DemangleSymbol() Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) { NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); BNNameType classFunctionType = NoNameType; NameList varName; From d49e9f93e8b0c62ccd064c2b39c3285974f45880 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Sun, 5 Jul 2026 13:00:24 -0400 Subject: [PATCH 12/14] Commonize GNU3 and MSVC demangler helpers --- demangler/demangled_reader.h | 231 +++++++++++++ demangler/gnu3/CMakeLists.txt | 4 +- demangler/gnu3/demangle_gnu3.cpp | 567 ++++++++++++------------------- demangler/gnu3/demangle_gnu3.h | 60 +--- demangler/msvc/CMakeLists.txt | 2 +- demangler/msvc/demangle_msvc.cpp | 24 +- demangler/msvc/demangle_msvc.h | 93 +---- demangler/nesting_guard.h | 57 ---- 8 files changed, 464 insertions(+), 574 deletions(-) create mode 100644 demangler/demangled_reader.h delete mode 100644 demangler/nesting_guard.h diff --git a/demangler/demangled_reader.h b/demangler/demangled_reader.h new file mode 100644 index 000000000..64a0ad78b --- /dev/null +++ b/demangler/demangled_reader.h @@ -0,0 +1,231 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// 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. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "demangler/demangled_type_node.h" + +#include +#include +#include +#include +#include + + +class DemangleException: public std::exception +{ + _STD_STRING m_message; + +public: + DemangleException(_STD_STRING msg = "Attempt to read beyond bounds or missing expected character"): + m_message(std::move(msg)) + {} + + [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } +}; + + +template +class DemangleNestingGuard +{ + size_t& m_depth; + size_t m_maxDepth; + +public: + explicit DemangleNestingGuard(size_t& depth, size_t maxDepth = MaxDepth): + m_depth(depth), + m_maxDepth(maxDepth) + { + m_depth++; + if (m_depth > m_maxDepth) + { + m_depth--; + throw DemangleException("Detected adversarial mangled string"); + } + } + + ~DemangleNestingGuard() + { + m_depth--; + } + + DemangleNestingGuard(const DemangleNestingGuard&) = delete; + DemangleNestingGuard(DemangleNestingGuard&&) = delete; + DemangleNestingGuard& operator=(const DemangleNestingGuard&) = delete; + DemangleNestingGuard& operator=(DemangleNestingGuard&&) = delete; +}; + + +class DemangleReader +{ + const char* m_begin = nullptr; + const char* m_ptr = nullptr; + const char* m_end = nullptr; + size_t m_maxReadStringLength = std::numeric_limits::max(); + bool m_throwOnPeekPastEnd = true; + + void ValidatePrintableAscii() const + { + for (const char* p = m_begin; p < m_end; p++) + if (*p < 0x20 || *p > 0x7e) + throw DemangleException(); + } + +public: + DemangleReader() = default; + + explicit DemangleReader(const _STD_STRING& data, + size_t maxReadStringLength = std::numeric_limits::max(), + bool throwOnPeekPastEnd = true): + m_maxReadStringLength(maxReadStringLength), + m_throwOnPeekPastEnd(throwOnPeekPastEnd) + { + Reset(data); + } + + void Reset(const _STD_STRING& data) + { + m_begin = data.c_str(); + m_ptr = m_begin; + m_end = m_begin + data.size(); + ValidatePrintableAscii(); + } + + [[nodiscard]] size_t Length() const { return static_cast(m_end - m_ptr); } + [[nodiscard]] size_t GetOffset() const { return static_cast(m_ptr - m_begin); } + + void SetOffset(size_t offset) + { + size_t length = static_cast(m_end - m_begin); + m_ptr = m_begin + std::min(offset, length); + } + + void UnRead(size_t count = 1) + { + if (count <= GetOffset()) + m_ptr -= count; + } + + [[nodiscard]] bool PeekMatch(const char* str, size_t len) const + { + if (len > Length()) + return false; + return memcmp(m_ptr, str, len) == 0; + } + + template + [[nodiscard]] bool PeekMatch(const char (&str)[N]) const + { + return PeekMatch(str, N - 1); + } + + [[nodiscard]] _STD_STRING PeekString(size_t count = 1) const + { + if (count > Length()) + return "\0"; + return _STD_STRING(m_ptr, count); + } + + [[nodiscard]] char PeekAt(size_t offset) const + { + if (offset >= Length()) + throw DemangleException(); + return m_ptr[offset]; + } + + [[nodiscard]] char Peek() const + { + if (m_ptr >= m_end) + { + if (m_throwOnPeekPastEnd) + throw DemangleException(); + return '\0'; + } + return *m_ptr; + } + + [[nodiscard]] char PeekOr(char fallback = '\0') const + { + if (Length() == 0) + return fallback; + return *m_ptr; + } + + [[nodiscard]] const char* GetRaw() const { return m_ptr; } + + void SetRaw(const char* p) + { + if (p < m_begin || p > m_end) + throw DemangleException(); + m_ptr = p; + } + + [[nodiscard]] char Read() + { + if (m_ptr >= m_end) + throw DemangleException(); + return *m_ptr++; + } + + _STD_STRING ReadString(size_t count = 1) + { + if (count > Length()) + throw DemangleException(); + if (count > m_maxReadStringLength) + throw DemangleException("Demangled node exceeds maximum length"); + _STD_STRING out(m_ptr, count); + m_ptr += count; + return out; + } + + _STD_STRING ReadUntil(char sentinel) + { + const char* found = static_cast(memchr(m_ptr, sentinel, m_end - m_ptr)); + if (!found) + throw DemangleException(); + size_t count = static_cast(found - m_ptr); + _STD_STRING out = ReadString(count); + Consume(); // sentinel + return out; + } + + bool ConsumeIf(char ch) + { + if (PeekOr() != ch) + return false; + Consume(); + return true; + } + + bool ConsumeIf(const char* str, size_t len) + { + if (!PeekMatch(str, len)) + return false; + Consume(len); + return true; + } + + template + bool ConsumeIf(const char (&str)[N]) + { + return ConsumeIf(str, N - 1); + } + + void Consume(size_t count = 1) + { + if (count > Length()) + throw DemangleException(); + m_ptr += count; + } +}; diff --git a/demangler/gnu3/CMakeLists.txt b/demangler/gnu3/CMakeLists.txt index 05cfa3f64..22bd623ce 100644 --- a/demangler/gnu3/CMakeLists.txt +++ b/demangler/gnu3/CMakeLists.txt @@ -9,9 +9,9 @@ file(GLOB SOURCES CONFIGURE_DEPENDS ../demangled_log.h ../demangled_template_simplifier.cpp ../demangled_template_simplifier.h + ../demangled_reader.h ../demangled_type_node.cpp - ../demangled_type_node.h - ../nesting_guard.h) + ../demangled_type_node.h) if(DEMO) add_library(${PROJECT_NAME} STATIC ${SOURCES}) diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index d4cffe1dd..ad26d3539 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -18,7 +18,7 @@ #include "demangle_gnu3.h" #include "demangler/demangled_log.h" #include "demangler/demangled_template_simplifier.h" -#include +#include #include #include @@ -31,8 +31,6 @@ using namespace std; #endif -static constexpr size_t MAX_DEMANGLE_NODE_LENGTH = 8192; - static BNTypeClass GetFinalizedTypeClass(const Ref& type) { #ifdef BINARYNINJACORE_LIBRARY @@ -330,57 +328,11 @@ static string DecodeHexFloat(const string& hex, size_t byteCount) return hex; } - -// ===== Reader implementation (non-templated) ===== - -DemangleGNU3Reader::DemangleGNU3Reader(const string& data): m_data(data), m_offset(0) -{} - - -void DemangleGNU3Reader::Reset(const string& data) -{ - m_data = data; - m_offset = 0; -} - - -string DemangleGNU3Reader::PeekString(size_t count) -{ - if (count > Length()) - return "\0"; - return m_data.substr(m_offset, count); -} - - - -#ifdef GNUDEMANGLE_DEBUG -string DemangleGNU3Reader::GetRaw() -{ - return m_data.substr(m_offset); -} -#endif - - - -string DemangleGNU3Reader::ReadString(size_t count) -{ - if (count > Length()) - throw DemangleException(); - if (count > MAX_DEMANGLE_NODE_LENGTH) - throw DemangleException("Demangled node exceeds maximum length"); - - const string out = m_data.substr(m_offset, count); - m_offset += count; - return out; -} - - - - // ===== DemangleGNU3 implementation ===== DemangleGNU3::DemangleGNU3(Platform& platform, const string& mangledName) : - m_reader(mangledName), + m_mangledName(mangledName), + m_reader(m_mangledName, MAX_DEMANGLE_NODE_LENGTH, false), m_platform(platform), m_lastTypeRef(nullptr), m_isParameter(false), @@ -392,12 +344,13 @@ DemangleGNU3::DemangleGNU3(Platform& platform, const string& mangledName) : m_inLocalName(false), m_nestingDepth(0) { - LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); } void DemangleGNU3::Reset(Platform& platform, const string& mangledName) { - m_reader.Reset(mangledName); + m_mangledName = mangledName; + m_reader.Reset(m_mangledName); m_platform = std::ref(platform); m_substitute.clear(); m_templateSubstitute.clear(); @@ -621,16 +574,16 @@ void DemangleGNU3::DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct) { cnst = false; vltl = false; rstrct = false; //[] - while (1) - { - switch (m_reader.Peek()) - { - case 'r': rstrct = true; break; - case 'V': vltl = true; break; - case 'K': cnst = true; break; - default: return; - } - m_reader.Consume(1); + while (true) + { + if (m_reader.ConsumeIf('r')) + rstrct = true; + else if (m_reader.ConsumeIf('V')) + vltl = true; + else if (m_reader.ConsumeIf('K')) + cnst = true; + else + return; } } @@ -638,7 +591,7 @@ void DemangleGNU3::DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct) string DemangleGNU3::DemangleSourceName() { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); m_lastName = m_reader.ReadString(DemangleNumber()); return m_lastName; } @@ -648,12 +601,11 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) { NestingGuard nestingGuard(m_nestingDepth); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); bool old_isparam; - if (m_reader.Peek() == 'Y') + if (m_reader.ConsumeIf('Y')) { // TODO: This function is external, should we do anything with that info? - m_reader.Consume(); } DemangledTypeNode retType = DemangleType(); @@ -664,7 +616,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) m_isParameter = true; m_functionSubstitute.push_back({}); [[maybe_unused]] int i = 0; - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { DemangledTypeNode param = DemangleType(); NodeRef paramRef = m_lastTypeRef; @@ -678,7 +630,6 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) m_functionSubstitute.back().push_back(paramRef); params.push_back({"", paramRef}); } - m_reader.Consume(); m_functionSubstitute.pop_back(); m_isParameter = old_isparam; if (!retTypeRef) @@ -691,7 +642,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) if (cnst || vltl) PushType(newType); - LogWithIndentation("After %s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("After %s : %s\n", __FUNCTION__, m_reader.GetRaw()); return newType; } @@ -720,11 +671,11 @@ void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode&, const ParamLis DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef) { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); if (outTypeRef) *outTypeRef = nullptr; size_t number = 0; - char elm = m_reader.Peek(); + char elm = m_reader.PeekOr(); if (elm == '_') { number = 0; @@ -732,7 +683,7 @@ DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* o else if (isdigit(elm)) { size_t n = 0; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) n = n * 10 + (m_reader.Read() - '0'); number = n + 1; } @@ -746,7 +697,7 @@ DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* o throw DemangleException(); } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) { throw DemangleException(); } @@ -808,7 +759,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() { NestingGuard nestingGuard(m_nestingDepth); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); m_lastTypeRef = nullptr; DemangledTypeNode type; NodeRef typeRef = nullptr; @@ -835,12 +786,12 @@ DemangledTypeNode DemangleGNU3::DemangleType() { case 'S': { - if (isdigit(m_reader.Peek()) || m_reader.Peek() == '_' || isupper(m_reader.Peek())) + char next = m_reader.PeekOr(); + if (isdigit(next) || next == '_' || isupper(next)) { type = DemangleSubstitution(&typeRef); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); ApplyTemplateArgs(type, std::move(args)); @@ -850,9 +801,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() } else { - if (m_reader.Peek() == 't') + if (m_reader.ConsumeIf('t')) { - m_reader.Consume(1); type = DemangleUnqualifiedName(); auto qn = CopyQualifiedName(type); qn.insert(qn.begin(), DemangledNamePart("std")); @@ -863,9 +813,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() { type = DemangleSubstitution(&typeRef); } - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); bool dependentTemplatePrefix = LastTypeNameSegmentBase(type) == "basic_ostream" && TemplateArgsReferenceTemplateParam("I" + m_reader.PeekString(m_reader.Length())); if (substitute && !dependentTemplatePrefix) @@ -886,21 +835,18 @@ DemangledTypeNode DemangleGNU3::DemangleType() ::= Tu # dependent elaborated type specifier using 'union' ::= Te # dependent elaborated type specifier using 'enum' */ - if (m_reader.Peek() == 's') + if (m_reader.ConsumeIf('s')) { - m_reader.Consume(); type = DemangledTypeNode::NamedType(StructNamedTypeClass, StringList{DemangleSourceName()}); break; } - else if (m_reader.Peek() == 'u') + else if (m_reader.ConsumeIf('u')) { - m_reader.Consume(); type = DemangledTypeNode::NamedType(UnionNamedTypeClass, StringList{DemangleSourceName()}); break; } - else if (m_reader.Peek() == 'e') + else if (m_reader.ConsumeIf('e')) { - m_reader.Consume(); type = DemangledTypeNode::NamedTypeWithDefaultIntegerWidth( EnumNamedTypeClass, StringList{DemangleSourceName()}); break; @@ -912,9 +858,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() // trailing IE — it belongs to the enclosing nested-name and will be // processed by DemangleNestedName's 'I' case, which resolves forward refs. substitute = !m_permitForwardTemplateRefs && !typeRef.IsTemplateParamPack(); - if (!m_permitForwardTemplateRefs && m_reader.Peek() == 'I') + if (!m_permitForwardTemplateRefs && m_reader.ConsumeIf('I')) { - m_reader.Consume(); if (substitute) PushType(type); ParamList args; @@ -963,9 +908,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() // Commonly used for Objective-C block pointers: // U13block_pointer -> "void (params...) block_pointer" DemangledNamePart extName(DemangleSourceName()); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList targs; DemangleTemplateArgs(targs); if (!targs.empty()) @@ -984,9 +928,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() // Vendor extended type: u [] // e.g. u14__remove_cvref, u20__remove_reference_t DemangledNamePart extName(DemangleSourceName()); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList targs; DemangleTemplateArgs(targs); if (!targs.empty()) @@ -1064,14 +1007,14 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 't': case 'T': type = CreateUnknownType("decltype(" + DemangleExpression() + ")"); - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); break; case 'v': { // vector of size uint64_t size = DemangleNumber(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); NodeRef childRef = nullptr; DemangledTypeNode child = DemangleType(); @@ -1081,7 +1024,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() break; } default: - LogWithIndentation("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw()); throw DemangleException(); } break; @@ -1092,11 +1035,11 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'A': // ::= A _ // ::= A [] _ - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { // _ uint64_t size = DemangleNumber(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); NodeRef childRef = nullptr; DemangledTypeNode child = DemangleType(); @@ -1110,11 +1053,11 @@ DemangledTypeNode DemangleGNU3::DemangleType() //Since our type system doesn't support expressions as dimensions //we preserve the element type node and render a synthetic name at finalization. string dimension = "[]"; - if (m_reader.Peek() != '_') + if (m_reader.PeekOr() != '_') { dimension = "[" + DemangleExpression() + "]"; } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); DemangledTypeNode inner = DemangleType(); @@ -1134,10 +1077,9 @@ DemangledTypeNode DemangleGNU3::DemangleType() m_lastName = lastName; substitute = true; - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { substitute = false; - m_reader.Consume(); PushType(type); ParamList args; DemangleTemplateArgs(args); @@ -1167,7 +1109,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) static const StringList stdName(vector{"std"}); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); char elm; elm = m_reader.Read(); StringList name; @@ -1194,7 +1136,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) // This handles both single-char (S0_ ... SZ_) and // multi-char (S10_, S11_, ...) seq-ids. size_t base36 = isdigit(elm) ? (size_t)(elm - '0') : (size_t)(elm - 'A' + 10); - while (m_reader.Peek() != '_') + while (m_reader.PeekOr() != '_') { char c = m_reader.Read(); if (isdigit(c)) @@ -1212,7 +1154,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) throw DemangleException(); } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) { throw DemangleException(); } @@ -1221,7 +1163,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) { ref = GetTypeRef(number); } - else if (number == m_substitute.size() && m_reader.Peek() == 'I' && !m_substitute.empty()) + else if (number == m_substitute.size() && m_reader.PeekOr() == 'I' && !m_substitute.empty()) { // GNU/libstdc++ pack expansions can elide an empty template-id that // LLVM still effectively treats as occupying this substitution slot. @@ -1250,14 +1192,13 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) string DemangleGNU3::DemangleNumberAsString() { bool negativeFactor = false; - if (m_reader.Peek() == 'n') + if (m_reader.ConsumeIf('n')) { negativeFactor = true; - m_reader.Consume(); } string number; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) { number += m_reader.Read(); } @@ -1270,20 +1211,19 @@ string DemangleGNU3::DemangleNumberAsString() int64_t DemangleGNU3::DemangleNumber() { bool negative = false; - if (m_reader.Peek() == 'n') + if (m_reader.ConsumeIf('n')) { negative = true; - m_reader.Consume(); } - if (!isdigit(m_reader.Peek())) + if (!isdigit(m_reader.PeekOr())) throw DemangleException(); int64_t result = 0; do { result = result * 10 + (m_reader.Read() - '0'); - } while (isdigit(m_reader.Peek())); + } while (isdigit(m_reader.PeekOr())); return negative ? -result : result; } @@ -1291,15 +1231,14 @@ int64_t DemangleGNU3::DemangleNumber() string DemangleGNU3::DemanglePrimaryExpression() { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); char elm1 = '\0'; string out; StringList tmpList; bool oldTopLevel; //expr-primary - if (m_reader.PeekString(2) == "_Z") + if (m_reader.ConsumeIf("_Z")) { - m_reader.Consume(2); // The embedded _Z... is an independent mangled name with its own // template scope. Save and clear the template substitution table // so inner T_ / T0_ etc. resolve within this symbol, not the outer @@ -1315,9 +1254,8 @@ string DemangleGNU3::DemanglePrimaryExpression() return out; } // LZE: function address template arg (GCC/Clang, without leading underscore) - if (m_reader.Peek() == 'Z') + if (m_reader.ConsumeIf('Z')) { - m_reader.Consume(); // 'Z' auto savedTemplateSubstitute2 = m_templateSubstitute; m_templateSubstitute.clear(); oldTopLevel = m_topLevel; @@ -1370,7 +1308,7 @@ string DemangleGNU3::DemanglePrimaryExpression() break; } } - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); return out; } @@ -1400,7 +1338,7 @@ string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op, DemangledTy string DemangleGNU3::DemangleBinaryExpression(const string& op, DemangledTypeNode* outNode) { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode lhsNode; DemangledTypeNode rhsNode; const string lhsExpr = DemangleExpression(outNode ? &lhsNode : nullptr); @@ -1436,11 +1374,11 @@ string DemangleGNU3::DemangleTypeString() string DemangleGNU3::DemangleExpressionList() { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); string expr; bool first = true; m_functionSubstitute.push_back({}); - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { if (!first) expr += ", "; @@ -1450,7 +1388,6 @@ string DemangleGNU3::DemangleExpressionList() first = false; } m_functionSubstitute.pop_back(); - m_reader.Consume(); return expr; } @@ -1458,7 +1395,7 @@ string DemangleGNU3::DemangleExpressionList() DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode outType; char elm1 = m_reader.Read(); @@ -1575,8 +1512,6 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('D','t'): case hash('D','T'): outType = CreateUnknownType(DemangleExpression()); - // if (m_reader.Read() != 'E') - // throw DemangleException(); break; case hash('U','l'): //Lambda { @@ -1596,16 +1531,17 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() if (param.GetClass() == VoidTypeClass) break; lambdaParams.push_back(std::move(param)); - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (m_reader.PeekOr() != 'E'); + if (!m_reader.ConsumeIf('E')) + throw DemangleException(); m_parsingLambdaParams = savedParsingLambdaParams; m_lambdaTemplateParamBase = savedLambdaTemplateParamBase; - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { name += DemangleNumberAsString(); } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); name += "'("; @@ -1626,13 +1562,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() string name; name = "'unnamed"; - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { name += DemangleNumberAsString(); } name += "\'"; - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); m_lastName = name; @@ -1660,7 +1596,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() } default: m_reader.UnRead(2); - if (isdigit(m_reader.Peek()) || m_reader.Read() == 'L') + if (isdigit(m_reader.PeekOr()) || m_reader.ConsumeIf('L')) { string name = DemangleSourceName(); if (name.size() > 11 && name.substr(0, 11) == "_GLOBAL__N_") @@ -1675,9 +1611,8 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() } // Consume ABI tags: B => [abi:tagname] // Applies to source names, operator names, and unnamed types. - while (m_reader.Peek() == 'B') + while (m_reader.ConsumeIf('B')) { - m_reader.Consume(); string tag = "[abi:" + DemangleSourceName() + "]"; ExtendTypeName(outType, tag); string lastName = LastTypeNameSegmentBase(outType); @@ -1696,27 +1631,24 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() // # e.g. ~X or ~X LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); StringList out; if (m_reader.Length() > 1) { - const string str = m_reader.PeekString(2); - if (str == "on") + if (m_reader.ConsumeIf("on")) { - m_reader.Consume(); m_reader.Consume(); // skip 'o','n' prefix char op1 = m_reader.Read(); char op2 = m_reader.Read(); out.push_back(GetOperator(op1, op2)); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); out.back() = NameSegmentWithTemplateArgs(out.back(), std::move(args)).GetString(); PushType(CreateUnknownType(out)); } } - else if (str == "dn") + else if (m_reader.PeekMatch("dn")) { string name = DemangleUnresolvedType().GetString(); if (name.empty()) @@ -1728,9 +1660,8 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() { // out.push_back(DemangleSourceName()); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); out.back() = NameSegmentWithTemplateArgs(out.back(), std::move(args)).GetString(); @@ -1744,19 +1675,17 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() { LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); // ::= [ ] # T:: or T:: // ::= # decltype(p):: // ::= DemangledTypeNode type; - if (m_reader.Peek() == 'T') + if (m_reader.ConsumeIf('T')) { - m_reader.Consume(); type = DemangleTemplateSubstitution(); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { PushType(type); - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); ApplyTemplateArgs(type, std::move(args)); @@ -1770,18 +1699,15 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() PushType(type); } } - else if (m_reader.Length() > 2 && (m_reader.PeekString(2) == "Dt" || m_reader.PeekString(2) == "DT")) + else if (m_reader.Length() > 2 && (m_reader.ConsumeIf("Dt") || m_reader.ConsumeIf("DT"))) { - m_reader.Consume(); // 'D' - m_reader.Consume(); // 't' or 'T' const string name = "decltype(" + DemangleExpression() + ")"; - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); type = CreateUnknownType(name); } - else if (m_reader.Peek() == 'S') + else if (m_reader.ConsumeIf('S')) { - m_reader.Consume(); type = DemangleSubstitution(); } else @@ -1794,7 +1720,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) { - LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); /* ::= ::= @@ -1982,7 +1908,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) string args; bool firstArg = true; m_functionSubstitute.push_back({}); - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { if (!firstArg) args += ", "; const string e = DemangleExpression(); @@ -1991,16 +1917,14 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) firstArg = false; } m_functionSubstitute.pop_back(); - m_reader.Consume(); // 'E' return callable + "(" + args + ")"; } case hash('c','v'): //type (expression) { DemangledTypeNode type = DemangleType(); out = type.GetString(); - if (m_reader.Peek() == '_') + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(); // consume '_' delimiter before expression list out += " (" + DemangleExpressionList() + ")"; } else @@ -2015,7 +1939,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) } case hash('i', 'l'): //{expr-list}, braced-init-list in any other context out = DemangleExpression(); - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); return out; case hash('f','p'): @@ -2038,14 +1962,13 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) // type before function params are known), the fallback paths below produce // a placeholder string "fp" / "fpN". listNumber = DemangleNumber() + 1; - if (listNumber < 0 || m_reader.Read() != 'p') + if (listNumber < 0 || !m_reader.ConsumeIf('p')) throw DemangleException(); } DemangleCVQualifiers(cnst, vltl, rstrct); - elm = m_reader.Peek(); - if (elm == '_') + elm = m_reader.PeekOr(); + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(1); if ((uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || (size_t)elementNum >= m_functionSubstitute[listNumber].size()) { @@ -2060,7 +1983,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) else if (isdigit(elm) || isupper(elm)) { elementNum = DemangleNumber() + 1; - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); if (elementNum < 0 || (uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || @@ -2105,15 +2028,14 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) # e.g. ~X or ~X */ DemangledQualifiedName structuredName; - if (m_reader.Peek() == 'N') + if (m_reader.ConsumeIf('N')) { - m_reader.Consume(); // Standard form: N + E // where is T_, Dt, or S. // GCC extension: N + E // When the first component is a digit (source name), skip the // unresolved-type and let the loop below handle all qualifiers. - if (!isdigit(m_reader.Peek())) + if (!isdigit(m_reader.PeekOr())) { DemangledTypeNode unresolvedType = DemangleUnresolvedType(); out += unresolvedType.GetString() + "::"; @@ -2127,10 +2049,9 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) DemangledNamePart structuredSegment(segName); // Push bare name (before template args) to substitution table. PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); // DemangleTemplateArgs(args); structuredSegment = NameSegmentWithTemplateArgs(segName, std::move(args)); @@ -2141,8 +2062,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) } structuredName.push_back(std::move(structuredSegment)); out += "::"; - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (!m_reader.ConsumeIf('E')); StringList baseName = DemangleBaseUnresolvedName(); out += JoinNameSegments(baseName); @@ -2150,7 +2070,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) SetStructuredExpressionNode(outNode, structuredName); return out; } - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { // + E // GCC sometimes omits the explicit qualifier-list 'E' when the last @@ -2172,10 +2092,9 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) DemangledNamePart structuredSegment(segName); // Push bare name to substitution table. PushType(CreateUnknownType(out)); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); DemangleTemplateArgs(args); // consumes the trailing 'E' structuredSegment = NameSegmentWithTemplateArgs(segName, std::move(args)); out.resize(segmentStart); @@ -2186,12 +2105,11 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) } structuredName.push_back(std::move(structuredSegment)); out += "::"; - }while (!hadTemplateArgs && m_reader.Peek() != 'E'); + }while (!hadTemplateArgs && m_reader.PeekOr() != 'E'); // Consume qualifier-list 'E' if present. GCC sometimes omits it when // the last qualifier had template-args whose 'E' served double duty, // so check rather than unconditionally consuming. - if (m_reader.Peek() == 'E') - m_reader.Consume(); + m_reader.ConsumeIf('E'); StringList baseName = DemangleBaseUnresolvedName(); out += JoinNameSegments(baseName); AppendStructuredNameSegments(structuredName, baseName); @@ -2208,16 +2126,15 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) // Process any digit-started names: if a name has template args AND // another source name follows, it is an intermediate qualifier level; // otherwise it is the final base-unresolved-name. - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) { const string segName = DemangleSourceName(); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); DemangleTemplateArgs(args); DemangledNamePart structuredSegment = NameSegmentWithTemplateArgs(segName, args); - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { // Another source name follows — intermediate qualifier. // Push to the substitution table, mirroring what the @@ -2257,10 +2174,9 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) m_reader.UnRead(2); out = DemangleSourceName(); DemangledNamePart structuredSegment(out); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); // DemangleTemplateArgs(args); structuredSegment = NameSegmentWithTemplateArgs(out, std::move(args)); @@ -2277,10 +2193,9 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) bool DemangleGNU3::TryDemangleTemplateParamExpressionPackExpansion(string& expr, bool& emptyPack) { - if (m_reader.Length() < 3 || m_reader.PeekString(3) != "spT") + if (!m_reader.ConsumeIf("spT")) return false; - m_reader.Consume(3); // "spT" NodeRef entry = DemangleTemplateSubstitutionEntry(); if (entry.emptyTemplatePack) { @@ -2354,7 +2269,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) exprNode = CreateUnknownType(expr); args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); } - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); if (hadNonTypeArg) *hadNonTypeArg = true; break; @@ -2375,7 +2290,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) break; } case 'T': - if (m_reader.Peek() == 'n') + if (m_reader.ConsumeIf('n')) { // ::= // ::= Tn # non-type parameter @@ -2384,7 +2299,6 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) // for the following argument. Binary Ninja does not print those // synthetic parameter names, so consume the declaration type and // keep only the actual following template argument. - m_reader.Consume(); topLevel = m_topLevel; m_topLevel = false; DemangleType(); @@ -2417,14 +2331,13 @@ void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) { NestingGuard nestingGuard(m_nestingDepth); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); const string lastName = m_lastName; - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { if (!DemangleTemplateArg(args, hadNonTypeArg)) break; } - m_reader.Consume(); m_lastName = lastName; return; } @@ -2464,7 +2377,7 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo */ LOG_INDENTATION_SCOPE; - LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{}); bool cnst = false, vltl = false, rstrct = false; bool ref = false; @@ -2477,19 +2390,17 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo DemangleCVQualifiers(cnst, vltl, rstrct); //[] - if (m_reader.Peek() == 'R') + if (m_reader.ConsumeIf('R')) { - m_reader.Consume(); ref = true; } - else if (m_reader.Peek() == 'O') + else if (m_reader.ConsumeIf('O')) { - m_reader.Consume(); ref = true; rvalueRef = true; } - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { isTemplate = false; substitute = true; @@ -2546,18 +2457,17 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo // We append them as "[abi:tag]" to the last name segment for display. // Save/restore m_lastName so that a following C1/D1 ctor/dtor name // still resolves to the class name, not the ABI tag string. - while (m_reader.Peek() == 'B') + while (m_reader.ConsumeIf('B')) { - m_reader.Consume(); string savedLastName = m_lastName; string abiTag = DemangleSourceName(); m_lastName = savedLastName; ExtendTypeName(type, "[abi:" + abiTag + "]"); } - bool dependentTemplatePrefix = !pushBareTemplatePrefix && m_reader.Peek() == 'I' && + bool dependentTemplatePrefix = !pushBareTemplatePrefix && m_reader.PeekOr() == 'I' && LastTypeNameSegmentBase(type) == "basic_ostream" && TemplateArgsReferenceTemplateParam(m_reader.PeekString(m_reader.Length())); - if (substitute && m_reader.Peek() != 'E' && !dependentTemplatePrefix) + if (substitute && m_reader.PeekOr() != 'E' && !dependentTemplatePrefix) { //Those template arguments were not the primary arguments so clear them from the sub listType while (m_templateSubstitute.size() > startSize) @@ -2566,9 +2476,8 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo } PushType(type); } - LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); } - m_reader.Consume(); if (cnst || vltl || rstrct) { @@ -2591,7 +2500,7 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() { NestingGuard nestingGuard(m_nestingDepth); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode type; StringList varName; // The local function has its own template scope. Save the outer template @@ -2613,16 +2522,14 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() else varName.push_back(type.GetString()); - if (m_reader.Peek() != 's') + if (!m_reader.ConsumeIf('s')) { // Handle default argument context: d [] _ - if (m_reader.Peek() == 'd') + if (m_reader.ConsumeIf('d')) { - m_reader.Consume(); - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) DemangleNumber(); - if (m_reader.Peek() == '_') - m_reader.Consume(); + m_reader.ConsumeIf('_'); } // DemangledTypeNode tmpType = DemangleName(); @@ -2637,21 +2544,18 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() } else { - m_reader.Consume(); type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); m_templateSubstitute = std::move(savedTemplateSubstitute); m_topLevel = oldTopLevel; } // [] //TODO: What do we do with discriminators? - if (m_reader.Peek() == '_') + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(); - if (m_reader.Peek() == '_') + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); } else @@ -2667,7 +2571,7 @@ DemangledTypeNode DemangleGNU3::DemangleName() { NestingGuard nestingGuard(m_nestingDepth); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw()); /* ::= ::= @@ -2685,9 +2589,8 @@ DemangledTypeNode DemangleGNU3::DemangleName() switch (m_reader.Read()) { case 'S': - if (m_reader.Peek() == 't') + if (m_reader.ConsumeIf('t')) { - m_reader.Consume(1); type = DemangleUnqualifiedName(); auto qn = CopyQualifiedName(type); qn.insert(qn.begin(), DemangledNamePart("std")); @@ -2699,9 +2602,8 @@ DemangledTypeNode DemangleGNU3::DemangleName() type = DemangleSubstitution(); } - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); if (substitute) PushType(type); ParamList args; @@ -2733,15 +2635,13 @@ DemangledTypeNode DemangleGNU3::DemangleName() ::= */ m_reader.UnRead(); - if (m_reader.Peek() == 'L') - m_reader.Consume(); + m_reader.ConsumeIf('L'); type = DemangleUnqualifiedName(); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { PushType(type); // ParamList args; - m_reader.Consume(); // DemangleTemplateArgs(args); ApplyTemplateArgs(type, std::move(args)); @@ -2755,7 +2655,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif { NestingGuard nestingGuard(m_nestingDepth); LOG_INDENTATION_SCOPE; - LogWithIndentation("%s: %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: %s\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode returnType; NodeRef returnTypeRef = nullptr; bool isReturnTypeUnknown = false; @@ -2771,7 +2671,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif ::= */ // - switch (m_reader.Peek()) + switch (m_reader.PeekOr()) { case 'G': m_reader.Consume(); @@ -2788,10 +2688,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif DemangledTypeNode nameNode = DemangleName(); // Consume optional base-36 seq-id (digits + uppercase A-Z) before '_'. string seqId; - while (m_reader.Length() > 0 && m_reader.Peek() != '_') + while (m_reader.Length() > 0 && !m_reader.ConsumeIf('_')) seqId += m_reader.Read(); - if (m_reader.Length() > 0) - m_reader.Consume(); // consume '_' string result = "reference_temporary_for_" + nameNode.GetString(); if (!seqId.empty()) result += "[" + seqId + "]"; @@ -2816,7 +2714,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif // Disambiguate: Intel Vector Function ABI (_ZGV...) vs guard variable (_ZGV). // Intel Vector ABI isa codes: b c d e x y Y z Z // Guard variable encoding starts with: N (nested), L (local), S (substitution), digit, etc. - char peekChar = m_reader.Peek(); + char peekChar = m_reader.PeekOr(); bool isVectorABI = (peekChar == 'b' || peekChar == 'c' || peekChar == 'd' || peekChar == 'e' || peekChar == 'x' || peekChar == 'y' || peekChar == 'Y'); // 'z'/'Z' are ambiguous: also used as Z-local-name prefix in guard variables @@ -2869,16 +2767,16 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif bool first = true; do { - if (m_reader.Peek() == 'v') + if (m_reader.ConsumeIf('v')) { - m_reader.Consume(); break; } if (!first) paramList += ", "; paramList += DemangleTypeString(); - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (m_reader.PeekOr() != 'E'); + if (!m_reader.ConsumeIf('E')) + throw DemangleException(); varName.back() += paramList + ")"; varName.push_back(DemangleSourceName()); @@ -2914,10 +2812,10 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif const char* maskName = (maskChar == 'M') ? "mask" : "nomask"; // Parse vlen: non-negative decimal integer - if (!isdigit(m_reader.Peek())) + if (!isdigit(m_reader.PeekOr())) throw DemangleException(); string vlenStr; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) vlenStr += m_reader.Read(); // Parse vparameters until '_' separator @@ -2927,7 +2825,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif // ::= empty | 'a' string paramsStr; bool firstParam = true; - while (m_reader.Length() > 0 && m_reader.Peek() != '_') + while (m_reader.Length() > 0 && m_reader.PeekOr() != '_') { if (!firstParam) paramsStr += ','; @@ -2948,16 +2846,15 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (hasStride) { - if (m_reader.Peek() == 's') + if (m_reader.ConsumeIf('s')) { // linear_step passed as another argument at given 0-based position - m_reader.Consume(); string argPos; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) argPos += m_reader.Read(); paramsStr += "(step=arg" + argPos + ")"; } - else if (isdigit(m_reader.Peek()) || m_reader.Peek() == 'n') + else if (isdigit(m_reader.PeekOr()) || m_reader.PeekOr() == 'n') { // Literal stride; 'n' prefix means negative string stride = DemangleNumberAsString(); @@ -2967,16 +2864,15 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif } // Optional alignment: 'a' - if (m_reader.Peek() == 'a') + if (m_reader.ConsumeIf('a')) { - m_reader.Consume(); - while (isdigit(m_reader.Peek())) - m_reader.Read(); + while (isdigit(m_reader.PeekOr())) + (void)m_reader.Read(); } } // Consume the '_' separator between parameters and routine name - if (m_reader.Length() == 0 || m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); // Remainder is the scalar routine name (may be a plain C name or a _Z mangled name) @@ -3028,16 +2924,16 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (kind == 'h') { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); } else if (kind == 'v') { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); } else @@ -3056,7 +2952,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif { DemangledTypeNode t = DemangleType(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); return DemangledTypeNode::NamedType(UnknownNamedTypeClass, @@ -3071,7 +2967,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif case 'h': //TODO: Convert to whatever the actual type is! { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; @@ -3111,10 +3007,10 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif case 'v': // virtual thunk { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; @@ -3148,26 +3044,23 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif return type; } - if (m_reader.Peek() == 'E') + if (m_reader.ConsumeIf('E')) { - m_reader.Consume(); return type; } cnst = type.IsConst(); vltl = type.IsVolatile(); auto suffix = type.GetPointerSuffixBits(); - if (m_reader.Peek() == 'J') + if (m_reader.ConsumeIf('J')) { - m_reader.Consume(); // TODO: If we get here we have a return type. What can we do with this info? } // Consume any ABI tags on the function/data name (e.g. B5cxx11). // For nested names these are already consumed inside DemangleNestedName(); // this handles the global-scope case. - while (m_reader.Peek() == 'B') + while (m_reader.ConsumeIf('B')) { - m_reader.Consume(); string savedLastName = m_lastName; string abiTag = DemangleSourceName(); m_lastName = savedLastName; @@ -3204,12 +3097,11 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_functionSubstitute.push_back({}); while (m_reader.Length() > 0) { - if (m_reader.Peek() == 'E') + if (m_reader.ConsumeIf('E')) { - m_reader.Consume(); break; } - if (m_reader.Peek() == '.') + if (m_reader.PeekOr() == '.') { // Extension, consume the rest string ext = m_reader.ReadString(m_reader.Length()); @@ -3226,8 +3118,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif } m_isParameter = true; - LogWithIndentation("Var: %s\n", m_reader.GetRaw().c_str()); - if (m_reader.PeekString(2) == "@@") + LogWithIndentation("Var: %s\n", m_reader.GetRaw()); + if (m_reader.PeekMatch("@@")) break; DemangledTypeNode param = DemangleType(); NodeRef paramRef = m_lastTypeRef; @@ -3235,9 +3127,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif continue; if (param.GetClass() == VoidTypeClass) { - if (m_reader.Peek() == 'E') + if (m_reader.ConsumeIf('E')) { - m_reader.Consume(); break; } break; @@ -3249,10 +3140,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif params.push_back({"", paramRef}); if (isVarArgs) { - if (m_reader.Peek() == 'E') - { - m_reader.Consume(); - } + m_reader.ConsumeIf('E'); break; } @@ -3299,12 +3187,17 @@ bool DemangleGNU3Static::IsGNU3MangledString(const string& name) bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) { + if (name.empty()) + return false; + size_t strippedCount = 0; string encoded = name; - while (encoded[0] == '_') + while (!encoded.empty() && encoded[0] == '_') { encoded.erase(0, 1); strippedCount ++; + if (encoded.empty()) + return false; } if (strippedCount == 0) @@ -3332,10 +3225,14 @@ bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) namespace { - static bool DemangleStringGNU3Segments( - Platform& platform, const string& name, Ref& outType, StringList& outVarName, - bool simplifyTemplates = false) + static std::optional DemangleGNU3WithConfig(const DemanglerConfig& config, const string& name) { + if (name.empty()) + return std::nullopt; + + Platform& platform = config.GetPlatform(); + bool simplifyTemplates = config.simplifyTemplates; + // Handle _block_invoke[.N] and _block_invoke_N suffixes (Clang/Apple block invocations). // E.g. ____ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_Pm_block_invoke.110 // -> "invocation_function_for_block_in_dyld::_main(...)" @@ -3362,14 +3259,14 @@ namespace if (zPos != string::npos && base[zPos] == 'Z') { string normalized = "_" + base.substr(zPos); - Ref baseType; - StringList baseName; - if (DemangleStringGNU3Segments(platform, normalized, baseType, baseName, simplifyTemplates)) + auto baseResult = DemangleGNU3WithConfig(config, normalized); + if (baseResult) { - outVarName.clear(); - outVarName.push_back("invocation_function_for_block_in_" + JoinNameSegments(baseName)); - outType = baseType; - return true; + DemanglerResult result; + result.name = QualifiedName(StringList{ + "invocation_function_for_block_in_" + JoinNameSegments(StringList(baseResult->name.begin(), baseResult->name.end()))}); + result.type = baseResult->type; + return result; } } } @@ -3382,17 +3279,14 @@ namespace name.compare(name.size() - tlvInitSuffix.size(), tlvInitSuffix.size(), tlvInitSuffix) == 0) { string base = name.substr(0, name.size() - tlvInitSuffix.size()); - Ref baseType; - StringList baseName; - if (DemangleStringGNU3Segments(platform, base, baseType, baseName, simplifyTemplates)) + auto result = DemangleGNU3WithConfig(config, base); + if (result) { - outVarName = std::move(baseName); - if (outVarName.size() > 0) - outVarName[outVarName.size() - 1] += "$tlv$init"; + if (result->name.size() > 0) + result->name[result->name.size() - 1] += "$tlv$init"; else - outVarName.push_back("$tlv$init"); - outType = baseType; - return true; + result->name = QualifiedName(StringList{"$tlv$init"}); + return result; } } @@ -3406,80 +3300,60 @@ namespace encoding = encoding.substr(3); else if (foundHeader && !header.empty()) { - outVarName.clear(); - outVarName.push_back(header); - outVarName.push_back(encoding); - outType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, outVarName).Finalize(platform); - return true; + DemanglerResult result; + StringList nameSegments{header, encoding}; + result.name = QualifiedName(nameSegments); + result.type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, nameSegments).Finalize(platform); + return result; } else - return false; + return std::nullopt; thread_local ::DemangleGNU3 demangle(platform, encoding); demangle.Reset(platform, encoding); try { - DemangledTypeNode type = demangle.DemangleSymbol(outVarName, simplifyTemplates); + DemanglerResult result; + StringList nameSegments; + DemangledTypeNode type = demangle.DemangleSymbol(nameSegments, simplifyTemplates); if (simplifyTemplates) DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); - outType = type.Finalize(platform); + result.type = type.Finalize(platform); - if (outVarName.size() == 0) + if (nameSegments.empty()) { - if (GetFinalizedTypeClass(outType) == NamedTypeReferenceClass && - outType->GetNamedTypeReference()->GetTypeReferenceClass() == UnknownNamedTypeClass) + if (GetFinalizedTypeClass(result.type) == NamedTypeReferenceClass && + result.type->GetNamedTypeReference()->GetTypeReferenceClass() == UnknownNamedTypeClass) { - const auto typeName = outType->GetTypeName(); - outVarName = StringList(typeName.begin(), typeName.end()); - outType = nullptr; + const auto typeName = result.type->GetTypeName(); + nameSegments = StringList(typeName.begin(), typeName.end()); + result.type = nullptr; } - else if (GetFinalizedTypeClass(outType) == NamedTypeReferenceClass) + else if (GetFinalizedTypeClass(result.type) == NamedTypeReferenceClass) { - auto typeName = outType->GetTypeName(); + auto typeName = result.type->GetTypeName(); if (typeName.size() > 0) - outVarName = StringList{"_" + typeName[typeName.size() - 1]}; + nameSegments = StringList{"_" + typeName[typeName.size() - 1]}; } } if (foundHeader && !header.empty()) - outVarName.insert(outVarName.begin(), header); + nameSegments.insert(nameSegments.begin(), header); + result.name = QualifiedName(nameSegments); + return result; } - catch (const DemangleException&) + catch (DemangleException& e) { - return false; + LogDebugF("GNU3 demangling failed '{}' '{}'", name, e.what()); } - return true; - } -} - - -bool DemangleGNU3Static::DemangleStringGNU3(Platform& platform, const string& name, Ref& outType, - QualifiedName& outVarName, bool simplifyTemplates) -{ - StringList outVarNameSegments; - if (!DemangleStringGNU3Segments(platform, name, outType, outVarNameSegments, simplifyTemplates)) - return false; - outVarName = QualifiedName(outVarNameSegments); - return true; -} - - -bool DemangleGNU3Static::DemangleStringGNU3(Architecture* arch, const string& name, Ref& outType, - QualifiedName& outVarName, bool simplifyTemplates) -{ - if (arch) - { - auto platform = arch->GetStandalonePlatform(); - if (platform) - return DemangleStringGNU3(*platform, name, outType, outVarName, simplifyTemplates); + catch (std::exception& e) + { + LogDebugF("GNU3 demangling failed '{}' '{}'", name, e.what()); + } + return std::nullopt; } - return DemangleStringGNU3(GetDemanglerFallbackPlatform(), name, outType, outVarName, simplifyTemplates); } -// ===== Explicit template instantiation ===== - - -// ===== Demangler plugin registration ===== class GNU3Demangler: public Demangler { @@ -3496,12 +3370,7 @@ class GNU3Demangler: public Demangler virtual std::optional Demangle(const string& name, const Config& config) override { - Result result; - Platform& platform = config.GetPlatform(); - if (!DemangleGNU3Static::DemangleStringGNU3(platform, name, result.type, result.name, - config.simplifyTemplates)) - return std::nullopt; - return result; + return DemangleGNU3WithConfig(config, name); } }; diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 8c49e751e..34a92b595 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -38,53 +38,7 @@ #define _STD_VECTOR std::vector #endif -#include "demangler/demangled_type_node.h" -#include "demangler/nesting_guard.h" - -class DemangleGNU3Reader -{ -public: - DemangleGNU3Reader(const _STD_STRING& data); - void Reset(const _STD_STRING& data); - _STD_STRING PeekString(size_t count=1); -#ifdef GNUDEMANGLE_DEBUG - _STD_STRING GetRaw(); -#endif - _STD_STRING ReadString(size_t count=1); - - size_t Length() const { return m_data.length() - m_offset; } - - char Peek() - { - if (1 > Length()) - return '\0'; - return (char)m_data[m_offset]; - } - - char Read() - { - if (1 > Length()) - throw DemangleException(); - return m_data[m_offset++]; - } - - void Consume(size_t count=1) - { - if (count > Length()) - throw DemangleException(); - m_offset += count; - } - - void UnRead(size_t count=1) - { - if (count <= m_offset) - m_offset -= count; - } - -private: - _STD_STRING m_data; - size_t m_offset; -}; +#include "demangler/demangled_reader.h" class DemangleGNU3 @@ -141,7 +95,10 @@ class DemangleGNU3 }; using NodeRefList = _STD_VECTOR; - DemangleGNU3Reader m_reader; + static constexpr size_t MAX_DEMANGLE_NODE_LENGTH = 8192; + static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 1024; + _STD_STRING m_mangledName; + DemangleReader m_reader{m_mangledName, MAX_DEMANGLE_NODE_LENGTH, false}; std::reference_wrapper m_platform; NodeRefList m_substitute; NodeRefList m_templateSubstitute; @@ -167,7 +124,7 @@ class DemangleGNU3 NodeRef typeRef; }; _STD_VECTOR m_pendingForwardRefs; - using NestingGuard = DemangleNestingGuard; + using NestingGuard = DemangleNestingGuard; void ResolveForwardTemplateRefs(DemangledTypeNode& type, const ParamList& args); enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name}; StringList DemangleBaseUnresolvedName(); @@ -235,9 +192,4 @@ class DemangleGNU3Static public: static bool IsGNU3MangledString(const _STD_STRING& name); static bool DemangleGlobalHeader(_STD_STRING& name, _STD_STRING& header); - - static bool DemangleStringGNU3(BN::Platform& platform, const _STD_STRING& name, BN::Ref& outType, - BN::QualifiedName& outVarName, bool simplifyTemplates = false); - static bool DemangleStringGNU3(BN::Architecture* arch, const _STD_STRING& name, BN::Ref& outType, - BN::QualifiedName& outVarName, bool simplifyTemplates = false); }; diff --git a/demangler/msvc/CMakeLists.txt b/demangler/msvc/CMakeLists.txt index 1b12bad05..53b51453b 100644 --- a/demangler/msvc/CMakeLists.txt +++ b/demangler/msvc/CMakeLists.txt @@ -7,9 +7,9 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.c *.h ../demangled_log.h + ../demangled_reader.h ../demangled_type_node.cpp ../demangled_type_node.h - ../nesting_guard.h ../demangled_template_simplifier.cpp ../demangled_template_simplifier.h) diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index 67241bb69..ae9727e3d 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -81,28 +81,6 @@ void MsvcTraceWithIndentation(fmt::format_string format, Args&&... args #define MSVC_TRACE_SCOPE do {} while(0) #endif -_STD_STRING Demangle::Reader::ReadString(size_t count) -{ - if (count > Length()) - throw DemangleException(); - _STD_STRING out(m_ptr, count); - m_ptr += count; - return out; -} - - -_STD_STRING Demangle::Reader::ReadUntil(char sentinel) -{ - const char* found = static_cast(memchr(m_ptr, sentinel, m_end - m_ptr)); - if (!found) - throw DemangleException(); - size_t count = found - m_ptr; - _STD_STRING out = ReadString(count); - Consume(); // sentinel - return out; -} - - DemangledTypeNode::NodeRef Demangle::BackrefList::GetTypeBackrefRef(size_t reference) { if (reference < typeList.size() && typeList[reference]) @@ -1771,7 +1749,7 @@ void Demangle::DemangleName(NameList& nameList, } else if (m_reader.ConsumeIf("??")) { - if (m_nestingDepth + strippedNestedNamePrefixes >= MaxDemangleNestingDepth) + if (m_nestingDepth + strippedNestedNamePrefixes >= MAX_DEMANGLE_NESTING_DEPTH) throw DemangleException("Demangle nesting depth exceeded"); strippedNestedNamePrefixes++; continue; diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h index 4927974db..6b0872695 100644 --- a/demangler/msvc/demangle_msvc.h +++ b/demangler/msvc/demangle_msvc.h @@ -37,8 +37,7 @@ #define _STD_VECTOR std::vector #endif -#include "demangler/demangled_type_node.h" -#include "demangler/nesting_guard.h" +#include "demangler/demangled_reader.h" class Demangle { @@ -67,90 +66,6 @@ class Demangle }; private: - class Reader - { - public: - Reader(const _STD_STRING& data) - { - Reset(data); - } - void Reset(const _STD_STRING& data) - { - m_ptr = data.c_str(); - m_end = data.c_str() + data.size(); - ValidatePrintableAscii(); - } - bool PeekMatch(const char* str, size_t len) const - { - if (len > Length()) - return false; - return memcmp(m_ptr, str, len) == 0; - } - [[nodiscard]] char PeekAt(size_t offset) const - { - if (offset >= Length()) - throw DemangleException(); - return m_ptr[offset]; - } - [[nodiscard]] char Peek() const - { - if (m_ptr >= m_end) - throw DemangleException(); - return *m_ptr; - } - [[nodiscard]] char PeekOr(char fallback = '\0') const - { - if (Length() == 0) - return fallback; - return *m_ptr; - } - [[nodiscard]] const char* GetRaw() const { return m_ptr; } - void SetRaw(const char* p) { m_ptr = p; } - [[nodiscard]] char Read() - { - if (m_ptr >= m_end) - throw DemangleException(); - return *m_ptr++; - } - bool ConsumeIf(char ch) - { - if (PeekOr() != ch) - return false; - Consume(); - return true; - } - bool ConsumeIf(const char* str, size_t len) - { - if (!PeekMatch(str, len)) - return false; - Consume(len); - return true; - } - template - bool ConsumeIf(const char (&str)[N]) - { - return ConsumeIf(str, N - 1); - } - void Consume(size_t count = 1) - { - if (count > Length()) - throw DemangleException(); - m_ptr += count; - } - [[nodiscard]] size_t Length() const { return static_cast(m_end - m_ptr); } - _STD_STRING ReadString(size_t count); - _STD_STRING ReadUntil(char sentinel); - private: - void ValidatePrintableAscii() const - { - for (const char* p = m_ptr; p < m_end; p++) - if (*p < 0x20 || *p > 0x7e) - throw DemangleException(); - } - const char* m_ptr; - const char* m_end; - }; - class BackrefList { public: @@ -232,12 +147,14 @@ class Demangle } _STD_STRING m_mangledName; // Owns the string; Reader points into it - Reader m_reader; + DemangleReader m_reader; BackrefList m_backrefList; BN::DemanglerConfig m_config; size_t m_templateParamDepth = 0; size_t m_nestingDepth = 0; - using NestingGuard = DemangleNestingGuard; + // The largest observed depth in a real-world corpus of roughly 200k MSVC symbols was 54. + static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 256; + using NestingGuard = DemangleNestingGuard; static void RewriteTemplateBackrefName(NameList& typeName, const BackrefList& nameBackrefList); static void PrependNameComponent(NameList& nameList, DemangledNamePart name); diff --git a/demangler/nesting_guard.h b/demangler/nesting_guard.h deleted file mode 100644 index 450ce7e67..000000000 --- a/demangler/nesting_guard.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016-2026 Vector 35 Inc. -// -// 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. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -class DemangleException: public std::exception -{ - _STD_STRING m_message; - -public: - DemangleException(_STD_STRING msg = "Attempt to read beyond bounds or missing expected character"): - m_message(std::move(msg)) - {} - - [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } -}; - -static constexpr size_t MaxDemangleNestingDepth = 1024; - -class DemangleNestingGuard -{ - size_t& m_nestingDepth; - -public: - explicit DemangleNestingGuard(size_t& nestingDepth) : m_nestingDepth(nestingDepth) - { - m_nestingDepth++; - if (m_nestingDepth > MaxDemangleNestingDepth) - { - m_nestingDepth--; - throw DemangleException("Detected adversarial mangled string"); - } - } - - ~DemangleNestingGuard() - { - m_nestingDepth--; - } - - DemangleNestingGuard(const DemangleNestingGuard&) = delete; - DemangleNestingGuard& operator=(const DemangleNestingGuard&) = delete; -}; From fd1cf98f0b734af1123b9de8d572756db7a27757 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Mon, 6 Jul 2026 08:18:26 -0400 Subject: [PATCH 13/14] Clang tidy cleanup of demangler code --- demangler/demangled_reader.h | 12 + demangler/demangled_template_simplifier.cpp | 244 +++-- demangler/demangled_template_simplifier.h | 2 +- demangler/demangled_type_node.cpp | 35 +- demangler/demangled_type_node.h | 57 +- demangler/gnu3/demangle_gnu3.cpp | 1032 +++++++++---------- demangler/gnu3/demangle_gnu3.h | 40 +- demangler/msvc/demangle_msvc.cpp | 41 +- demangler/msvc/demangle_msvc.h | 3 - 9 files changed, 723 insertions(+), 743 deletions(-) diff --git a/demangler/demangled_reader.h b/demangler/demangled_reader.h index 64a0ad78b..4b669e0e1 100644 --- a/demangler/demangled_reader.h +++ b/demangler/demangled_reader.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -189,6 +190,17 @@ class DemangleReader return out; } + std::string_view ReadStringView(size_t count = 1) + { + if (count > Length()) + throw DemangleException(); + if (count > m_maxReadStringLength) + throw DemangleException("Demangled node exceeds maximum length"); + std::string_view out(m_ptr, count); + m_ptr += count; + return out; + } + _STD_STRING ReadUntil(char sentinel) { const char* found = static_cast(memchr(m_ptr, sentinel, m_end - m_ptr)); diff --git a/demangler/demangled_template_simplifier.cpp b/demangler/demangled_template_simplifier.cpp index e17641269..2f2d7f13b 100644 --- a/demangler/demangled_template_simplifier.cpp +++ b/demangler/demangled_template_simplifier.cpp @@ -15,12 +15,9 @@ #include "demangled_template_simplifier.h" #include -#include -#include #include #include #include -#include #ifdef BINARYNINJACORE_LIBRARY using namespace BinaryNinjaCore; @@ -34,7 +31,7 @@ namespace using Param = DemangledTypeNode::Param; using ParamList = vector; - static std::string_view TrimSpaces(std::string_view s) + std::string_view TrimSpaces(std::string_view s) { size_t start = 0; while (start < s.size() && s[start] == ' ') @@ -45,13 +42,13 @@ namespace return s.substr(start, end - start); } - static bool StartsWith(std::string_view s, const char* prefix) + bool StartsWith(std::string_view s, const char* prefix) { size_t n = strlen(prefix); return s.compare(0, n, prefix) == 0; } - static std::string_view StripLeadingTypeKeyword(std::string_view s) + std::string_view StripLeadingTypeKeyword(std::string_view s) { s = TrimSpaces(s); if (StartsWith(s, "class ")) @@ -65,7 +62,7 @@ namespace return s; } - static string RemoveSpaces(std::string_view s) + string RemoveSpaces(std::string_view s) { string out; out.reserve(s.size()); @@ -77,18 +74,18 @@ namespace return out; } - static bool StringEquals(const string& s, std::string_view value) + bool StringEquals(const string& s, std::string_view value) { return s.size() == value.size() && (value.empty() || memcmp(s.data(), value.data(), value.size()) == 0); } - static bool IsUnqualifiedNonPointerType(const DemangledTypeNode& type) + bool IsUnqualifiedNonPointerType(const DemangledTypeNode& type) { return !type.IsConst() && !type.IsVolatile() && type.GetPointerSuffixBits() == 0; } - static size_t ResolvedIntegerWidth(size_t width, DemangledTypeNode::WidthKind widthKind) + size_t ResolvedIntegerWidth(size_t width, DemangledTypeNode::WidthKind widthKind) { switch (widthKind) { @@ -102,37 +99,37 @@ namespace } } - static bool GetUnqualifiedIntegerTypeInfo(const DemangledTypeNode& type, size_t& width, + bool GetUnqualifiedIntegerTypeInfo(const DemangledTypeNode& type, size_t& width, DemangledTypeNode::WidthKind& widthKind, bool& isSigned, std::string_view& altName) { return IsUnqualifiedNonPointerType(type) && type.GetIntegerTypeInfo(width, widthKind, isSigned, altName); } - static bool GetUnqualifiedWideCharTypeInfo(const DemangledTypeNode& type, size_t& width, + bool GetUnqualifiedWideCharTypeInfo(const DemangledTypeNode& type, size_t& width, std::string_view& altName) { return IsUnqualifiedNonPointerType(type) && type.GetWideCharTypeInfo(width, altName); } - static string CanonicalTypeNoSpaces(const DemangledTypeNode& type) + string CanonicalTypeNoSpaces(const DemangledTypeNode& type) { string s = type.GetString(); return RemoveSpaces(StripLeadingTypeKeyword(std::string_view(s.data(), s.size()))); } - static string CanonicalTypeNoSpaces(std::string_view type) + string CanonicalTypeNoSpaces(std::string_view type) { return RemoveSpaces(StripLeadingTypeKeyword(type)); } - static bool SameType(const DemangledTypeNode& a, const DemangledTypeNode& b) + bool SameType(const DemangledTypeNode& a, const DemangledTypeNode& b) { if (a.IsStructurallyEqual(b)) return true; return CanonicalTypeNoSpaces(a) == CanonicalTypeNoSpaces(b); } - static bool SameType(const DemangledTypeNode& a, std::string_view b) + bool SameType(const DemangledTypeNode& a, std::string_view b) { if (!a.IsConst() && !a.IsVolatile() && a.GetPointerSuffixBits() == 0 && a.GetClass() == NamedTypeReferenceClass) { @@ -147,7 +144,7 @@ namespace return CanonicalTypeNoSpaces(a) == CanonicalTypeNoSpaces(b); } - static bool IsSimpleNamedType(const DemangledTypeNode& type, std::string_view name, bool cnst = false) + bool IsSimpleNamedType(const DemangledTypeNode& type, std::string_view name, bool cnst = false) { if (type.IsConst() != cnst || type.IsVolatile() || type.GetPointerSuffixBits() != 0 || type.GetClass() != NamedTypeReferenceClass) @@ -160,7 +157,7 @@ namespace return StringEquals(base, name); } - static bool IsQualifiedName(const DemangledTypeNode& type, std::initializer_list expected) + bool IsQualifiedName(const DemangledTypeNode& type, std::initializer_list expected) { if (!IsUnqualifiedNonPointerType(type) || type.GetClass() != NamedTypeReferenceClass) return false; @@ -179,7 +176,7 @@ namespace return true; } - static const char* CharFamilyAliasName(const DemangledTypeNode& element, const char* charAlias, + const char* CharFamilyAliasName(const DemangledTypeNode& element, const char* charAlias, const char* wcharAlias, const char* char8Alias = nullptr, const char* char16Alias = nullptr, const char* char32Alias = nullptr) { @@ -233,12 +230,12 @@ namespace return nullptr; } - static bool HasOnlyOmittedPointerSuffixes(const DemangledTypeNode& type) + bool HasOnlyOmittedPointerSuffixes(const DemangledTypeNode& type) { return (type.GetPointerSuffixBits() & ~(1u << Ptr64Suffix)) == 0; } - static bool IsConstTypeNamed(const DemangledTypeNode& type, std::string_view name) + bool IsConstTypeNamed(const DemangledTypeNode& type, std::string_view name) { if (IsSimpleNamedType(type, name, true)) return true; @@ -250,7 +247,7 @@ namespace return SameType(unqualified, name); } - static bool IsInlineStdNamespace(const DemangledNamePart& component) + bool IsInlineStdNamespace(const DemangledNamePart& component) { if (component.HasTemplateArguments()) return false; @@ -258,7 +255,7 @@ namespace return name == "__1" || name == "__cxx11" || name == "__detail"; } - static bool IsAbslInlineNamespace(const DemangledNamePart& component) + bool IsAbslInlineNamespace(const DemangledNamePart& component) { if (component.HasTemplateArguments()) return false; @@ -273,7 +270,7 @@ namespace return name.size() > 4; } - static bool IsStdScope(const DemangledQualifiedName& name, size_t* templateIndex) + bool IsStdScope(const DemangledQualifiedName& name, size_t* templateIndex) { if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "std") return false; @@ -287,13 +284,13 @@ namespace return true; } - static bool IsStdScope(const DemangledTypeNode& type, size_t* templateIndex) + bool IsStdScope(const DemangledTypeNode& type, size_t* templateIndex) { const DemangledQualifiedName& name = type.GetName(); return IsStdScope(name, templateIndex); } - static bool IsAbslScope(const DemangledQualifiedName& name, size_t* templateIndex) + bool IsAbslScope(const DemangledQualifiedName& name, size_t* templateIndex) { if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "absl") return false; @@ -307,13 +304,13 @@ namespace return true; } - static bool IsAbslScope(const DemangledTypeNode& type, size_t* templateIndex) + bool IsAbslScope(const DemangledTypeNode& type, size_t* templateIndex) { const DemangledQualifiedName& name = type.GetName(); return IsAbslScope(name, templateIndex); } - static bool IsStdTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + bool IsStdTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) { size_t templateIndex = 0; if (!IsStdScope(type, &templateIndex)) @@ -329,7 +326,7 @@ namespace return true; } - static bool IsAbslTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + bool IsAbslTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) { size_t templateIndex = 0; if (!IsAbslScope(type, &templateIndex)) @@ -345,7 +342,7 @@ namespace return true; } - static bool IsAbslNestedTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, + bool IsAbslNestedTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, const ParamList** args = nullptr) { size_t scopeIndex = 0; @@ -364,7 +361,7 @@ namespace return true; } - static bool IsAbslNestedName(const DemangledTypeNode& type, std::string_view scope, std::string_view base) + bool IsAbslNestedName(const DemangledTypeNode& type, std::string_view scope, std::string_view base) { size_t scopeIndex = 0; if (!IsAbslScope(type, &scopeIndex)) @@ -376,7 +373,7 @@ namespace name[scopeIndex + 1].GetBase() == base; } - static bool IsStdPmrTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + bool IsStdPmrTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) { const DemangledQualifiedName& name = type.GetName(); if (name.size() < 3 || name[0].HasTemplateArguments() || name[0].GetBase() != "std") @@ -397,7 +394,7 @@ namespace return true; } - static bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) { const ParamList* args = nullptr; if (!IsStdTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) @@ -405,7 +402,7 @@ namespace return SameType(*(*args)[0].type, arg); } - static bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, std::string_view arg) + bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, std::string_view arg) { const ParamList* args = nullptr; if (!IsStdTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) @@ -413,7 +410,7 @@ namespace return SameType(*(*args)[0].type, arg); } - static bool IsStdPmrOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + bool IsStdPmrOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) { const ParamList* args = nullptr; if (!IsStdPmrTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) @@ -421,12 +418,12 @@ namespace return SameType(*(*args)[0].type, arg); } - static bool IsPmrPolymorphicAllocator(const DemangledTypeNode& type, const DemangledTypeNode& valueType) + bool IsPmrPolymorphicAllocator(const DemangledTypeNode& type, const DemangledTypeNode& valueType) { return IsStdPmrOneArgTemplate(type, "polymorphic_allocator", valueType); } - static bool IsConstTypeOf(const DemangledTypeNode& type, const DemangledTypeNode& base) + bool IsConstTypeOf(const DemangledTypeNode& type, const DemangledTypeNode& base) { if (type.IsConst() && !type.IsVolatile()) { @@ -446,7 +443,7 @@ namespace actual == CanonicalTypeNoSpaces(std::string_view(prefixConst.data(), prefixConst.size())); } - static bool IsStdPairOfConstKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, + bool IsStdPairOfConstKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, const DemangledTypeNode& value) { const ParamList* pairArgs = nullptr; @@ -456,7 +453,7 @@ namespace return IsConstTypeOf(*(*pairArgs)[0].type, key) && SameType(*(*pairArgs)[1].type, value); } - static bool IsStdPairOfKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, + bool IsStdPairOfKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, const DemangledTypeNode& value) { const ParamList* pairArgs = nullptr; @@ -466,7 +463,7 @@ namespace return SameType(*(*pairArgs)[0].type, key) && SameType(*(*pairArgs)[1].type, value); } - static bool IsDefaultPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, + bool IsDefaultPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, const DemangledTypeNode& value) { const ParamList* allocArgs = nullptr; @@ -475,7 +472,7 @@ namespace return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value); } - static bool IsDefaultPairAllocatorWithMutableKey(const DemangledTypeNode& type, const DemangledTypeNode& key, + bool IsDefaultPairAllocatorWithMutableKey(const DemangledTypeNode& type, const DemangledTypeNode& key, const DemangledTypeNode& value) { const ParamList* allocArgs = nullptr; @@ -485,7 +482,7 @@ namespace IsStdPairOfKeyValue(*(*allocArgs)[0].type, key, value); } - static bool IsPmrPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, + bool IsPmrPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, const DemangledTypeNode& value) { const ParamList* allocArgs = nullptr; @@ -495,7 +492,7 @@ namespace return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value); } - static bool IsAbslOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + bool IsAbslOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) { const ParamList* args = nullptr; if (!IsAbslTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) @@ -503,9 +500,9 @@ namespace return SameType(*(*args)[0].type, arg); } - static std::string_view AbiBaseName(std::string_view name); + std::string_view AbiBaseName(std::string_view name); - static bool IsStdStringLike(const DemangledTypeNode& type) + bool IsStdStringLike(const DemangledTypeNode& type) { const DemangledQualifiedName& name = type.GetName(); size_t templateIndex = 0; @@ -523,7 +520,7 @@ namespace base == "u16string_view" || base == "u32string_view"); } - static bool IsAbslStringViewLike(const DemangledTypeNode& type) + bool IsAbslStringViewLike(const DemangledTypeNode& type) { size_t templateIndex = 0; if (!IsAbslScope(type, &templateIndex)) @@ -533,12 +530,12 @@ namespace name[templateIndex].GetBase() == "string_view"; } - static bool IsAbslDefaultStringLikeKey(const DemangledTypeNode& key) + bool IsAbslDefaultStringLikeKey(const DemangledTypeNode& key) { return IsStdStringLike(key) || IsAbslStringViewLike(key); } - static bool IsAbslNestedOneArgTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, + bool IsAbslNestedOneArgTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, const DemangledTypeNode& arg) { const ParamList* args = nullptr; @@ -547,7 +544,7 @@ namespace return SameType(*(*args)[0].type, arg); } - static bool IsAbslHashDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) + bool IsAbslHashDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) { if (IsAbslNestedOneArgTemplate(type, "container_internal", "hash_default_hash", key) || IsAbslNestedOneArgTemplate(type, "hash_internal", "Hash", key) || IsAbslOneArgTemplate(type, "Hash", key)) @@ -566,7 +563,7 @@ namespace return false; } - static bool IsAbslEqDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) + bool IsAbslEqDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) { if (IsAbslNestedOneArgTemplate(type, "container_internal", "hash_default_eq", key) || IsStdOneArgTemplate(type, "equal_to", key)) @@ -589,7 +586,7 @@ namespace return false; } - static bool IsBoostScope(const DemangledQualifiedName& name, size_t* templateIndex) + bool IsBoostScope(const DemangledQualifiedName& name, size_t* templateIndex) { if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "boost") return false; @@ -599,13 +596,13 @@ namespace return true; } - static bool IsBoostScope(const DemangledTypeNode& type, size_t* templateIndex) + bool IsBoostScope(const DemangledTypeNode& type, size_t* templateIndex) { const DemangledQualifiedName& name = type.GetName(); return IsBoostScope(name, templateIndex); } - static bool IsBoostTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + bool IsBoostTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) { size_t templateIndex = 0; if (!IsBoostScope(type, &templateIndex)) @@ -621,7 +618,7 @@ namespace return true; } - static bool IsBoostOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + bool IsBoostOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) { const ParamList* args = nullptr; if (!IsBoostTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) @@ -629,7 +626,7 @@ namespace return SameType(*(*args)[0].type, arg); } - static bool GetSimpleLiteralName(const DemangledTypeNode& arg, std::string_view& literal) + bool GetSimpleLiteralName(const DemangledTypeNode& arg, std::string_view& literal) { if (arg.IsConst() || arg.IsVolatile() || arg.GetPointerSuffixBits() != 0 || arg.GetClass() != NamedTypeReferenceClass) @@ -642,12 +639,12 @@ namespace return true; } - static bool IsIntegerSuffix(char c) + bool IsIntegerSuffix(char c) { return c == 'u' || c == 'U' || c == 'l' || c == 'L'; } - static bool MatchesNonTypeValue(std::string_view raw, std::string_view value) + bool MatchesNonTypeValue(std::string_view raw, std::string_view value) { raw = TrimSpaces(raw); size_t castClose = raw.rfind(')'); @@ -673,13 +670,13 @@ namespace return rawIndex == raw.size(); } - static bool IsNonTypeValue(const DemangledTypeNode& arg, std::string_view value) + bool IsNonTypeValue(const DemangledTypeNode& arg, std::string_view value) { std::string_view literal; return GetSimpleLiteralName(arg, literal) && MatchesNonTypeValue(literal, value); } - static bool CanonicalIntegerLiteral(std::string_view raw, string& out) + bool CanonicalIntegerLiteral(std::string_view raw, string& out) { raw = TrimSpaces(raw); bool hadCast = false; @@ -715,7 +712,7 @@ namespace return !out.empty() && out != s; } - static bool CanonicalizeNonTypeIntegerLiteral(DemangledTypeNode& type) + bool CanonicalizeNonTypeIntegerLiteral(DemangledTypeNode& type) { std::string_view literal; if (!GetSimpleLiteralName(type, literal)) @@ -728,12 +725,12 @@ namespace return true; } - static bool IsVoidType(const DemangledTypeNode& arg) + bool IsVoidType(const DemangledTypeNode& arg) { return SameType(arg, "void"); } - static bool IsPtrdiffType(const DemangledTypeNode& arg) + bool IsPtrdiffType(const DemangledTypeNode& arg) { if (IsSimpleNamedType(arg, "long") || IsSimpleNamedType(arg, "long int") || IsSimpleNamedType(arg, "long long") || IsSimpleNamedType(arg, "long long int") || @@ -758,7 +755,7 @@ namespace t == "__int64" || t == "int64_t" || t == "std::ptrdiff_t"; } - static bool IsPointerTo(const DemangledTypeNode& type, const DemangledTypeNode& pointee) + bool IsPointerTo(const DemangledTypeNode& type, const DemangledTypeNode& pointee) { const DemangledTypeNode* child = nullptr; BNReferenceType referenceType = PointerReferenceType; @@ -767,7 +764,7 @@ namespace SameType(*child, pointee); } - static bool IsReferenceTo(const DemangledTypeNode& type, const DemangledTypeNode& referenced) + bool IsReferenceTo(const DemangledTypeNode& type, const DemangledTypeNode& referenced) { const DemangledTypeNode* child = nullptr; BNReferenceType referenceType = PointerReferenceType; @@ -776,7 +773,7 @@ namespace SameType(*child, referenced); } - static bool IsConstPointerTo(const DemangledTypeNode& type, std::string_view pointee) + bool IsConstPointerTo(const DemangledTypeNode& type, std::string_view pointee) { const DemangledTypeNode* child = nullptr; BNReferenceType referenceType = PointerReferenceType; @@ -785,7 +782,7 @@ namespace IsConstTypeNamed(*child, pointee); } - static bool IsStringConstIterator(const DemangledTypeNode& type, bool wide, bool pmr) + bool IsStringConstIterator(const DemangledTypeNode& type, bool wide, bool pmr) { const ParamList* wrapIterArgs = nullptr; if (IsStdTemplate(type, "__wrap_iter", &wrapIterArgs) && wrapIterArgs->size() == 1 && @@ -803,7 +800,7 @@ namespace return IsQualifiedName(type, {"std", stringName, "const_iterator"}); } - static const char* RegexIteratorAliasName(const DemangledTypeNode& iterator, const char* charPointerAlias, + const char* RegexIteratorAliasName(const DemangledTypeNode& iterator, const char* charPointerAlias, const char* wcharPointerAlias, const char* stringAlias, const char* wstringAlias, bool pmr = false) { if (IsConstPointerTo(iterator, "char")) @@ -817,7 +814,7 @@ namespace return nullptr; } - static bool IsSubMatchOfIterator(const DemangledTypeNode& type, const DemangledTypeNode& iterator, bool pmr = false) + bool IsSubMatchOfIterator(const DemangledTypeNode& type, const DemangledTypeNode& iterator, bool pmr = false) { const ParamList* subMatchArgs = nullptr; if (IsStdTemplate(type, "sub_match", &subMatchArgs) && subMatchArgs->size() == 1 && (*subMatchArgs)[0].type) @@ -835,7 +832,7 @@ namespace return expected && name[templateIndex].GetBase() == expected; } - static bool IsDefaultSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) + bool IsDefaultSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) { const ParamList* allocArgs = nullptr; if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) @@ -843,7 +840,7 @@ namespace return IsSubMatchOfIterator(*(*allocArgs)[0].type, iterator); } - static bool IsPmrSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) + bool IsPmrSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) { const ParamList* allocArgs = nullptr; if (!IsStdPmrTemplate(type, "polymorphic_allocator", &allocArgs) || allocArgs->size() != 1 || @@ -852,7 +849,7 @@ namespace return IsSubMatchOfIterator(*(*allocArgs)[0].type, iterator, true); } - static const char* WideStringName(std::string_view name) + const char* WideStringName(std::string_view name) { if (name == "basic_string") return "wstring"; if (name == "basic_stringbuf") return "wstringbuf"; @@ -871,7 +868,7 @@ namespace return nullptr; } - static const char* AtomicAliasForName(std::string_view t) + const char* AtomicAliasForName(std::string_view t) { if (t == "bool") return "atomic_bool"; if (t == "char") return "atomic_char"; @@ -922,7 +919,7 @@ namespace return nullptr; } - static const char* AtomicAliasForPrimitive(const DemangledTypeNode& type) + const char* AtomicAliasForPrimitive(const DemangledTypeNode& type) { if (!IsUnqualifiedNonPointerType(type)) return nullptr; @@ -972,7 +969,7 @@ namespace return nullptr; } - static const char* AtomicAliasForNamedType(const DemangledTypeNode& type) + const char* AtomicAliasForNamedType(const DemangledTypeNode& type) { if (!IsUnqualifiedNonPointerType(type) || type.GetClass() != NamedTypeReferenceClass) return nullptr; @@ -988,7 +985,7 @@ namespace return nullptr; } - static const char* AtomicAliasName(const DemangledTypeNode& type) + const char* AtomicAliasName(const DemangledTypeNode& type) { if (const char* alias = AtomicAliasForPrimitive(type)) return alias; @@ -1045,12 +1042,12 @@ namespace return nullptr; } - static void ResizeTemplateArgs(DemangledNamePart& c, size_t n) + void ResizeTemplateArgs(DemangledNamePart& c, size_t n) { c.GetMutableTemplateArguments().resize(n); } - static std::string_view AbiBaseName(std::string_view name) + std::string_view AbiBaseName(std::string_view name) { size_t suffixStart = name.size(); while (suffixStart > 0 && name[suffixStart - 1] == ']') @@ -1064,7 +1061,7 @@ namespace return name.substr(0, suffixStart); } - static std::string_view AbiTagSuffix(std::string_view name) + std::string_view AbiTagSuffix(std::string_view name) { size_t suffixStart = name.size(); while (suffixStart > 0 && name[suffixStart - 1] == ']') @@ -1078,7 +1075,7 @@ namespace return name.substr(suffixStart); } - static void ReplaceWithAlias(DemangledNamePart& c, std::string_view alias) + void ReplaceWithAlias(DemangledNamePart& c, std::string_view alias) { string replacement(alias.data(), alias.size()); const std::string_view abiSuffix = AbiTagSuffix(c.GetBase()); @@ -1087,7 +1084,7 @@ namespace c.ClearTemplateArguments(); } - static bool ApplyStringRule(DemangledNamePart& c) + bool ApplyStringRule(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1139,7 +1136,7 @@ namespace return true; } - static bool ApplyIostreamRule(DemangledNamePart& c) + bool ApplyIostreamRule(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1174,7 +1171,7 @@ namespace return true; } - static bool ApplyContainerRules(DemangledNamePart& c) + bool ApplyContainerRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1265,7 +1262,7 @@ namespace return false; } - static bool ApplyDiscoveredStdRules(DemangledNamePart& c) + bool ApplyDiscoveredStdRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1369,7 +1366,7 @@ namespace return false; } - static bool ApplyChronoRules(DemangledNamePart& c) + bool ApplyChronoRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); if (c.GetBase() != "duration" || args.size() != 2 || !args[1].type) @@ -1385,7 +1382,7 @@ namespace return false; } - static bool ApplyGenericStdDefaultRules(DemangledNamePart& c) + bool ApplyGenericStdDefaultRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); if (args.size() == 2 && args[0].type && args[1].type && @@ -1408,7 +1405,7 @@ namespace return false; } - static bool ApplyStdFunctionTemplateRules(DemangledNamePart& c) + bool ApplyStdFunctionTemplateRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1466,7 +1463,7 @@ namespace return false; } - static bool ApplyStdTemplateRules(DemangledNamePart& c) + bool ApplyStdTemplateRules(DemangledNamePart& c) { if (ApplyStringRule(c)) return true; @@ -1481,7 +1478,7 @@ namespace return ApplyGenericStdDefaultRules(c); } - static bool ApplyAbslTemplateRules(DemangledNamePart& c) + bool ApplyAbslTemplateRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1556,7 +1553,7 @@ namespace return false; } - static bool ApplyBoostContainerStringRule(DemangledNamePart& c) + bool ApplyBoostContainerStringRule(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1579,7 +1576,7 @@ namespace return true; } - static bool ApplyBoostContainerTemplateRules(DemangledNamePart& c) + bool ApplyBoostContainerTemplateRules(DemangledNamePart& c) { if (ApplyBoostContainerStringRule(c)) return true; @@ -1629,7 +1626,7 @@ namespace return false; } - static bool ApplyBoostUnorderedTemplateRules(DemangledNamePart& c) + bool ApplyBoostUnorderedTemplateRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1669,7 +1666,7 @@ namespace return false; } - static bool ApplyPmrAliasToStdTemplate(DemangledNamePart& c) + bool ApplyPmrAliasToStdTemplate(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1752,7 +1749,7 @@ namespace return false; } - static bool ApplyConcreteStdAliasRules(DemangledNamePart& c) + bool ApplyConcreteStdAliasRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1897,7 +1894,7 @@ namespace return false; } - static bool ApplyPmrConcreteAliasRules(DemangledNamePart& c) + bool ApplyPmrConcreteAliasRules(DemangledNamePart& c) { ParamList& args = c.GetMutableTemplateArguments(); const std::string_view name = AbiBaseName(c.GetBase()); @@ -1922,7 +1919,7 @@ namespace return false; } - static void SimplifyTemplateArguments(DemangledNamePart& c, bool& changed) + void SimplifyTemplateArguments(DemangledNamePart& c, bool& changed) { for (auto& arg: c.GetMutableTemplateArguments()) { @@ -1939,7 +1936,7 @@ namespace } } - static bool ApplyNamespaceRules(DemangledQualifiedName& name) + bool ApplyNamespaceRules(DemangledQualifiedName& name) { bool changed = false; if (!name.empty() && !name[0].HasTemplateArguments() && name[0].GetBase() == "__gnu_cxx") @@ -1948,7 +1945,7 @@ namespace changed = true; } - for (size_t i = 1; i < name.size();) + for (auto i = 1; i < name.size();) { if (!name[i - 1].HasTemplateArguments() && name[i - 1].GetBase() == "std" && IsInlineStdNamespace(name[i])) @@ -1959,7 +1956,7 @@ namespace } i++; } - for (size_t i = 1; i < name.size();) + for (auto i = 1; i < name.size();) { if (!name[i - 1].HasTemplateArguments() && name[i - 1].GetBase() == "absl" && IsAbslInlineNamespace(name[i])) @@ -1973,10 +1970,10 @@ namespace return changed; } - static bool ApplyStdRules(DemangledQualifiedName& name) + bool ApplyStdRules(DemangledQualifiedName& name) { bool changed = false; - for (size_t i = 1; i < name.size(); i++) + for (auto i = 1; i < name.size(); i++) { if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") continue; @@ -1984,7 +1981,7 @@ namespace changed = true; } - for (size_t i = 1; i < name.size(); i++) + for (auto i = 1; i < name.size(); i++) { if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") continue; @@ -1996,7 +1993,7 @@ namespace } } - for (size_t i = 1; i < name.size(); i++) + for (auto i = 1; i < name.size(); i++) { if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") continue; @@ -2004,7 +2001,7 @@ namespace changed = true; } - for (size_t i = 2; i < name.size(); i++) + for (auto i = 2; i < name.size(); i++) { if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "std" || name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "pmr") @@ -2013,7 +2010,7 @@ namespace changed = true; } - for (size_t i = 2; i < name.size(); i++) + for (auto i = 2; i < name.size(); i++) { if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "std" || name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "chrono") @@ -2024,7 +2021,7 @@ namespace return changed; } - static bool ApplyAbslRules(DemangledQualifiedName& name) + bool ApplyAbslRules(DemangledQualifiedName& name) { bool changed = false; for (size_t i = 1; i < name.size(); i++) @@ -2037,7 +2034,7 @@ namespace return changed; } - static bool ApplyBoostRules(DemangledQualifiedName& name) + bool ApplyBoostRules(DemangledQualifiedName& name) { bool changed = false; for (size_t i = 1; i < name.size(); i++) @@ -2074,7 +2071,7 @@ namespace string tags; }; - static AbiTaggedName SplitAbiTagSuffix(std::string_view name) + AbiTaggedName SplitAbiTagSuffix(std::string_view name) { size_t suffixStart = name.size(); while (suffixStart > 0 && name[suffixStart - 1] == ']') @@ -2091,14 +2088,14 @@ namespace }; } - static string AddAbiTags(const string& base, const string& tags) + string AddAbiTags(const string& base, const string& tags) { if (tags.empty() || (base.size() >= tags.size() && base.compare(base.size() - tags.size(), tags.size(), tags) == 0)) return base; return base + tags; } - static bool ConstructorNameMatches(const string& previousBase, const string& currentBase) + bool ConstructorNameMatches(const string& previousBase, const string& currentBase) { if (previousBase == currentBase) return true; @@ -2114,7 +2111,7 @@ namespace return false; } - static bool RewriteSimplifiedConstructorSegments(DemangledQualifiedName& name) + bool RewriteSimplifiedConstructorSegments(DemangledQualifiedName& name) { bool changed = false; for (size_t i = 1; i < name.size(); i++) @@ -2134,7 +2131,7 @@ namespace string replacement = AddAbiTags(previous.base, current.tags); if (destructor) - replacement = "~" + replacement; + replacement.insert(replacement.begin(), '~'); bool argsChanged = false; DemangledNamePart simplifiedCurrent = name[i]; @@ -2170,12 +2167,12 @@ namespace return changed; } - static bool IsAliasName(std::string_view alias, const unordered_set& aliases) + bool IsAliasName(std::string_view alias, const unordered_set& aliases) { return aliases.find(alias) != aliases.end(); } - static bool IsTopLevelSimplifiedAlias(const DemangledQualifiedName& name) + bool IsTopLevelSimplifiedAlias(const DemangledQualifiedName& name) { if (name.empty() || name[0].HasTemplateArguments()) return false; @@ -2241,17 +2238,14 @@ namespace return false; } - static bool HasTemplateArgs(const DemangledQualifiedName& name) + bool HasTemplateArgs(const DemangledQualifiedName& name) { - for (const auto& component: name) - if (component.HasTemplateArguments()) - return true; - return false; + return std::ranges::any_of(name, [](const auto& component) { return component.HasTemplateArguments(); }); } // Compatibility parser for qualified names that do not come from a demangler AST. // Native GNU3/MSVC paths use the structured DemangledTypeNode overloads. - static size_t FindTemplateOpen(std::string_view component) + size_t FindTemplateOpen(std::string_view component) { component = TrimSpaces(component); if (StartsWith(component, "operator")) @@ -2290,7 +2284,7 @@ namespace return std::string_view::npos; } - static vector SplitTopLevel(std::string_view s, char delimiter) + vector SplitTopLevel(std::string_view s, char delimiter) { vector out; int angleDepth = 0, parenDepth = 0, bracketDepth = 0; @@ -2327,9 +2321,9 @@ namespace return out; } - static DemangledTypeNode ParseCompatibilityType(std::string_view s); + DemangledTypeNode ParseCompatibilityType(std::string_view s); - static ParamList ParseCompatibilityArgs(std::string_view s) + ParamList ParseCompatibilityArgs(std::string_view s) { ParamList args; for (std::string_view arg: SplitTopLevel(s, ',')) @@ -2340,7 +2334,7 @@ namespace return args; } - static DemangledQualifiedName ParseCompatibilityName(std::string_view s) + DemangledQualifiedName ParseCompatibilityName(std::string_view s) { DemangledQualifiedName out; for (std::string_view part: SplitTopLevel(s, ':')) @@ -2367,7 +2361,7 @@ namespace return out; } - static DemangledTypeNode ParseCompatibilityType(std::string_view s) + DemangledTypeNode ParseCompatibilityType(std::string_view s) { s = StripLeadingTypeKeyword(s); if (s.empty()) @@ -2377,7 +2371,7 @@ namespace return DemangledTypeNode::NamedType(UnknownNamedTypeClass, ParseCompatibilityName(s)); } - static StringList RenderSegments(const DemangledQualifiedName& name) + StringList RenderSegments(const DemangledQualifiedName& name) { return DemangledTypeNode::NamedType(UnknownNamedTypeClass, name).RenderTypeNameSegments(); } diff --git a/demangler/demangled_template_simplifier.h b/demangler/demangled_template_simplifier.h index b8e5c2581..b57c4f281 100644 --- a/demangler/demangled_template_simplifier.h +++ b/demangler/demangled_template_simplifier.h @@ -14,7 +14,7 @@ #pragma once -#include "demangler/demangled_type_node.h" +#include "demangled_type_node.h" namespace DemangledTemplateSimplifier { diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index 755f7e3d6..2b32bc17f 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -31,11 +31,11 @@ using namespace std; namespace { - static constexpr uint8_t DemangledPtr64Bit = 1u << 0; - static constexpr uint8_t DemangledUnalignedBit = 1u << 1; - static constexpr uint8_t DemangledRestrictBit = 1u << 2; - static constexpr uint8_t DemangledReferenceBit = 1u << 3; - static constexpr uint8_t DemangledLvalueBit = 1u << 4; + constexpr uint8_t DemangledPtr64Bit = 1u << 0; + constexpr uint8_t DemangledUnalignedBit = 1u << 1; + constexpr uint8_t DemangledRestrictBit = 1u << 2; + constexpr uint8_t DemangledReferenceBit = 1u << 3; + constexpr uint8_t DemangledLvalueBit = 1u << 4; class DemanglerFallbackArchitecture : public Architecture { @@ -544,6 +544,14 @@ DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, return n; } +DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, + std::string_view nameSegment, size_t width, bool isSigned) +{ + DemangledQualifiedName nameSegments; + nameSegments.emplace_back(nameSegment); + return NamedType(cls, std::move(nameSegments), width, isSigned); +} + DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, DemangledQualifiedName nameSegments, size_t width, bool isSigned) { @@ -552,15 +560,20 @@ DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, return n; } -DemangledTypeNode DemangledTypeNode::NamedTypeWithDefaultIntegerWidth(BNNamedTypeReferenceClass cls, - StringList nameSegments, bool isSigned) +DemangledTypeNode DemangledTypeNode::NamedType(StringList nameSegments, size_t width, bool isSigned) { - DemangledTypeNode n = NamedType(cls, std::move(nameSegments), 0, isSigned); - if (auto payload = std::get_if(&n.m_payload)) - payload->widthKind = DefaultIntegerWidth; - return n; + return NamedType(UnknownNamedTypeClass, std::move(nameSegments), width, isSigned); } +DemangledTypeNode DemangledTypeNode::NamedType(std::string_view nameSegment, size_t width, bool isSigned) +{ + return NamedType(UnknownNamedTypeClass, nameSegment, width, isSigned); +} + +DemangledTypeNode DemangledTypeNode::NamedType(DemangledQualifiedName nameSegments, size_t width, bool isSigned) +{ + return NamedType(UnknownNamedTypeClass, std::move(nameSegments), width, isSigned); +} DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string suffix) { diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index 76c79e9e3..90eb6a0f8 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -15,7 +15,6 @@ #pragma once #ifdef BINARYNINJACORE_LIBRARY -#include "qualifiedname.h" #include "type.h" #include "architecture.h" #ifndef BN @@ -40,7 +39,6 @@ #endif #endif -#include #include #include #include @@ -77,18 +75,23 @@ class DemangledNamePart DemangledNamePart(_STD_STRING base, _STD_VECTOR templateArgs, bool spaceAfterComma = false); - const _STD_STRING& GetBase() const { return m_base; } + [[nodiscard]] const _STD_STRING& GetBase() const { return m_base; } void SetBase(_STD_STRING base) { m_base = std::move(base); } void AppendBase(const _STD_STRING& suffix) { m_base += suffix; } - bool HasTemplateArguments() const { return m_hasTemplateArgs || !m_templateArgs.empty(); } - const _STD_VECTOR& GetTemplateArguments() const { return m_templateArgs; } + void AppendBase(std::string_view suffix) + { + if (!suffix.empty()) + m_base.append(suffix.data(), suffix.size()); + } + [[nodiscard]] bool HasTemplateArguments() const { return m_hasTemplateArgs || !m_templateArgs.empty(); } + [[nodiscard]] const _STD_VECTOR& GetTemplateArguments() const { return m_templateArgs; } _STD_VECTOR& GetMutableTemplateArguments() { return m_templateArgs; } void SetTemplateArguments(_STD_VECTOR args, bool spaceAfterComma = false); void ClearTemplateArguments(); void AppendString(_STD_STRING& out, BN::Platform& platform) const; _STD_STRING GetString(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; - bool IsStructurallyEqual(const DemangledNamePart& other) const; + [[nodiscard]] bool IsStructurallyEqual(const DemangledNamePart& other) const; static Ref CreateShared(DemangledNamePart part); static Ref CreateSharedCopy(const DemangledNamePart& part); @@ -150,10 +153,14 @@ class DemangledTypeNode std::nullptr_t, _STD_VECTOR params); static DemangledTypeNode NamedType(BNNamedTypeReferenceClass cls, StringList nameSegments, size_t width = 0, bool isSigned = false); + static DemangledTypeNode NamedType(BNNamedTypeReferenceClass cls, + std::string_view nameSegment, size_t width = 0, bool isSigned = false); static DemangledTypeNode NamedType(BNNamedTypeReferenceClass cls, DemangledQualifiedName nameSegments, size_t width = 0, bool isSigned = false); - static DemangledTypeNode NamedTypeWithDefaultIntegerWidth(BNNamedTypeReferenceClass cls, - StringList nameSegments, bool isSigned = false); + // Use when the mangling provides a name but not a struct/class/union/enum kind. + static DemangledTypeNode NamedType(StringList nameSegments, size_t width = 0, bool isSigned = false); + static DemangledTypeNode NamedType(std::string_view nameSegment, size_t width = 0, bool isSigned = false); + static DemangledTypeNode NamedType(DemangledQualifiedName nameSegments, size_t width = 0, bool isSigned = false); static DemangledTypeNode PostfixType(NodeRef child, _STD_STRING suffix); static DemangledTypeNode PostfixType(NodeRef child, _STD_STRING separator, NodeRef suffixType); static DemangledTypeNode UnaryExpression(_STD_STRING op, NodeRef child); @@ -161,22 +168,22 @@ class DemangledTypeNode static NodeRef CreateShared(DemangledTypeNode node); static NodeRef CreateSharedCopy(const DemangledTypeNode& node); - BNTypeClass GetClass() const { return GetPayloadClass(); } - const DemangledQualifiedName& GetName() const; - DemangledQualifiedName& GetMutableName(); - bool IsConst() const { return m_const; } - bool IsVolatile() const { return m_volatile; } - BNNameType GetNameType() const { return m_nameType; } - bool HasTemplateArguments() const; - uint8_t GetPointerSuffixBits() const { return m_pointerSuffixBits; } - BNNamedTypeReferenceClass GetNTRClass() const; + [[nodiscard]] BNTypeClass GetClass() const { return GetPayloadClass(); } + [[nodiscard]] const DemangledQualifiedName& GetName() const; + [[nodiscard]] DemangledQualifiedName& GetMutableName(); + [[nodiscard]] bool IsConst() const { return m_const; } + [[nodiscard]] bool IsVolatile() const { return m_volatile; } + [[nodiscard]] BNNameType GetNameType() const { return m_nameType; } + [[nodiscard]] bool HasTemplateArguments() const; + [[nodiscard]] uint8_t GetPointerSuffixBits() const { return m_pointerSuffixBits; } + [[nodiscard]] BNNamedTypeReferenceClass GetNTRClass() const; bool GetIntegerTypeInfo(size_t& width, WidthKind& widthKind, bool& isSigned, std::string_view& altName) const; bool GetWideCharTypeInfo(size_t& width, std::string_view& altName) const; bool GetPointerChildType(const DemangledTypeNode*& childType, BNReferenceType& referenceType) const; void SetParenthesizedMemberPointer(bool parenthesized); - StringList RenderTypeNameSegments(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; - bool IsStructurallyEqual(const DemangledTypeNode& other) const; - bool ContainsNodeRef(const NodeRef& target) const; + [[nodiscard]] StringList RenderTypeNameSegments(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + [[nodiscard]] bool IsStructurallyEqual(const DemangledTypeNode& other) const; + [[nodiscard]] bool ContainsNodeRef(const NodeRef& target) const; bool MutateChildTypes(const std::function& mutator); bool MutateQualifiedNames(const std::function& mutator); @@ -298,10 +305,10 @@ class DemangledTypeNode UnaryExpressionPayload, BinaryExpressionPayload>; - bool HasUndeterminedTopLevelSize() const; - uint8_t GetValueConfidence() const; - BNTypeClass GetPayloadClass() const; - NodeRef GetPrimaryChild() const; + [[nodiscard]] bool HasUndeterminedTopLevelSize() const; + [[nodiscard]] uint8_t GetValueConfidence() const; + [[nodiscard]] BNTypeClass GetPayloadClass() const; + [[nodiscard]] NodeRef GetPrimaryChild() const; static size_t ResolveWidth(size_t width, WidthKind widthKind, const BN::Platform& platform = GetDemanglerFallbackPlatform()); BNNameType m_nameType; @@ -314,7 +321,7 @@ class DemangledTypeNode // Helpers for string formatting static uint8_t PointerSuffixBit(BNPointerSuffix ps); void AddPointerSuffixes(BN::TypeBuilder& tb, bool omitPtr64 = true) const; - bool HasPostfixType() const; + [[nodiscard]] bool HasPostfixType() const; void AppendPostfixType(_STD_STRING& out, BN::Platform& platform) const; void AppendUnaryExpression(_STD_STRING& out, BN::Platform& platform) const; void AppendBinaryExpression(_STD_STRING& out, BN::Platform& platform) const; diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index ad26d3539..e8c961322 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -18,7 +18,7 @@ #include "demangle_gnu3.h" #include "demangler/demangled_log.h" #include "demangler/demangled_template_simplifier.h" -#include +#include #include #include @@ -30,27 +30,35 @@ using namespace BinaryNinja; using namespace std; #endif - -static BNTypeClass GetFinalizedTypeClass(const Ref& type) +namespace { + void AppendStringView(string& out, std::string_view s) + { + if (!s.empty()) + out.append(s.data(), s.size()); + } + + + BNTypeClass GetFinalizedTypeClass(const Ref& type) + { #ifdef BINARYNINJACORE_LIBRARY - return type->GetTypeClass(); + return type->GetTypeClass(); #else - return type->GetClass(); + return type->GetClass(); #endif -} + } #define hash(x,y) (64 * (x) + (y)) #ifdef GNUDEMANGLE_DEBUG -void LogWithIndentation(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - _STD_STRING indentedFormat = DemangleLogIndentation::Apply(fmt); - PerformLog(0, DebugLog, "", 0, indentedFormat.c_str(), args); - va_end(args); -} + void LogWithIndentation(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + _STD_STRING indentedFormat = DemangleLogIndentation::Apply(fmt); + PerformLog(0, DebugLog, "", 0, indentedFormat.c_str(), args); + va_end(args); + } #define LOG_INDENTATION_SCOPE DemangleLogIndentationScope logIndentationScope #else @@ -58,280 +66,351 @@ void LogWithIndentation(const char* fmt, ...) #define LOG_INDENTATION_SCOPE do {} while(0) #endif -static size_t TotalStringSize(const StringList& v) -{ - size_t n = 0; - for (const auto& s : v) - n += s.size(); - return n; -} - - -static string JoinNameSegments(const StringList& name) -{ - if (name.empty()) - return {}; - if (name.size() == 1) - return name[0]; - - string out; - out.reserve(TotalStringSize(name) + (name.size() - 1) * 2); - out += name[0]; - for (size_t i = 1; i < name.size(); i++) + size_t TotalStringSize(const StringList& v) { - out += "::"; - out += name[i]; + size_t n = 0; + for (const auto& s : v) + n += s.size(); + return n; } - return out; -} -static bool TemplateArgsReferenceTemplateParam(const string& raw) -{ - if (raw.empty() || (raw[0] != 'I' && raw[0] != 'J')) - return false; - - size_t i = 0; - size_t depth = 0; - while (i < raw.size()) + string JoinNameSegments(const StringList& name) { - char c = raw[i++]; - if (c == 'I' || c == 'J') - { - depth++; - continue; - } - if (c == 'E') + if (name.empty()) + return {}; + if (name.size() == 1) + return name[0]; + + string out; + out.reserve(TotalStringSize(name) + (name.size() - 1) * 2); + out += name[0]; + for (size_t i = 1; i < name.size(); i++) { - if (depth == 0) - return false; - depth--; - if (depth == 0) - return false; - continue; + out += "::"; + out += name[i]; } - if (c == 'T') - return true; - if (c >= '0' && c <= '9') + return out; + } + + + bool TemplateArgsReferenceTemplateParam(std::string_view raw) + { + if (raw.empty() || (raw[0] != 'I' && raw[0] != 'J')) + return false; + + size_t i = 0; + size_t depth = 0; + while (i < raw.size()) { - size_t len = c - '0'; - while (i < raw.size() && raw[i] >= '0' && raw[i] <= '9') - len = (len * 10) + (raw[i++] - '0'); - i = std::min(raw.size(), i + len); + char c = raw[i++]; + if (c == 'I' || c == 'J') + { + depth++; + continue; + } + if (c == 'E') + { + if (depth == 0) + return false; + depth--; + if (depth == 0) + return false; + continue; + } + if (c == 'T') + return true; + if (c >= '0' && c <= '9') + { + size_t len = c - '0'; + while (i < raw.size() && raw[i] >= '0' && raw[i] <= '9') + len = (len * 10) + (raw[i++] - '0'); + i = std::min(raw.size(), i + len); + } } + return false; } - return false; -} -static DemangledNamePart NameSegmentWithTemplateArgs(const string& name, vector args) -{ - return DemangledNamePart(name, std::move(args), true); -} + DemangledNamePart NameSegmentWithTemplateArgs(std::string_view name, vector args) + { + return {string(name), std::move(args), true}; + } -static void AppendStructuredNameSegments(DemangledQualifiedName& name, const StringList& segments) -{ - for (const auto& segment: segments) - name.emplace_back(segment); -} + void AppendStructuredNameSegments(DemangledQualifiedName& name, const StringList& segments) + { + for (const auto& segment: segments) + name.emplace_back(segment); + } -static void SetStructuredExpressionNode(DemangledTypeNode* outNode, const DemangledQualifiedName& name) -{ - if (outNode && !name.empty()) + void SetStructuredExpressionNode(DemangledTypeNode* outNode, const DemangledQualifiedName& name) { - if (name.size() > 1 && !name.back().HasTemplateArguments()) + if (outNode && !name.empty()) { - DemangledQualifiedName baseName(name.begin(), name.end() - 1); - DemangledTypeNode baseType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, std::move(baseName)); - *outNode = DemangledTypeNode::PostfixType( - DemangledTypeNode::CreateShared(std::move(baseType)), "::" + name.back().GetString()); - return; + if (name.size() > 1 && !name.back().HasTemplateArguments()) + { + DemangledQualifiedName baseName(name.begin(), name.end() - 1); + DemangledTypeNode baseType = DemangledTypeNode::NamedType(std::move(baseName)); + *outNode = DemangledTypeNode::PostfixType( + DemangledTypeNode::CreateShared(std::move(baseType)), "::" + name.back().GetString()); + return; + } + *outNode = DemangledTypeNode::NamedType(name); } - *outNode = DemangledTypeNode::NamedType(UnknownNamedTypeClass, name); } -} -static string GetOperator(char elm1, char elm2) -{ - switch (hash(elm1, elm2)) + std::string_view GetOperator(char elm1, char elm2) + { + switch (hash(elm1, elm2)) + { + case hash('d','c'): return "dynamic_cast"; + case hash('s','c'): return "static_cast"; + case hash('c','c'): return "const_cast"; + case hash('r','c'): return "reinterpret_cast"; + case hash('t','i'): // fall through + case hash('t','e'): return "typeid"; + case hash('s','t'): // fall through + case hash('s','z'): return "sizeof"; + case hash('a','t'): // fall through + case hash('a','z'): return "alignof"; + case hash('a','w'): return "co_await"; + case hash('n','x'): return "noexcept"; + case hash('s','Z'): // fall through + case hash('s','P'): return "sizeof..."; + case hash('s','p'): return ""; + case hash('t','w'): // fall through + case hash('t','r'): return "throw"; + case hash('l','s'): return "<<"; // << + case hash('r','s'): return ">>"; // >> + case hash('a','S'): return "="; // = + case hash('n','t'): return "!"; // ! + case hash('e','q'): return "=="; // == + case hash('n','e'): return "!="; // != + case hash('i','x'): return "[]"; // [] + case hash('d','t'): return "."; // . + case hash('p','t'): return "->"; // -> + case hash('m','l'): return "*"; // * + case hash('p','p'): return "++"; // ++ (postfix in context) + case hash('m','m'): return "--"; // -- (postfix in context) + case hash('n','g'): // fall through - (unary) + case hash('m','i'): return "-"; // - + case hash('p','s'): // fall through + (unary) + case hash('p','l'): return "+"; // + + case hash('a','d'): // fall through & (unary) + case hash('a','n'): return "&"; // & + case hash('p','m'): return "->*"; // ->* + case hash('d','v'): return "/"; // / + case hash('r','m'): return "%"; // % + case hash('l','t'): return "<"; // < + case hash('l','e'): return "<="; // <= + case hash('g','t'): return ">"; // > + case hash('g','e'): return ">="; // >= + case hash('c','m'): return ","; // , + case hash('c','l'): return "()"; // () + case hash('c','o'): return "~"; // ~ + case hash('e','o'): return "^"; // ^ + case hash('o','r'): return "|"; // | + case hash('a','a'): return "&&"; // && + case hash('o','o'): return "||"; // || + case hash('d','e'): return "*"; // * (unary) + case hash('m','L'): return "*="; // *= + case hash('p','L'): return "+="; // += + case hash('m','I'): return "-="; // -= + case hash('d','V'): return "/="; // /= + case hash('r','M'): return "%="; // %= + case hash('r','S'): return ">>="; // >>= + case hash('l','S'): return "<<="; // <<= + case hash('a','N'): return "&="; // &= + case hash('o','R'): return "|="; // |= + case hash('e','O'): return "^="; // ^= + case hash('s','s'): return "<=>"; // <=> + case hash('d','l'): return "delete"; // delete + case hash('d','a'): return "delete[]"; // delete[] + case hash('n','w'): return "new"; // new + case hash('n','a'): return "new[]"; // new [] + default: return ""; + } + } + + + BNNameType GetNameType(char elm1, char elm2) { - case hash('d','c'): return "dynamic_cast"; - case hash('s','c'): return "static_cast"; - case hash('c','c'): return "const_cast"; - case hash('r','c'): return "reinterpret_cast"; - case hash('t','i'): return "typeid"; - case hash('t','e'): return "typeid"; - case hash('s','t'): return "sizeof"; - case hash('s','z'): return "sizeof"; - case hash('a','t'): return "alignof"; - case hash('a','z'): return "alignof"; - case hash('a','w'): return "co_await"; - case hash('n','x'): return "noexcept"; - case hash('s','Z'): return "sizeof..."; - case hash('s','P'): return "sizeof..."; - case hash('s','p'): return ""; - case hash('t','w'): return "throw"; - case hash('t','r'): return "throw"; - case hash('l','s'): return "<<"; // << - case hash('r','s'): return ">>"; // >> - case hash('a','S'): return "="; // = - case hash('n','t'): return "!"; // ! - case hash('e','q'): return "=="; // == - case hash('n','e'): return "!="; // != - case hash('i','x'): return "[]"; // [] - case hash('d','t'): return "."; // . - case hash('p','t'): return "->"; // -> - case hash('m','l'): return "*"; // * - case hash('p','p'): return "++"; // ++ (postfix in context) - case hash('m','m'): return "--"; // -- (postfix in context) - case hash('n','g'): return "-"; // - (unary) - case hash('m','i'): return "-"; // - - case hash('p','s'): return "+"; // + (unary) - case hash('p','l'): return "+"; // + - case hash('a','d'): return "&"; // & (unary) - case hash('a','n'): return "&"; // & - case hash('p','m'): return "->*"; // ->* - case hash('d','v'): return "/"; // / - case hash('r','m'): return "%"; // % - case hash('l','t'): return "<"; // < - case hash('l','e'): return "<="; // <= - case hash('g','t'): return ">"; // > - case hash('g','e'): return ">="; // >= - case hash('c','m'): return ","; // , - case hash('c','l'): return "()"; // () - case hash('c','o'): return "~"; // ~ - case hash('e','o'): return "^"; // ^ - case hash('o','r'): return "|"; // | - case hash('a','a'): return "&&"; // && - case hash('o','o'): return "||"; // || - case hash('d','e'): return "*"; // * (unary) - case hash('m','L'): return "*="; // *= - case hash('p','L'): return "+="; // += - case hash('m','I'): return "-="; // -= - case hash('d','V'): return "/="; // /= - case hash('r','M'): return "%="; // %= - case hash('r','S'): return ">>="; // >>= - case hash('l','S'): return "<<="; // <<= - case hash('a','N'): return "&="; // &= - case hash('o','R'): return "|="; // |= - case hash('e','O'): return "^="; // ^= - case hash('s','s'): return "<=>"; // <=> - case hash('d','l'): return "delete"; // delete - case hash('d','a'): return "delete[]"; // delete[] - case hash('n','w'): return "new"; // new - case hash('n','a'): return "new[]"; // new [] - default: return ""; + switch (hash(elm1, elm2)) + { + case hash('n','t'): return OperatorNotNameType; // ! + case hash('n','g'): return OperatorMinusNameType; // - (unary) + case hash('p','s'): return OperatorPlusNameType; // + (unary) + case hash('a','d'): return OperatorBitAndNameType; // & (unary) + case hash('d','e'): return OperatorStarNameType; // * (unary) + case hash('i','x'): return OperatorArrayNameType; // [] + case hash('p','p'): return OperatorIncrementNameType; // ++ (postfix in context) + case hash('m','m'): return OperatorDecrementNameType; // -- (postfix in context) + case hash('l','s'): return OperatorLeftShiftNameType; // << + case hash('r','s'): return OperatorRightShiftNameType; // >> + case hash('a','S'): return OperatorAssignNameType; // = + case hash('e','q'): return OperatorEqualNameType; // == + case hash('n','e'): return OperatorNotEqualNameType; // != + case hash('p','t'): return OperatorArrowNameType; // -> + case hash('m','l'): return OperatorStarNameType; // * + case hash('m','i'): return OperatorMinusNameType; // - + case hash('p','l'): return OperatorPlusNameType; // + + case hash('a','n'): return OperatorBitAndNameType; // & + case hash('p','m'): return OperatorArrowStarNameType; // ->* + case hash('d','v'): return OperatorDivideNameType; // / + case hash('r','m'): return OperatorModulusNameType; // % + case hash('l','t'): return OperatorLessThanNameType; // < + case hash('l','e'): return OperatorLessThanEqualNameType; // <= + case hash('g','t'): return OperatorGreaterThanNameType; // > + case hash('g','e'): return OperatorGreaterThanEqualNameType; // >= + case hash('c','m'): return OperatorCommaNameType; // , + case hash('c','l'): return OperatorParenthesesNameType; // () + case hash('c','o'): return OperatorTildeNameType; // ~ + case hash('e','o'): return OperatorXorNameType; // ^ + case hash('o','r'): return OperatorBitOrNameType; // | + case hash('a','a'): return OperatorLogicalAndNameType; // && + case hash('o','o'): return OperatorLogicalOrNameType; // || + case hash('m','L'): return OperatorStarEqualNameType; // *= + case hash('p','L'): return OperatorPlusEqualNameType; // += + case hash('m','I'): return OperatorMinusEqualNameType; // -= + case hash('d','V'): return OperatorDivideEqualNameType; // /= + case hash('r','M'): return OperatorModulusEqualNameType; // %= + case hash('r','S'): return OperatorRightShiftEqualNameType; // >>= + case hash('l','S'): return OperatorLeftShiftEqualNameType; // <<= + case hash('a','N'): return OperatorAndEqualNameType; // &= + case hash('o','R'): return OperatorOrEqualNameType; // |= + case hash('e','O'): return OperatorXorEqualNameType; // ^= + case hash('d','l'): return OperatorDeleteNameType; // delete + case hash('d','a'): return OperatorDeleteArrayNameType; // delete[] + case hash('n','w'): return OperatorNewNameType; // new + case hash('n','a'): return OperatorNewArrayNameType; // new [] + // Note: C1-C5 (constructor) and D0-D5 (destructor) are handled directly + // by DemangleUnqualifiedName with their own case blocks, so they never + // reach GetNameType. + default: + return NoNameType; + } + } + + + // Decode a big-endian hex string into a float or double. + // Returns the decimal string representation, or the raw hex with a type + // prefix if decoding fails or the result is NaN/Inf. + string DecodeHexFloat(std::string_view hex, size_t byteCount) + { + if (hex.size() != byteCount * 2) + return string(hex); + + // Parse big-endian hex into an integer, then reinterpret as float/double + uint64_t bits = 0; + for (size_t i = 0; i < hex.size(); i++) + { + char c = hex[i]; + uint64_t nibble; + if (c >= '0' && c <= '9') nibble = c - '0'; + else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10; + else return string(hex); + bits = (bits << 4) | nibble; + } + + if (byteCount == 4) + { + union { uint32_t i; float f; } u; + u.i = static_cast(bits); + if (std::isnan(u.f) || std::isinf(u.f)) + { + string out = "(float)"; + AppendStringView(out, hex); + return out; + } + return to_string(u.f); + } + else if (byteCount == 8) + { + union { uint64_t i; double d; } u; + u.i = bits; + if (std::isnan(u.d) || std::isinf(u.d)) + { + string out = "(double)"; + AppendStringView(out, hex); + return out; + } + return to_string(u.d); + } + return string(hex); } -} -static BNNameType GetNameType(char elm1, char elm2) -{ - switch (hash(elm1, elm2)) + + void ExtendTypeName(DemangledTypeNode& type, std::string_view extend) { - case hash('n','t'): return OperatorNotNameType; // ! - case hash('n','g'): return OperatorMinusNameType; // - (unary) - case hash('p','s'): return OperatorPlusNameType; // + (unary) - case hash('a','d'): return OperatorBitAndNameType; // & (unary) - case hash('d','e'): return OperatorStarNameType; // * (unary) - case hash('i','x'): return OperatorArrayNameType; // [] - case hash('p','p'): return OperatorIncrementNameType; // ++ (postfix in context) - case hash('m','m'): return OperatorDecrementNameType; // -- (postfix in context) - case hash('l','s'): return OperatorLeftShiftNameType; // << - case hash('r','s'): return OperatorRightShiftNameType; // >> - case hash('a','S'): return OperatorAssignNameType; // = - case hash('e','q'): return OperatorEqualNameType; // == - case hash('n','e'): return OperatorNotEqualNameType; // != - case hash('p','t'): return OperatorArrowNameType; // -> - case hash('m','l'): return OperatorStarNameType; // * - case hash('m','i'): return OperatorMinusNameType; // - - case hash('p','l'): return OperatorPlusNameType; // + - case hash('a','n'): return OperatorBitAndNameType; // & - case hash('p','m'): return OperatorArrowStarNameType; // ->* - case hash('d','v'): return OperatorDivideNameType; // / - case hash('r','m'): return OperatorModulusNameType; // % - case hash('l','t'): return OperatorLessThanNameType; // < - case hash('l','e'): return OperatorLessThanEqualNameType; // <= - case hash('g','t'): return OperatorGreaterThanNameType; // > - case hash('g','e'): return OperatorGreaterThanEqualNameType; // >= - case hash('c','m'): return OperatorCommaNameType; // , - case hash('c','l'): return OperatorParenthesesNameType; // () - case hash('c','o'): return OperatorTildeNameType; // ~ - case hash('e','o'): return OperatorXorNameType; // ^ - case hash('o','r'): return OperatorBitOrNameType; // | - case hash('a','a'): return OperatorLogicalAndNameType; // && - case hash('o','o'): return OperatorLogicalOrNameType; // || - case hash('m','L'): return OperatorStarEqualNameType; // *= - case hash('p','L'): return OperatorPlusEqualNameType; // += - case hash('m','I'): return OperatorMinusEqualNameType; // -= - case hash('d','V'): return OperatorDivideEqualNameType; // /= - case hash('r','M'): return OperatorModulusEqualNameType; // %= - case hash('r','S'): return OperatorRightShiftEqualNameType; // >>= - case hash('l','S'): return OperatorLeftShiftEqualNameType; // <<= - case hash('a','N'): return OperatorAndEqualNameType; // &= - case hash('o','R'): return OperatorOrEqualNameType; // |= - case hash('e','O'): return OperatorXorEqualNameType; // ^= - case hash('d','l'): return OperatorDeleteNameType; // delete - case hash('d','a'): return OperatorDeleteArrayNameType; // delete[] - case hash('n','w'): return OperatorNewNameType; // new - case hash('n','a'): return OperatorNewArrayNameType; // new [] - // Note: C1-C5 (constructor) and D0-D5 (destructor) are handled directly - // by DemangleUnqualifiedName with their own case blocks, so they never - // reach GetNameType. - default: - return NoNameType; + if (type.GetClass() != NamedTypeReferenceClass) + return; + + DemangledQualifiedName name = type.GetName(); + if (name.empty()) + { + name.emplace_back(extend); + type.SetName(std::move(name)); + return; + } + + name.back().AppendBase(extend); + type.SetName(std::move(name)); } -} + void ApplyTemplateArgs(DemangledTypeNode& type, DemangleGNU3::ParamList args) + { + if (type.GetClass() != NamedTypeReferenceClass) + return; + + DemangledQualifiedName qn = type.GetName(); + if (qn.empty()) + qn.emplace_back(""); + qn.back().SetTemplateArguments(std::move(args), true); + type.SetName(std::move(qn)); + } -// Decode a big-endian hex string into a float or double. -// Returns the decimal string representation, or the raw hex with a type -// prefix if decoding fails or the result is NaN/Inf. -static string DecodeHexFloat(const string& hex, size_t byteCount) -{ - if (hex.size() != byteCount * 2) - return hex; - // Parse big-endian hex into an integer, then reinterpret as float/double - uint64_t bits = 0; - for (size_t i = 0; i < hex.size(); i++) + void AppendTypeName(DemangledTypeNode& type, const DemangledTypeNode& extend) { - char c = hex[i]; - uint64_t nibble; - if (c >= '0' && c <= '9') nibble = c - '0'; - else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10; - else return hex; - bits = (bits << 4) | nibble; + if (type.GetClass() != NamedTypeReferenceClass) + return; + + DemangledQualifiedName newName = type.GetName(); + DemangledQualifiedName extendName = extend.GetName(); + newName.reserve(newName.size() + extendName.size()); + newName.insert(newName.end(), extendName.begin(), extendName.end()); + type.SetName(std::move(newName)); } - if (byteCount == 4) + + std::string_view LastTypeNameSegmentBase(const DemangledTypeNode& type) { - union { uint32_t i; float f; } u; - u.i = (uint32_t)bits; - if (std::isnan(u.f) || std::isinf(u.f)) - return "(float)" + hex; - return to_string(u.f); + const auto& qn = type.GetName(); + if (!qn.empty()) + return qn.back().GetBase(); + return {}; } - else if (byteCount == 8) + + + bool LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNode& type) { - union { uint64_t i; double d; } u; - u.i = bits; - if (std::isnan(u.d) || std::isinf(u.d)) - return "(double)" + hex; - return to_string(u.d); + const auto& qn = type.GetName(); + if (qn.empty()) + return false; + return qn.back().HasTemplateArguments(); } - return hex; } // ===== DemangleGNU3 implementation ===== -DemangleGNU3::DemangleGNU3(Platform& platform, const string& mangledName) : - m_mangledName(mangledName), +DemangleGNU3::DemangleGNU3(Platform& platform, string mangledName) : + m_mangledName(std::move(mangledName)), m_reader(m_mangledName, MAX_DEMANGLE_NODE_LENGTH, false), m_platform(platform), m_lastTypeRef(nullptr), @@ -347,9 +426,9 @@ DemangleGNU3::DemangleGNU3(Platform& platform, const string& mangledName) : LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); } -void DemangleGNU3::Reset(Platform& platform, const string& mangledName) +void DemangleGNU3::Reset(Platform& platform, string mangledName) { - m_mangledName = mangledName; + m_mangledName = std::move(mangledName); m_reader.Reset(m_mangledName); m_platform = std::ref(platform); m_substitute.clear(); @@ -369,122 +448,9 @@ void DemangleGNU3::Reset(Platform& platform, const string& mangledName) } -DemangledTypeNode DemangleGNU3::CreateUnknownType(const StringList& s) -{ - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, s); -} - - -DemangledTypeNode DemangleGNU3::CreateUnknownType(const string& s) -{ - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{s}); -} - - -static DemangledQualifiedName CopyQualifiedName(const DemangledTypeNode& type) -{ - return type.GetName(); -} - - -void DemangleGNU3::ExtendTypeName(DemangledTypeNode& type, const string& extend) -{ - if (type.GetClass() != NamedTypeReferenceClass) - return; - - DemangledQualifiedName name = CopyQualifiedName(type); - if (name.empty()) - { - name.emplace_back(extend); - type.SetName(std::move(name)); - return; - } - - name.back().AppendBase(extend); - type.SetName(std::move(name)); -} - - -void DemangleGNU3::ApplyTemplateArgs(DemangledTypeNode& type, ParamList args) -{ - if (type.GetClass() != NamedTypeReferenceClass) - return; - - DemangledQualifiedName qn = CopyQualifiedName(type); - if (qn.empty()) - qn.emplace_back(""); - - qn.back().SetTemplateArguments(std::move(args), true); - type.SetName(std::move(qn)); -} - - -void DemangleGNU3::AppendTypeName(DemangledTypeNode& type, const DemangledTypeNode& extend) -{ - if (type.GetClass() != NamedTypeReferenceClass) - return; - - DemangledQualifiedName newName = CopyQualifiedName(type); - DemangledQualifiedName extendName = CopyQualifiedName(extend); - newName.reserve(newName.size() + extendName.size()); - newName.insert(newName.end(), extendName.begin(), extendName.end()); - type.SetName(std::move(newName)); -} - - -string DemangleGNU3::LastTypeNameSegmentBase(const DemangledTypeNode& type) -{ - const auto& qn = type.GetName(); - if (!qn.empty()) - return qn.back().GetBase(); - return {}; -} - - -bool DemangleGNU3::LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNode& type) -{ - const auto& qn = type.GetName(); - if (qn.empty()) - return false; - return qn.back().HasTemplateArguments(); -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(NodeRef type) -{ - if (type) - m_templateSubstitute.push_back(type); - return type; -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(const DemangledTypeNode& type) -{ - auto ref = DemangledTypeNode::CreateSharedCopy(type); - m_templateSubstitute.push_back(ref); - return ref; -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(DemangledTypeNode&& type) -{ - auto ref = DemangledTypeNode::CreateShared(std::move(type)); - m_templateSubstitute.push_back(ref); - return ref; -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateParamPack(ParamList args) -{ - NodeRef ref = NodeRef::TemplateParamPack(std::move(args)); - m_templateSubstitute.push_back(ref); - return ref; -} - - void DemangleGNU3::PushEmptyTemplateParamSubstitution() { - m_templateSubstitute.push_back(NodeRef::TemplateParamPack({})); + m_templateSubstitute.emplace_back(NodeRef::TemplateParamPack({})); } @@ -500,18 +466,10 @@ const DemangledTypeNode& DemangleGNU3::GetTemplateType(size_t ref) #endif -DemangleGNU3::NodeRef DemangleGNU3::PushType(NodeRef type) -{ - if (type) - m_substitute.push_back(type); - return type; -} - - DemangleGNU3::NodeRef DemangleGNU3::PushType(const DemangledTypeNode& type) { auto ref = DemangledTypeNode::CreateSharedCopy(type); - m_substitute.push_back(ref); + m_substitute.emplace_back(ref); return ref; } @@ -519,17 +477,11 @@ DemangleGNU3::NodeRef DemangleGNU3::PushType(const DemangledTypeNode& type) DemangleGNU3::NodeRef DemangleGNU3::PushType(DemangledTypeNode&& type) { auto ref = DemangledTypeNode::CreateShared(std::move(type)); - m_substitute.push_back(ref); + m_substitute.emplace_back(ref); return ref; } -void DemangleGNU3::PushEmptyTypeSubstitution() -{ - m_substitute.push_back(NodeRef::EmptyTemplatePack()); -} - - DemangleGNU3::NodeRef DemangleGNU3::GetTypeRef(size_t ref) { if (ref >= m_substitute.size()) @@ -588,12 +540,13 @@ void DemangleGNU3::DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct) } -string DemangleGNU3::DemangleSourceName() +std::string_view DemangleGNU3::DemangleSourceName() { LOG_INDENTATION_SCOPE; LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); - m_lastName = m_reader.ReadString(DemangleNumber()); - return m_lastName; + std::string_view name = m_reader.ReadStringView(DemangleNumber()); + m_lastName = name; + return name; } @@ -614,7 +567,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) ParamList params; old_isparam = m_isParameter; m_isParameter = true; - m_functionSubstitute.push_back({}); + m_functionSubstitute.emplace_back(); [[maybe_unused]] int i = 0; while (!m_reader.ConsumeIf('E')) { @@ -627,7 +580,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) LogWithIndentation("Var_%d - %s\n", i++, param.GetString().c_str()); if (!paramRef) paramRef = DemangledTypeNode::CreateShared(std::move(param)); - m_functionSubstitute.back().push_back(paramRef); + m_functionSubstitute.back().emplace_back(paramRef); params.push_back({"", paramRef}); } m_functionSubstitute.pop_back(); @@ -719,7 +672,7 @@ DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* o // replaced once the outer template args are known. if (m_permitForwardTemplateRefs) { - auto typeRef = DemangledTypeNode::CreateShared(CreateUnknownType("auto")); + auto typeRef = DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType("auto")); m_pendingForwardRefs.push_back({number, typeRef}); if (outTypeRef) *outTypeRef = typeRef; @@ -728,7 +681,7 @@ DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* o if (m_parsingLambdaParams && number >= m_lambdaTemplateParamBase) { - auto typeRef = DemangledTypeNode::CreateShared(CreateUnknownType("auto")); + auto typeRef = DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType("auto")); if (outTypeRef) *outTypeRef = typeRef; return typeRef; @@ -804,7 +757,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() if (m_reader.ConsumeIf('t')) { type = DemangleUnqualifiedName(); - auto qn = CopyQualifiedName(type); + DemangledQualifiedName qn = type.GetName(); qn.insert(qn.begin(), DemangledNamePart("std")); type.SetName(std::move(qn)); substitute = true; @@ -837,18 +790,18 @@ DemangledTypeNode DemangleGNU3::DemangleType() */ if (m_reader.ConsumeIf('s')) { - type = DemangledTypeNode::NamedType(StructNamedTypeClass, StringList{DemangleSourceName()}); + type = DemangledTypeNode::NamedType(StructNamedTypeClass, DemangleSourceName()); break; } else if (m_reader.ConsumeIf('u')) { - type = DemangledTypeNode::NamedType(UnionNamedTypeClass, StringList{DemangleSourceName()}); + type = DemangledTypeNode::NamedType(UnionNamedTypeClass, DemangleSourceName()); break; } else if (m_reader.ConsumeIf('e')) { - type = DemangledTypeNode::NamedTypeWithDefaultIntegerWidth( - EnumNamedTypeClass, StringList{DemangleSourceName()}); + type = DemangledTypeNode::NamedType( + EnumNamedTypeClass, DemangleSourceName(), m_platform.get().GetArchitecture()->GetDefaultIntegerSize()); break; } @@ -917,7 +870,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() } DemangledTypeNode inner = DemangleType(); NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); - auto extType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, DemangledQualifiedName{std::move(extName)}); + auto extType = DemangledTypeNode::NamedType(DemangledQualifiedName{std::move(extName)}); NodeRef extNameRef = DemangledTypeNode::CreateShared(std::move(extType)); type = DemangledTypeNode::PostfixType(innerRef, " ", extNameRef); substitute = true; @@ -935,7 +888,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() if (!targs.empty()) extName.SetTemplateArguments(std::move(targs), true); } - type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, DemangledQualifiedName{std::move(extName)}); + type = DemangledTypeNode::NamedType(DemangledQualifiedName{std::move(extName)}); substitute = true; break; } @@ -966,7 +919,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() NodeRef memberNameRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(memberName))); DemangledTypeNode member = DemangleType(); NodeRef memberRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(member))); - type = DemangledTypeNode::MemberPointerType(memberRef, CopyQualifiedName(*memberNameRef), cnst, vltl); + type = DemangledTypeNode::MemberPointerType(memberRef, memberNameRef->GetName(), cnst, vltl); type.SetParenthesizedMemberPointer(true); substitute = true; break; @@ -982,12 +935,12 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'u': type = DemangledTypeNode::IntegerType(1, false, "char8_t"); break; case 'i': type = DemangledTypeNode::WideCharType(4, "char32_t"); break; case 's': type = DemangledTypeNode::WideCharType(2, "char16_t"); break; - case 'a': type = CreateUnknownType("auto"); break; //auto type - case 'c': type = CreateUnknownType("decltype(auto)"); break; //decltype(auto) + case 'a': type = DemangledTypeNode::NamedType("auto"); break; //auto type + case 'c': type = DemangledTypeNode::NamedType("decltype(auto)"); break; //decltype(auto) case 'n': { static const StringList stdNullptrTName(vector{"std", "nullptr_t"}); - type = CreateUnknownType(stdNullptrTName); + type = DemangledTypeNode::NamedType(stdNullptrTName); break; } case 'p': @@ -1006,7 +959,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() } case 't': case 'T': - type = CreateUnknownType("decltype(" + DemangleExpression() + ")"); + type = DemangledTypeNode::NamedType("decltype(" + DemangleExpression() + ")"); if (!m_reader.ConsumeIf('E')) throw DemangleException(); break; @@ -1071,7 +1024,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() m_reader.UnRead(); type = DemangleName(); - string lastName = LastTypeNameSegmentBase(type); + std::string_view lastName = LastTypeNameSegmentBase(type); if (lastName.empty()) throw DemangleException(); m_lastName = lastName; @@ -1110,8 +1063,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) LOG_INDENTATION_SCOPE; LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); - char elm; - elm = m_reader.Read(); + char elm = m_reader.Read(); StringList name; size_t number = 0; switch (elm) @@ -1135,14 +1087,14 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) // The actual substitution index = base36_value + 1. // This handles both single-char (S0_ ... SZ_) and // multi-char (S10_, S11_, ...) seq-ids. - size_t base36 = isdigit(elm) ? (size_t)(elm - '0') : (size_t)(elm - 'A' + 10); + size_t base36 = isdigit(elm) ? static_cast(elm - '0') : static_cast(elm - 'A' + 10); while (m_reader.PeekOr() != '_') { char c = m_reader.Read(); if (isdigit(c)) - base36 = base36 * 36 + (size_t)(c - '0'); + base36 = base36 * 36 + static_cast(c - '0'); else if (isupper(c)) - base36 = base36 * 36 + (size_t)(c - 'A' + 10); + base36 = base36 * 36 + static_cast(c - 'A' + 10); else throw DemangleException(); } @@ -1178,7 +1130,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) ref = GetTypeRef(number); } const DemangledTypeNode& resolved = *ref; - string lastName = LastTypeNameSegmentBase(resolved); + std::string_view lastName = LastTypeNameSegmentBase(resolved); if (!lastName.empty()) m_lastName = lastName; if (outTypeRef) @@ -1186,7 +1138,7 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) return resolved; } m_lastName = name.back(); - return CreateUnknownType(name); + return DemangledTypeNode::NamedType(std::move(name)); } string DemangleGNU3::DemangleNumberAsString() @@ -1314,28 +1266,34 @@ string DemangleGNU3::DemanglePrimaryExpression() } -string DemangleGNU3::DemangleUnarySuffixExpression(const string& op) +string DemangleGNU3::DemangleUnarySuffixExpression(std::string_view op) { - return "(" + DemangleExpression() + ")" + op; + string out = "(" + DemangleExpression() + ")"; + out += op; + return out; } -string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op, DemangledTypeNode* outNode) +string DemangleGNU3::DemangleUnaryPrefixExpression(std::string_view op, DemangledTypeNode* outNode) { DemangledTypeNode exprNode; string expr = DemangleExpression(outNode ? &exprNode : nullptr); if (outNode) { if (exprNode.GetClass() == VoidTypeClass) - exprNode = CreateUnknownType(expr); + exprNode = DemangledTypeNode::NamedType(expr); *outNode = DemangledTypeNode::UnaryExpression( - op, DemangledTypeNode::CreateShared(std::move(exprNode))); + string(op), DemangledTypeNode::CreateShared(std::move(exprNode))); } - return op + "(" + expr + ")"; + string out(op); + out += "("; + out += expr; + out += ")"; + return out; } -string DemangleGNU3::DemangleBinaryExpression(const string& op, DemangledTypeNode* outNode) +string DemangleGNU3::DemangleBinaryExpression(std::string_view op, DemangledTypeNode* outNode) { LOG_INDENTATION_SCOPE; LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); @@ -1348,20 +1306,29 @@ string DemangleGNU3::DemangleBinaryExpression(const string& op, DemangledTypeNod if (outNode) { if (lhsNode.GetClass() == VoidTypeClass) - lhsNode = CreateUnknownType(lhsExpr); + lhsNode = DemangledTypeNode::NamedType(lhsExpr); if (rhsNode.GetClass() == VoidTypeClass) - rhsNode = CreateUnknownType(rhsExpr); + rhsNode = DemangledTypeNode::NamedType(rhsExpr); *outNode = DemangledTypeNode::BinaryExpression( - DemangledTypeNode::CreateShared(std::move(lhsNode)), op, + DemangledTypeNode::CreateShared(std::move(lhsNode)), string(op), DemangledTypeNode::CreateShared(std::move(rhsNode))); } - return lhs + " " + op + " " + rhs; + string out = lhs; + out += " "; + out += op; + out += " "; + out += rhs; + return out; } -string DemangleGNU3::DemangleUnaryPrefixType(const string& op) +string DemangleGNU3::DemangleUnaryPrefixType(std::string_view op) { - return op + "(" + DemangleTypeString() + ")"; + string out(op); + out += "("; + out += DemangleTypeString(); + out += ")"; + return out; } @@ -1377,14 +1344,15 @@ string DemangleGNU3::DemangleExpressionList() LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); string expr; bool first = true; - m_functionSubstitute.push_back({}); + m_functionSubstitute.emplace_back(); while (!m_reader.ConsumeIf('E')) { if (!first) expr += ", "; const string e = DemangleExpression(); expr += e; - m_functionSubstitute.back().push_back(DemangledTypeNode::CreateShared(CreateUnknownType(e))); + m_functionSubstitute.back().emplace_back( + DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType(e))); first = false; } m_functionSubstitute.pop_back(); @@ -1446,9 +1414,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('o','R'): // |= case hash('e','O'): // ^= case hash('s','s'): // <=> - outType = CreateUnknownType("operator" + GetOperator(elm1, elm2)); + { + string name = "operator"; + name += GetOperator(elm1, elm2); + outType = DemangledTypeNode::NamedType(name); outType.SetNameType(GetNameType(elm1, elm2)); break; + } case hash('t','i'): case hash('t','e'): case hash('s','t'): @@ -1464,9 +1436,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('d','a'): // delete[] case hash('n','w'): // new case hash('n','a'): // new [] - outType = CreateUnknownType("operator " + GetOperator(elm1, elm2)); + { + string name = "operator "; + name += GetOperator(elm1, elm2); + outType = DemangledTypeNode::NamedType(name); outType.SetNameType(GetNameType(elm1, elm2)); break; + } case hash('v','0'): case hash('v','1'): case hash('v','2'): @@ -1484,7 +1460,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('C','3'): case hash('C','4'): case hash('C','5'): - outType = CreateUnknownType(m_lastName); + outType = DemangledTypeNode::NamedType(m_lastName); outType.SetNameType(ConstructorNameType); break; case hash('C','I'): // Inheriting constructor: CI1 or CI2 @@ -1496,7 +1472,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() string savedLastName = m_lastName; DemangleType(); m_lastName = savedLastName; - outType = CreateUnknownType(m_lastName); + outType = DemangledTypeNode::NamedType(m_lastName); outType.SetNameType(ConstructorNameType); break; } @@ -1506,12 +1482,12 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('D','3'): case hash('D','4'): case hash('D','5'): - outType = CreateUnknownType("~" + m_lastName); + outType = DemangledTypeNode::NamedType("~" + m_lastName); outType.SetNameType(DestructorNameType); break; case hash('D','t'): case hash('D','T'): - outType = CreateUnknownType(DemangleExpression()); + outType = DemangledTypeNode::NamedType(DemangleExpression()); break; case hash('U','l'): //Lambda { @@ -1553,7 +1529,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() } name += ")"; m_lastName = name; - outType = CreateUnknownType(name); + outType = DemangledTypeNode::NamedType(name); PushType(outType); break; } @@ -1572,7 +1548,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() throw DemangleException(); m_lastName = name; - outType = CreateUnknownType(name); + outType = DemangledTypeNode::NamedType(name); break; } case hash('c','v'): //type (expression) @@ -1590,19 +1566,18 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() DemangledTypeNode cvType = DemangleType(); NodeRef cvTypeRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(cvType))); m_permitForwardTemplateRefs = savedPermit; - outType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, - DemangledQualifiedName{DemangledNamePart("operator ", std::move(cvTypeRef))}); + outType = DemangledTypeNode::NamedType(DemangledQualifiedName{DemangledNamePart("operator ", std::move(cvTypeRef))}); break; } default: m_reader.UnRead(2); if (isdigit(m_reader.PeekOr()) || m_reader.ConsumeIf('L')) { - string name = DemangleSourceName(); + string name(DemangleSourceName()); if (name.size() > 11 && name.substr(0, 11) == "_GLOBAL__N_") name = "(anonymous namespace)"; m_lastName = name; - outType = CreateUnknownType(name); + outType = DemangledTypeNode::NamedType(name); } else { @@ -1613,10 +1588,12 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() // Applies to source names, operator names, and unnamed types. while (m_reader.ConsumeIf('B')) { - string tag = "[abi:" + DemangleSourceName() + "]"; + string tag = "[abi:"; + AppendStringView(tag, DemangleSourceName()); + tag += "]"; ExtendTypeName(outType, tag); - string lastName = LastTypeNameSegmentBase(outType); - m_lastName = lastName.empty() ? tag : lastName; + std::string_view lastName = LastTypeNameSegmentBase(outType); + m_lastName = lastName.empty() ? tag : string(lastName); } return outType; } @@ -1639,27 +1616,31 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() { char op1 = m_reader.Read(); char op2 = m_reader.Read(); - out.push_back(GetOperator(op1, op2)); + out.emplace_back(GetOperator(op1, op2)); if (m_reader.ConsumeIf('I')) { ParamList args; DemangleTemplateArgs(args); out.back() = NameSegmentWithTemplateArgs(out.back(), std::move(args)).GetString(); - PushType(CreateUnknownType(out)); + PushType(DemangledTypeNode::NamedType(out)); } } else if (m_reader.PeekMatch("dn")) { string name = DemangleUnresolvedType().GetString(); if (name.empty()) - out.push_back("~" + DemangleSourceName()); + { + string dtorName = "~"; + AppendStringView(dtorName, DemangleSourceName()); + out.push_back(std::move(dtorName)); + } else out.push_back("~" + name); } else { // - out.push_back(DemangleSourceName()); + out.emplace_back(DemangleSourceName()); if (m_reader.ConsumeIf('I')) { ParamList args; @@ -1704,7 +1685,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() const string name = "decltype(" + DemangleExpression() + ")"; if (!m_reader.ConsumeIf('E')) throw DemangleException(); - type = CreateUnknownType(name); + type = DemangledTypeNode::NamedType(name); } else if (m_reader.ConsumeIf('S')) { @@ -1806,10 +1787,10 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) case hash('c','c'): case hash('r','c'): { - const string op = GetOperator(elm1, elm2); + std::string_view op = GetOperator(elm1, elm2); const string castType = DemangleTypeString(); const string castExpr = DemangleExpression(); - return op + "<" + castType + ">(" + castExpr + ")"; + return string(op) + "<" + castType + ">(" + castExpr + ")"; } case hash('t','i'): case hash('t','e'): @@ -1818,9 +1799,8 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) case hash('a','t'): case hash('a','z'): case hash('n','x'): - return GetOperator(elm1, elm2) + "(" + DemangleTypeString() + ")"; case hash('s','Z'): - return GetOperator(elm1, elm2) + "(" + DemangleTypeString() + ")"; + return string(GetOperator(elm1, elm2)) + "(" + DemangleExpression() + ")"; case hash('s','P'): { ParamList args; @@ -1828,13 +1808,11 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) return "sizeof...(" + NameSegmentWithTemplateArgs("", std::move(args)).GetString() + ")..."; } case hash('s','p'): - { return "(" + DemangleExpression() + ")..."; - } case hash('t','w'): - return GetOperator(elm1, elm2) + DemangleExpression(); + return string(GetOperator(elm1, elm2)) + DemangleExpression(); case hash('t','r'): - return GetOperator(elm1, elm2); //rethrow + return string(GetOperator(elm1, elm2)); //rethrow case hash('n','t'): // ! case hash('n','g'): // - (unary) case hash('p','s'): // + (unary) @@ -1907,13 +1885,14 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) const string callable = DemangleExpression(); string args; bool firstArg = true; - m_functionSubstitute.push_back({}); + m_functionSubstitute.emplace_back(); while (!m_reader.ConsumeIf('E')) { if (!firstArg) args += ", "; const string e = DemangleExpression(); args += e; - m_functionSubstitute.back().push_back(DemangledTypeNode::CreateShared(CreateUnknownType(e))); + m_functionSubstitute.back().emplace_back( + DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType(e))); firstArg = false; } m_functionSubstitute.pop_back(); @@ -1969,8 +1948,8 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) elm = m_reader.PeekOr(); if (m_reader.ConsumeIf('_')) { - if ((uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || - (size_t)elementNum >= m_functionSubstitute[listNumber].size()) + if (static_cast(listNumber) >= static_cast(m_functionSubstitute.size()) || + static_cast(elementNum) >= m_functionSubstitute[listNumber].size()) { // fp_ used before params are known (e.g., in decltype return type) out = (elementNum == 0) ? "fp" : "fp" + std::to_string(elementNum - 1); @@ -1986,8 +1965,8 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) if (!m_reader.ConsumeIf('_')) throw DemangleException(); if (elementNum < 0 || - (uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || - (size_t)elementNum >= m_functionSubstitute[listNumber].size()) + static_cast(listNumber) >= static_cast(m_functionSubstitute.size()) || + static_cast(elementNum) >= m_functionSubstitute[listNumber].size()) { // fpN_ used before params are known out = "fp" + std::to_string(elementNum - 1); @@ -2043,12 +2022,12 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) } do { - const string segName = DemangleSourceName(); + std::string_view segName = DemangleSourceName(); const size_t segmentStart = out.size(); - out += segName; + AppendStringView(out, segName); DemangledNamePart structuredSegment(segName); // Push bare name (before template args) to substitution table. - PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); + PushType(DemangledTypeNode::NamedType(StringList{out})); if (m_reader.ConsumeIf('I')) { ParamList args; @@ -2058,7 +2037,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) out.resize(segmentStart); out += structuredSegment.GetString(); // Also push the template instantiation (name+args). - PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); + PushType(DemangledTypeNode::NamedType(StringList{out})); } structuredName.push_back(std::move(structuredSegment)); out += "::"; @@ -2086,12 +2065,12 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) do { hadTemplateArgs = false; - const string segName = DemangleSourceName(); + std::string_view segName = DemangleSourceName(); const size_t segmentStart = out.size(); - out += segName; + AppendStringView(out, segName); DemangledNamePart structuredSegment(segName); // Push bare name to substitution table. - PushType(CreateUnknownType(out)); + PushType(DemangledTypeNode::NamedType(out)); if (m_reader.ConsumeIf('I')) { ParamList args; @@ -2100,7 +2079,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) out.resize(segmentStart); out += structuredSegment.GetString(); // Also push the template instantiation. - PushType(CreateUnknownType(out)); + PushType(DemangledTypeNode::NamedType(out)); hadTemplateArgs = true; } structuredName.push_back(std::move(structuredSegment)); @@ -2128,7 +2107,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) // otherwise it is the final base-unresolved-name. while (isdigit(m_reader.PeekOr())) { - const string segName = DemangleSourceName(); + std::string_view segName = DemangleSourceName(); if (m_reader.ConsumeIf('I')) { ParamList args; @@ -2140,7 +2119,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) // Push to the substitution table, mirroring what the // N-prefix sr branch does for each nested qualifier. string segment = NameSegmentWithTemplateArgs(segName, std::move(args)).GetString(); - PushType(CreateUnknownType(out + segment)); + PushType(DemangledTypeNode::NamedType(out + segment)); out += segment + "::"; structuredName.push_back(std::move(structuredSegment)); } @@ -2156,7 +2135,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) else { // Plain source name with no template args — final base name. - out += segName; + AppendStringView(out, segName); structuredName.emplace_back(segName); SetStructuredExpressionNode(outNode, structuredName); return out; @@ -2183,8 +2162,7 @@ string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) out = structuredSegment.GetString(); } if (outNode) - *outNode = DemangledTypeNode::NamedType( - UnknownNamedTypeClass, DemangledQualifiedName{std::move(structuredSegment)}); + *outNode = DemangledTypeNode::NamedType(DemangledQualifiedName{std::move(structuredSegment)}); break; } return out; @@ -2217,7 +2195,7 @@ bool DemangleGNU3::AppendTemplateParamPackExpansion(ParamList& params, const Nod if (expansion.emptyTemplatePack) { - PushEmptyTypeSubstitution(); + m_substitute.emplace_back(NodeRef::EmptyTemplatePack()); return true; } @@ -2240,18 +2218,21 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) DemangledTypeNode tmp; NodeRef tmpRef; bool tmpValid = false; - string expr; bool topLevel; switch (m_reader.Read()) { case 'L': + { + string expr; expr = DemanglePrimaryExpression(); - tmp = CreateUnknownType(expr); + tmp = DemangledTypeNode::NamedType(expr); tmpRef = DemangledTypeNode::CreateShared(std::move(tmp)); args.push_back({"", tmpRef}); tmpValid = true; - if (hadNonTypeArg) *hadNonTypeArg = true; + if (hadNonTypeArg) + *hadNonTypeArg = true; break; + } case 'X': { string expr; @@ -2266,7 +2247,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) if (!emptyPack) { if (!haveExprNode || exprNode.GetClass() == VoidTypeClass) - exprNode = CreateUnknownType(expr); + exprNode = DemangledTypeNode::NamedType(expr); args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); } if (!m_reader.ConsumeIf('E')) @@ -2285,7 +2266,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) ParamList packArgs(args.begin() + prevArgSize, args.end()); while (m_templateSubstitute.size() > prevTemplateSize) m_templateSubstitute.pop_back(); - PushTemplateParamPack(std::move(packArgs)); + m_templateSubstitute.emplace_back(NodeRef::TemplateParamPack(std::move(packArgs))); } break; } @@ -2321,7 +2302,7 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) if (m_topLevel && tmpValid) { LogWithIndentation("Adding template ref: %s\n", tmpRef ? tmpRef->GetString().c_str() : ""); - PushTemplateType(tmpRef); + m_templateSubstitute.emplace_back(tmpRef); } return true; } @@ -2339,7 +2320,6 @@ void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) break; } m_lastName = lastName; - return; } @@ -2378,7 +2358,7 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo LOG_INDENTATION_SCOPE; LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); - DemangledTypeNode type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{}); + DemangledTypeNode type = DemangledTypeNode::NamedType(StringList{}); bool cnst = false, vltl = false, rstrct = false; bool ref = false; bool rvalueRef = false; @@ -2460,7 +2440,7 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo while (m_reader.ConsumeIf('B')) { string savedLastName = m_lastName; - string abiTag = DemangleSourceName(); + string abiTag(DemangleSourceName()); m_lastName = savedLastName; ExtendTypeName(type, "[abi:" + abiTag + "]"); } @@ -2517,7 +2497,7 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() type = DemangleSymbol(varName); m_inLocalName = savedInLocalName; - if (varName.size() > 0) + if (!varName.empty()) varName.back() += type.GetStringAfterName(m_platform); else varName.push_back(type.GetString()); @@ -2533,7 +2513,7 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() } // DemangledTypeNode tmpType = DemangleName(); - type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); + type = DemangledTypeNode::NamedType(varName); AppendTypeName(type, tmpType); type.SetNTRType(tmpType.GetNTRClass()); type.SetConst(tmpType.IsConst()); @@ -2544,7 +2524,7 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() } else { - type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); + type = DemangledTypeNode::NamedType(varName); m_templateSubstitute = std::move(savedTemplateSubstitute); m_topLevel = oldTopLevel; } @@ -2592,7 +2572,7 @@ DemangledTypeNode DemangleGNU3::DemangleName() if (m_reader.ConsumeIf('t')) { type = DemangleUnqualifiedName(); - auto qn = CopyQualifiedName(type); + DemangledQualifiedName qn = type.GetName(); qn.insert(qn.begin(), DemangledNamePart("std")); type.SetName(std::move(qn)); substitute = true; @@ -2661,7 +2641,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif bool isReturnTypeUnknown = false; DemangledTypeNode type; ParamList params; - bool cnst = false, vltl = false, rstrct = false; + bool cnst = false, vltl = false; bool oldTopLevel; StringList name; @@ -2694,7 +2674,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (!seqId.empty()) result += "[" + seqId + "]"; varName.push_back(result); - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); + return DemangledTypeNode::NamedType(varName); } case 'T': // transaction clone: GTt (safe) or GTn (non-safe) { @@ -2706,8 +2686,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_topLevel = false; DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform)}); + return DemangledTypeNode::NamedType(StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform)}); } case 'V': { @@ -2726,11 +2705,11 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif // that don't appear in valid vparameter sequences. if (!isVectorABI && (peekChar == 'z' || peekChar == 'Z')) { - _STD_STRING ahead = m_reader.PeekString(std::min((size_t)32, m_reader.Length())); + _STD_STRING ahead = m_reader.PeekString(std::min(static_cast(32), m_reader.Length())); if (ahead.size() >= 3 && (ahead[1] == 'M' || ahead[1] == 'N')) { size_t pos = 2; - while (pos < ahead.size() && isdigit((unsigned char)ahead[pos])) + while (pos < ahead.size() && isdigit(static_cast(ahead[pos]))) pos++; if (pos > 2) // had at least one vlen digit { @@ -2741,7 +2720,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif { char c = ahead[pos]; if (c == 'v' || c == 'l' || c == 'u' || c == 'R' || - c == 'L' || c == 's' || isdigit((unsigned char)c)) + c == 'L' || c == 's' || isdigit(static_cast(c))) pos++; else { @@ -2778,7 +2757,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (!m_reader.ConsumeIf('E')) throw DemangleException(); varName.back() += paramList + ")"; - varName.push_back(DemangleSourceName()); + varName.emplace_back(DemangleSourceName()); return type; } @@ -2875,7 +2854,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (!m_reader.ConsumeIf('_')) throw DemangleException(); - // Remainder is the scalar routine name (may be a plain C name or a _Z mangled name) + // Remainder is the scalar routine name (maybe a plain C name or a _Z mangled name) string routineName = m_reader.ReadString(m_reader.Length()); // Build the human-readable annotation @@ -2893,8 +2872,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif } annotation += ']'; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{routineName + annotation}); + return DemangledTypeNode::NamedType(StringList{routineName + annotation}); } default: throw DemangleException(); @@ -2945,8 +2923,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_topLevel = false; DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); + return DemangledTypeNode::NamedType(StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'C': { @@ -2955,8 +2932,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif if (!m_reader.ConsumeIf('_')) throw DemangleException(); - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"construction_vtable_for_" + DemangleTypeString() + "-in-" + t.GetString()}); + return DemangledTypeNode::NamedType(StringList{"construction_vtable_for_" + DemangleTypeString() + "-in-" + t.GetString()}); } case 'D': LogWarn("Unsupported: 'typeinfo common proxy'\n"); @@ -2973,8 +2949,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_topLevel = false; DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); + return DemangledTypeNode::NamedType(StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'H': // TLS init function { @@ -2982,12 +2957,10 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_topLevel = false; DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform)}); + return DemangledTypeNode::NamedType(StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform)}); } case 'I': - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"typeinfo_for_" + DemangleTypeString()}); + return DemangledTypeNode::NamedType(StringList{"typeinfo_for_" + DemangleTypeString()}); case 'J': LogWarn("Unsupported: 'java class'\n"); throw DemangleException(); @@ -3016,8 +2989,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_topLevel = false; DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); + return DemangledTypeNode::NamedType(StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'V': //Vtable return DemangledTypeNode::NamedType(StructNamedTypeClass, @@ -3028,8 +3000,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif m_topLevel = false; DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform)}); + return DemangledTypeNode::NamedType(StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform)}); } default: throw DemangleException(); @@ -3062,7 +3033,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif while (m_reader.ConsumeIf('B')) { string savedLastName = m_lastName; - string abiTag = DemangleSourceName(); + string abiTag(DemangleSourceName()); m_lastName = savedLastName; ExtendTypeName(type, "[abi:" + abiTag + "]"); } @@ -3094,7 +3065,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif returnType = DemangledTypeNode::AddressSizedIntegerType(true); } - m_functionSubstitute.push_back({}); + m_functionSubstitute.emplace_back(); while (m_reader.Length() > 0) { if (m_reader.ConsumeIf('E')) @@ -3112,7 +3083,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif else if (ext == ".debug_frame") ext = "debug frame"; // On the off chance some invalid mangled string is passed in. - if (varName.size() > 0) + if (!varName.empty()) varName.back() += " " + ext; break; } @@ -3157,8 +3128,6 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif type.SetPointerSuffixBits(suffix); type.SetConst(cnst); type.SetVolatile(vltl); - if (rstrct) - type.SetPointerSuffixBits(1u << RestrictSuffix); if (simplifyTemplates) DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); @@ -3171,9 +3140,9 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplif // ===== Non-templated static methods ===== -bool DemangleGNU3Static::IsGNU3MangledString(const string& name) +bool DemangleGNU3Static::IsGNU3MangledString(std::string_view name) { - string headerless = name; + string headerless(name); string header; if (DemangleGlobalHeader(headerless, header)) return true; @@ -3225,7 +3194,7 @@ bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) namespace { - static std::optional DemangleGNU3WithConfig(const DemanglerConfig& config, const string& name) + std::optional DemangleGNU3WithConfig(const DemanglerConfig& config, std::string_view name) { if (name.empty()) return std::nullopt; @@ -3236,31 +3205,31 @@ namespace // Handle _block_invoke[.N] and _block_invoke_N suffixes (Clang/Apple block invocations). // E.g. ____ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_Pm_block_invoke.110 // -> "invocation_function_for_block_in_dyld::_main(...)" - static const string blockInvokeSuffix = "_block_invoke"; + static constexpr std::string_view blockInvokeSuffix = "_block_invoke"; size_t blockPos = name.rfind(blockInvokeSuffix); - if (blockPos != string::npos) + if (blockPos != std::string_view::npos) { // Verify the suffix is _block_invoke optionally followed by [._] only - string tail = name.substr(blockPos + blockInvokeSuffix.size()); + std::string_view tail = name.substr(blockPos + blockInvokeSuffix.size()); bool validSuffix = tail.empty(); if (!validSuffix && (tail[0] == '.' || tail[0] == '_')) { size_t i = 1; - while (i < tail.size() && isdigit((unsigned char)tail[i])) + while (i < tail.size() && isdigit(static_cast(tail[i]))) i++; validSuffix = (i == tail.size() && i > 1); } if (validSuffix) { // Extract the base symbol: everything before _block_invoke - string base = name.substr(0, blockPos); + std::string_view base = name.substr(0, blockPos); // Normalize leading underscores: find 'Z' after underscores, keep one '_' before it size_t zPos = base.find_first_not_of('_'); - if (zPos != string::npos && base[zPos] == 'Z') + if (zPos != std::string_view::npos && base[zPos] == 'Z') { - string normalized = "_" + base.substr(zPos); - auto baseResult = DemangleGNU3WithConfig(config, normalized); - if (baseResult) + string normalized = "_"; + AppendStringView(normalized, base.substr(zPos)); + if (auto baseResult = DemangleGNU3WithConfig(config, normalized)) { DemanglerResult result; result.name = QualifiedName(StringList{ @@ -3274,13 +3243,12 @@ namespace // Handle macOS thread-local variable initializer suffix: $tlv$init // E.g. __ZL9recursive$tlv$init -> demangle "__ZL9recursive" then annotate. - static const string tlvInitSuffix = "$tlv$init"; + static constexpr std::string_view tlvInitSuffix = "$tlv$init"; if (name.size() > tlvInitSuffix.size() && name.compare(name.size() - tlvInitSuffix.size(), tlvInitSuffix.size(), tlvInitSuffix) == 0) { - string base = name.substr(0, name.size() - tlvInitSuffix.size()); - auto result = DemangleGNU3WithConfig(config, base); - if (result) + std::string_view base = name.substr(0, name.size() - tlvInitSuffix.size()); + if (auto result = DemangleGNU3WithConfig(config, base)) { if (result->name.size() > 0) result->name[result->name.size() - 1] += "$tlv$init"; @@ -3290,7 +3258,7 @@ namespace } } - string encoding = name; + string encoding(name); string header; bool foundHeader = DemangleGNU3Static::DemangleGlobalHeader(encoding, header); @@ -3303,7 +3271,7 @@ namespace DemanglerResult result; StringList nameSegments{header, encoding}; result.name = QualifiedName(nameSegments); - result.type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, nameSegments).Finalize(platform); + result.type = DemangledTypeNode::NamedType(nameSegments).Finalize(platform); return result; } else @@ -3361,14 +3329,14 @@ class GNU3Demangler: public Demangler GNU3Demangler(): Demangler("gnu3") { } - ~GNU3Demangler() override {} + ~GNU3Demangler() override = default; - virtual bool IsMangledString(const string& name) override + bool IsMangledString(const string& name) override { return DemangleGNU3Static::IsGNU3MangledString(name); } - virtual std::optional Demangle(const string& name, const Config& config) override + std::optional Demangle(const string& name, const Config& config) override { return DemangleGNU3WithConfig(config, name); } @@ -3389,7 +3357,7 @@ extern "C" BINARYNINJAPLUGIN bool CorePluginInit() #endif { - static GNU3Demangler* demangler = new GNU3Demangler(); + static auto demangler = new GNU3Demangler(); return Demangler::Register(demangler); } } diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 34a92b595..591e460c5 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -14,6 +14,7 @@ #pragma once #include +#include #include // XXX: Compiled directly into the core for performance reasons @@ -23,9 +24,6 @@ // refactor to use DemangledTypeNode and the simplifier refactor // its very possible this performance penalty is completely gone #ifdef BINARYNINJACORE_LIBRARY -#include "qualifiedname.h" -#include "type.h" -#include "architecture.h" #include "binaryview.h" #include "demangle.h" #define BN BinaryNinjaCore @@ -43,8 +41,10 @@ class DemangleGNU3 { +public: using ParamList = _STD_VECTOR; using TypeNodeRef = DemangledTypeNode::NodeRef; +private: struct NodeRef { TypeNodeRef type; @@ -87,8 +87,8 @@ class DemangleGNU3 } explicit operator bool() const { return type != nullptr; } - bool IsTemplateParamPack() const { return templatePack != nullptr; } - bool IsTemplateParamPackExpansion() const { return templatePackExpansion; } + [[nodiscard]] bool IsTemplateParamPack() const { return templatePack != nullptr; } + [[nodiscard]] bool IsTemplateParamPackExpansion() const { return templatePackExpansion; } DemangledTypeNode& operator*() const { return *type; } DemangledTypeNode* operator->() const { return type.get(); } operator TypeNodeRef() const { return type; } @@ -129,14 +129,14 @@ class DemangleGNU3 enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name}; StringList DemangleBaseUnresolvedName(); DemangledTypeNode DemangleUnresolvedType(); - _STD_STRING DemangleUnarySuffixExpression(const _STD_STRING& op); - _STD_STRING DemangleUnaryPrefixExpression(const _STD_STRING& op, DemangledTypeNode* outNode = nullptr); - _STD_STRING DemangleBinaryExpression(const _STD_STRING& op, DemangledTypeNode* outNode = nullptr); - _STD_STRING DemangleUnaryPrefixType(const _STD_STRING& op); + _STD_STRING DemangleUnarySuffixExpression(std::string_view op); + _STD_STRING DemangleUnaryPrefixExpression(std::string_view op, DemangledTypeNode* outNode = nullptr); + _STD_STRING DemangleBinaryExpression(std::string_view op, DemangledTypeNode* outNode = nullptr); + _STD_STRING DemangleUnaryPrefixType(std::string_view op); _STD_STRING DemangleTypeString(); _STD_STRING DemangleExpressionList(); DemangledTypeNode DemangleUnqualifiedName(); - _STD_STRING DemangleSourceName(); + std::string_view DemangleSourceName(); _STD_STRING DemangleNumberAsString(); _STD_STRING DemangleExpression(DemangledTypeNode* outNode = nullptr); _STD_STRING DemanglePrimaryExpression(); @@ -154,35 +154,21 @@ class DemangleGNU3 DemangledTypeNode DemangleType(); int64_t DemangleNumber(); DemangledTypeNode DemangleNestedName(bool* allTypeTemplateArgs = nullptr, bool pushBareTemplatePrefix = true); - NodeRef PushTemplateType(NodeRef type); - NodeRef PushTemplateType(const DemangledTypeNode& type); - NodeRef PushTemplateType(DemangledTypeNode&& type); - NodeRef PushTemplateParamPack(ParamList args); void PushEmptyTemplateParamSubstitution(); - NodeRef PushType(NodeRef type); NodeRef PushType(const DemangledTypeNode& type); NodeRef PushType(DemangledTypeNode&& type); - void PushEmptyTypeSubstitution(); NodeRef GetTypeRef(size_t ref); const DemangledTypeNode& GetType(size_t ref); bool AppendTemplateParamPackExpansion(ParamList& params, const NodeRef& expansion, bool functionParameter); - DemangledTypeNode CreateUnknownType(const StringList& s); - DemangledTypeNode CreateUnknownType(const _STD_STRING& s); - static void ExtendTypeName(DemangledTypeNode& type, const _STD_STRING& extend); - static void ApplyTemplateArgs(DemangledTypeNode& type, ParamList args); - static void AppendTypeName(DemangledTypeNode& type, const DemangledTypeNode& extend); - static _STD_STRING LastTypeNameSegmentBase(const DemangledTypeNode& type); - static bool LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNode& type); - #ifdef GNUDEMANGLE_DEBUG const DemangledTypeNode& GetTemplateType(size_t ref); void PrintTables(); #endif public: - DemangleGNU3(BN::Platform& platform, const _STD_STRING& mangledName); - void Reset(BN::Platform& platform, const _STD_STRING& mangledName); + DemangleGNU3(BN::Platform& platform, _STD_STRING mangledName); + void Reset(BN::Platform& platform, _STD_STRING mangledName); DemangledTypeNode DemangleSymbol(StringList& varName, bool simplifyTemplates = false); }; @@ -190,6 +176,6 @@ class DemangleGNU3 class DemangleGNU3Static { public: - static bool IsGNU3MangledString(const _STD_STRING& name); + static bool IsGNU3MangledString(std::string_view name); static bool DemangleGlobalHeader(_STD_STRING& name, _STD_STRING& header); }; diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index ae9727e3d..448bd2244 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -2604,26 +2604,29 @@ DemanglerResult Demangle::Finalize() return result; } -static std::optional DemangleMSWithConfig(const DemanglerConfig& config, const _STD_STRING& mangledName) +namespace { - if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) - return std::nullopt; - - try - { - thread_local Demangle demangle(config, mangledName); - demangle.Reset(config, mangledName); - return demangle.Finalize(); - } - catch (DemangleException& e) - { - LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); - } - catch (std::exception& e) + std::optional DemangleMSWithConfig(const DemanglerConfig& config, const _STD_STRING& mangledName) { - LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); + if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) + return std::nullopt; + + try + { + thread_local Demangle demangle(config, mangledName); + demangle.Reset(config, mangledName); + return demangle.Finalize(); + } + catch (DemangleException& e) + { + LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); + } + catch (std::exception& e) + { + LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); + } + return std::nullopt; } - return std::nullopt; } class MSDemangler: public Demangler @@ -2634,12 +2637,12 @@ class MSDemangler: public Demangler } ~MSDemangler() override = default; - bool IsMangledString(const _STD_STRING& name) override + virtual bool IsMangledString(const _STD_STRING& name) override { return !name.empty() && (name[0] == '?' || name[0] == '.'); } - std::optional Demangle(const string& name, const Config& config) override + virtual std::optional Demangle(const string& name, const Config& config) override { return DemangleMSWithConfig(config, name); } diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h index 6b0872695..83154b1f5 100644 --- a/demangler/msvc/demangle_msvc.h +++ b/demangler/msvc/demangle_msvc.h @@ -13,7 +13,6 @@ // limitations under the License. #pragma once -#include #include // XXX: Compiled directly into the core for performance reasons @@ -23,8 +22,6 @@ // refactor to use DemangledTypeNode and the simplifier refactor // its very possible this performance penalty is completely gone #ifdef BINARYNINJACORE_LIBRARY -#include "qualifiedname.h" -#include "type.h" #include "binaryview.h" #include "demangle.h" #define BN BinaryNinjaCore From f28afe48ea2c162b68829c6404b8f186bde871ad Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Wed, 15 Jul 2026 11:24:29 -0400 Subject: [PATCH 14/14] Fix some rust fmt+clippy warnings --- plugins/workflow_swift/src/demangler/mod.rs | 6 +----- rust/src/demangle.rs | 16 +++------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/plugins/workflow_swift/src/demangler/mod.rs b/plugins/workflow_swift/src/demangler/mod.rs index 415a397a2..9523920f3 100644 --- a/plugins/workflow_swift/src/demangler/mod.rs +++ b/plugins/workflow_swift/src/demangler/mod.rs @@ -28,11 +28,7 @@ impl CustomDemangler for SwiftDemangler { || name.starts_with("_T") } - fn demangle( - &self, - name: &str, - config: &DemanglerConfig, - ) -> Option { + fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option { let ctx = swift_demangler::Context::new(); let symbol = swift_demangler::Symbol::parse(&ctx, name)?; diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index fc76ff7fc..f28736b20 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -41,7 +41,7 @@ fn llvm_demangler() -> *mut BNDemangler { } /// Platform, view, and simplification options used by demangler APIs. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct DemanglerConfig { pub platform: Option>, pub view: Option>, @@ -143,16 +143,6 @@ impl DemanglerConfig { } } -impl Default for DemanglerConfig { - fn default() -> Self { - Self { - platform: None, - view: None, - simplify_templates: false, - } - } -} - /// Demangled name and optional type recovered from a mangled name. #[derive(Clone, Debug, PartialEq, Eq)] pub struct DemanglerResult { @@ -287,9 +277,9 @@ pub fn demangle_ms( pub fn simplify_demangled_template_name(name: impl Into) -> QualifiedName { let name = name.into(); - let mut raw_name = QualifiedName::into_raw(name.clone()); + let raw_name = QualifiedName::into_raw(name.clone()); let mut result = BNQualifiedName::default(); - let ok = unsafe { BNSimplifyDemangledTemplateName(&mut raw_name, &mut result) }; + let ok = unsafe { BNSimplifyDemangledTemplateName(&raw_name, &mut result) }; QualifiedName::free_raw(raw_name); if ok { QualifiedName::from_owned_raw(result)