From 8149774201203326096099394434871992996628 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Mon, 23 Sep 2024 17:15:17 -0400 Subject: Demangler plugin API Closes #467 --- binaryninjaapi.h | 115 ++ binaryninjacore.h | 45 +- demangle.cpp | 135 ++ demangler/gnu3/CMakeLists.txt | 40 + demangler/gnu3/demangle_gnu3.cpp | 2373 +++++++++++++++++++++++++++++++ demangler/gnu3/demangle_gnu3.h | 122 ++ demangler/msvc/CMakeLists.txt | 40 + demangler/msvc/demangle_msvc.cpp | 1887 ++++++++++++++++++++++++ demangler/msvc/demangle_msvc.h | 162 +++ python/demangle.py | 255 +++- rust/Cargo.lock | 8 + rust/Cargo.toml | 3 +- rust/examples/test_demangler/Cargo.toml | 17 + rust/examples/test_demangler/src/lib.rs | 64 + rust/src/demangle.rs | 258 +++- view/elf/elfview.cpp | 18 +- view/macho/machoview.cpp | 35 +- view/pe/coffview.cpp | 17 +- view/pe/peview.cpp | 49 +- 19 files changed, 5547 insertions(+), 96 deletions(-) create mode 100644 demangler/gnu3/CMakeLists.txt create mode 100644 demangler/gnu3/demangle_gnu3.cpp create mode 100644 demangler/gnu3/demangle_gnu3.h create mode 100644 demangler/msvc/CMakeLists.txt create mode 100644 demangler/msvc/demangle_msvc.cpp create mode 100644 demangler/msvc/demangle_msvc.h create mode 100644 rust/examples/test_demangler/Cargo.toml create mode 100644 rust/examples/test_demangler/src/lib.rs diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 3ba08f5f..1a59a58b 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1564,11 +1564,29 @@ namespace BinaryNinja { */ Ref Load(Ref rawData, bool updateAnalysis, std::function 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] arch Architecture for the symbol. Required for pointer and integer sizes. + \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(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + Ref view = nullptr, const bool simplify = false); + /*! Demangles using LLVM's demangler \param[in] mangledName a mangled (msvc/itanium/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 */ @@ -1579,6 +1597,7 @@ namespace BinaryNinja { \param[in] mangledName a mangled (msvc/itanium/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 */ @@ -1591,6 +1610,7 @@ namespace BinaryNinja { \param[out] outType Reference to Type to output \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 */ @@ -1607,6 +1627,7 @@ namespace BinaryNinja { \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 */ @@ -1620,6 +1641,7 @@ namespace BinaryNinja { \param[out] outType Reference to Type to output \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 */ @@ -1636,6 +1658,7 @@ namespace BinaryNinja { \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 */ @@ -17968,6 +17991,98 @@ namespace BinaryNinja { bool IsAborted(); }; + /*! + \ingroup demangler + */ + class Demangler: public StaticCoreRefCountObject + { + std::string m_nameForRegister; + + protected: + explicit Demangler(const std::string& name); + Demangler(BNDemangler* demangler); + 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); + static void FreeVarNameCallback(void* ctxt, BNQualifiedName* name); + + public: + /*! Register a custom Demangler. Newly registered demanglers will get priority over + previously registered demanglers and built-in demanglers. + */ + static void 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); + + /*! Promote a demangler to the highest-priority position. + + \param demangler Demangler to promote + */ + static void Promote(Ref demangler); + + std::string GetName() const; + + /*! Determine if a given name is mangled and this demangler can process it + + 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 + does not require the demangler to succeed the call to Demangle, but simply + implies that it may succeed. + + \param name Raw mangled name string + \return True if the demangler thinks it can handle the name + */ + virtual bool IsMangledString(const std::string& name) = 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. + 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 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 + */ + virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, + QualifiedName& outVarName, Ref view = nullptr) = 0; + }; + + /*! + \ingroup demangler + */ + class CoreDemangler: public Demangler + { + public: + CoreDemangler(BNDemangler* demangler); + 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); + }; + namespace Unicode { std::string UTF16ToUTF8(const uint8_t* utf16, const size_t len); diff --git a/binaryninjacore.h b/binaryninjacore.h index 5d5cb414..e08633b1 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -297,6 +297,7 @@ extern "C" typedef struct BNCollaborationLazyT BNCollaborationLazyT; typedef struct BNUndoAction BNUndoAction; typedef struct BNUndoEntry BNUndoEntry; + typedef struct BNDemangler BNDemangler; //! Console log levels typedef enum BNLogLevel @@ -3323,6 +3324,15 @@ extern "C" void (*freeConflictList)(void* context, BNAnalysisMergeConflict** conflictList, size_t count); } BNAnalysisMergeConflictSplitterCallbacks; + typedef struct BNDemanglerCallbacks + { + void* context; + bool (*isMangledString)(void* ctxt, const char* name); + bool (*demangle)(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, + BNQualifiedName* outVarName, BNBinaryView* view); + void (*freeVarName)(void* ctxt, BNQualifiedName* name); + } BNDemanglerCallbacks; + typedef bool(*BNProgressFunction)(void*, size_t, size_t); typedef bool(*BNCollaborationAnalysisConflictHandler)(void*, const char** keys, BNAnalysisMergeConflict** conflicts, size_t conflictCount); typedef bool(*BNCollaborationNameChangesetFunction)(void*, BNCollaborationChangeset*); @@ -6546,14 +6556,6 @@ extern "C" BINARYNINJACOREAPI BNTypeLibrary** BNGetPlatformTypeLibrariesByName( BNPlatform* platform, const char* depName, size_t* count); - // 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); - // Download providers BINARYNINJACOREAPI BNDownloadProvider* BNRegisterDownloadProvider( const char* name, BNDownloadProviderCallbacks* callbacks); @@ -6748,6 +6750,14 @@ extern "C" BNReportCollection* reports, BNBinaryView* view, const char* title, BNFlowGraph* graph); 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); @@ -6756,11 +6766,24 @@ extern "C" BINARYNINJACOREAPI void BNFreeDemangledName(char*** name, size_t nameElements); BINARYNINJACOREAPI bool BNDemangleLLVM(const char* mangledName, - char*** outVarName, size_t* outVarNameElements, const bool simplify); + char*** outVarName, size_t* outVarNameElements, const bool simplify); BINARYNINJACOREAPI bool BNDemangleLLVMWithOptions(const char* mangledName, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); + char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); + + BINARYNINJACOREAPI BNDemangler* BNRegisterDemangler(const char* name, 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 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 +// Plugin repository APIs BINARYNINJACOREAPI char** BNPluginGetApis(BNRepoPlugin* p, size_t* count); BINARYNINJACOREAPI const char* BNPluginGetAuthor(BNRepoPlugin* p); BINARYNINJACOREAPI const char* BNPluginGetDescription(BNRepoPlugin* p); diff --git a/demangle.cpp b/demangle.cpp index 3217905c..ad392d47 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -1,8 +1,27 @@ #include "binaryninjaapi.h" #include using namespace std; +using namespace BinaryNinja; namespace BinaryNinja { + bool DemangleGeneric(Ref arch, const std::string& name, Ref& outType, + QualifiedName& outVarName, Ref view, bool simplify) + { + BNType* apiType; + BNQualifiedName apiVarName; + bool success = BNDemangleGeneric( + arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr, simplify); + + if (!success) + return false; + + if (apiType) + outType = new Type(apiType); + outVarName = QualifiedName::FromAPIObject(&apiVarName); + BNFreeQualifiedName(&apiVarName); + return true; + } + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, BinaryView* view) { @@ -109,4 +128,120 @@ namespace BinaryNinja { BNFreeQualifiedName(&name); return result; } + + Demangler::Demangler(const std::string& name): m_nameForRegister(name) + { + } + + Demangler::Demangler(BNDemangler* demangler) + { + m_object = demangler; + } + + bool Demangler::IsMangledStringCallback(void* ctxt, const char* name) + { + Demangler* demangler = (Demangler*)ctxt; + return demangler->IsMangledString(name); + } + + bool Demangler::DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, + BNQualifiedName* outVarName, BNBinaryView* view) + { + Demangler* demangler = (Demangler*)ctxt; + + Ref apiArch = new CoreArchitecture(arch); + Ref apiView = view ? new BinaryView(BNNewViewReference(view)) : nullptr; + + Ref apiType; + QualifiedName apiVarName; + bool success = demangler->Demangle(apiArch, name, apiType, apiVarName, apiView); + if (!success) + return false; + + if (apiType) + { + *outType = BNNewTypeReference(apiType->m_object); + } + else + { + *outType = nullptr; + } + *outVarName = apiVarName.GetAPIObject(); + + return true; + } + + void Demangler::FreeVarNameCallback(void* ctxt, BNQualifiedName* name) + { + QualifiedName::FreeAPIObject(name); + } + + void Demangler::Register(Demangler* demangler) + { + BNDemanglerCallbacks cb; + cb.context = (void*)demangler; + cb.isMangledString = IsMangledStringCallback; + cb.demangle = DemangleCallback; + cb.freeVarName = FreeVarNameCallback; + demangler->m_object = BNRegisterDemangler(demangler->m_nameForRegister.c_str(), &cb); + } + + std::vector> Demangler::GetList() + { + size_t count; + BNDemangler** list = BNGetDemanglerList(&count); + vector> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreDemangler(list[i])); + BNFreeDemanglerList(list); + return result; + } + + Ref Demangler::GetByName(const std::string& name) + { + BNDemangler* result = BNGetDemanglerByName(name.c_str()); + if (!result) + return nullptr; + return new CoreDemangler(result); + } + + void Demangler::Promote(Ref demangler) + { + BNPromoteDemangler(demangler->m_object); + } + + std::string Demangler::GetName() const + { + char* name = BNGetDemanglerName(m_object); + std::string value = name; + BNFreeString(name); + return value; + } + + CoreDemangler::CoreDemangler(BNDemangler* demangler): Demangler(demangler) + { + } + + bool CoreDemangler::IsMangledString(const std::string& name) + { + return BNIsDemanglerMangledName(m_object, name.c_str()); + } + + bool CoreDemangler::Demangle(Ref arch, const std::string& name, Ref& outType, + QualifiedName& outVarName, Ref view) + { + BNType* apiType; + BNQualifiedName apiVarName; + bool success = BNDemanglerDemangle( + m_object, arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr); + + if (!success) + return false; + + if (apiType) + outType = new Type(apiType); + outVarName = QualifiedName::FromAPIObject(&apiVarName); + BNFreeQualifiedName(&apiVarName); + return true; + } } // namespace BinaryNinja diff --git a/demangler/gnu3/CMakeLists.txt b/demangler/gnu3/CMakeLists.txt new file mode 100644 index 00000000..b7b4b149 --- /dev/null +++ b/demangler/gnu3/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(demangle_gnu3) + +file(GLOB SOURCES + *.cpp + *.c + *.h) + +add_library(${PROJECT_NAME} SHARED ${SOURCES}) + +if(NOT BN_INTERNAL_BUILD) + # Out-of-tree build + find_path( + BN_API_PATH + NAMES binaryninjaapi.h + HINTS ../../.. binaryninjaapi $ENV{BN_API_PATH} + REQUIRED + ) + add_subdirectory(${BN_API_PATH} api) +endif() + +target_link_libraries(${PROJECT_NAME} binaryninjaapi) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + C_STANDARD 99 + C_STANDARD_REQUIRED ON + C_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON) + +if(BN_INTERNAL_BUILD) + plugin_rpath(${PROJECT_NAME}) + set_target_properties(${PROJECT_NAME} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp new file mode 100644 index 00000000..412003d1 --- /dev/null +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -0,0 +1,2373 @@ +// Copyright 2016-2024 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. + +// Includes snippets from LLVM, which is under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. + +#include "binaryninjaapi.h" +#include +#include +#include +#include "demangle_gnu3.h" + + +using namespace BinaryNinja; +using namespace std; + + +#define MAX_DEMANGLE_LENGTH 4096 +#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(string fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(0, DebugLog, (_indent + fmt).c_str(), args); + va_end(args); +} +#else +#define indent() +#define dedent() +void MyLogDebug(string fmt, ...) +{ (void)fmt; } +#endif + +static inline void rtrim(string &s) +{ + s.erase(find_if(s.rbegin(), s.rend(), [](int c) { return !isspace(c); }).base(), s.end()); +} + + +static string GetTemplateString(vector args) +{ + string name = "<"; + for (size_t i = 0; i < args.size(); i++) + { + if (i != 0) + { + name += ", "; + } + + name += args[i].name; + } + rtrim(name); + if (name.back() == '>') + name += " "; //Be c++03 compliant where we can + name += ">"; + return name; +} + + +static void ExtendTypeName(TypeBuilder& type, const string& extend) +{ + QualifiedName qn = type.GetTypeName(); + if (qn.StringSize() + extend.size() > MAX_DEMANGLE_LENGTH) + throw DemangleException("Detected adversarial mangled string"); + if (qn.size() > 0) + qn.back() += extend; + else + qn.push_back(extend); + type.SetTypeName(qn); +} + + +static TypeBuilder CreateUnknownType(const QualifiedName& s) +{ + return TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, s)); +} + + +static TypeBuilder CreateUnknownType(const string& s) +{ + return TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, {s})); +} + +DemangleGNU3::Reader::Reader(const string& data): m_data(data), m_offset(0) +{} + + +string DemangleGNU3::Reader::PeekString(size_t count) +{ + if (count > Length()) + return "\0"; + return m_data.substr(m_offset, count); +} + + +char DemangleGNU3::Reader::Peek() +{ + if (1 > Length()) + return '\0'; + return (char)m_data[m_offset]; +} + + +bool DemangleGNU3::Reader::NextIsOneOf(const string& list) +{ + char elm = Peek(); + for (auto a : list) + { + if (a == elm) + return true; + } + return false; +} + + +string DemangleGNU3::Reader::GetRaw() +{ + return m_data.substr(m_offset); +} + + +char DemangleGNU3::Reader::Read() +{ + if (1 > Length()) + throw DemangleException(); + return m_data[m_offset++]; +} + + +string DemangleGNU3::Reader::ReadString(size_t count) +{ + if (count > Length()) + throw DemangleException(); + + const string out = m_data.substr(m_offset, count); + m_offset += count; + return out; +} + + +string DemangleGNU3::Reader::ReadUntil(char sentinal) +{ + size_t pos = m_data.find_first_of(sentinal, m_offset); + if (pos == string::npos) + throw DemangleException(); + return ReadString(pos); +} + + +void DemangleGNU3::Reader::UnRead(size_t count) +{ + if (count <= m_offset) + m_offset -= count; +} + + +void DemangleGNU3::Reader::Consume(size_t count) +{ + if (count > Length()) + throw DemangleException(); + m_offset += count; +} + + +size_t DemangleGNU3::Reader::Length() const +{ + return m_data.length() - m_offset; +} + + +DemangleGNU3::DemangleGNU3(Architecture* arch, const string& mangledName) : + m_reader(mangledName), + m_arch(arch), + m_isParameter(false), + m_shouldDeleteReader(true), + m_topLevel(true), + m_isOperatorOverload(false) +{ + MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); +} + + +void DemangleGNU3::PushTemplateType(TypeBuilder type) +{ + m_templateSubstitute.push_back(type); +} + + +const TypeBuilder& DemangleGNU3::GetTemplateType(size_t ref) +{ + if (ref >= m_templateSubstitute.size()) + { + // PrintTables(); + throw DemangleException(); + } + return m_templateSubstitute[ref]; +} + + +void DemangleGNU3::PushType(TypeBuilder type) +{ + m_substitute.push_back(type); +} + + +const TypeBuilder& DemangleGNU3::GetType(size_t ref) +{ + if (ref >= m_substitute.size()) + { + // PrintTables(); + throw DemangleException(); + } + return m_substitute[ref]; +} + + +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()); + } + + 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()); + } +} + + +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); + } +} + + +string DemangleGNU3::DemangleSourceName() +{ + indent(); + MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + m_lastName = m_reader.ReadString(DemangleNumber()); + dedent(); + return m_lastName; +} + + +TypeBuilder DemangleGNU3::DemangleFunction(bool cnst, bool vltl) +{ + indent(); + MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + bool old_isparam; + if (m_reader.Peek() == 'Y') + { + // TODO: This function is external, should we do anything with that info? + m_reader.Consume(); + } + + TypeBuilder retType = DemangleType(); + + vector params; + old_isparam = m_isParameter; + m_isParameter = true; + m_functionSubstitute.push_back({}); + int i = 0; + while (m_reader.Peek() != 'E') + { + TypeBuilder param = DemangleType(); + if (param.GetClass() == VoidTypeClass) + continue; + MyLogDebug("Var_%d - %s\n", i++, param.GetString().c_str()); + m_functionSubstitute.back().push_back(param); + params.push_back({"", param.Finalize(), true, Variable()}); + } + m_reader.Consume(); + m_functionSubstitute.pop_back(); + m_isParameter = old_isparam; + TypeBuilder newType = TypeBuilder::FunctionType(retType.Finalize(), nullptr, params); + PushType(newType); + + newType.SetConst(cnst); + newType.SetVolatile(vltl); + + if (cnst || vltl) + PushType(newType); + MyLogDebug("After %s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + dedent(); + return newType; +} + + +const TypeBuilder& DemangleGNU3::DemangleTemplateSubstitution() +{ + indent(); + MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + size_t number = 0; + char elm = m_reader.Peek(); + if (elm == '_') + { + number = 0; + } + else if (isdigit(elm)) + { + m_reader.Consume(); + number = elm - '0' + 1; + } + else if (isupper(elm)) + { + m_reader.Consume(); + number = elm - 'A' + 11; + } + else + { + throw DemangleException(); + } + + if (m_reader.Read() != '_') + { + throw DemangleException(); + } + dedent(); + return GetTemplateType(number); +} + + +TypeBuilder DemangleGNU3::DemangleType() +{ + indent(); + MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + TypeBuilder type; + bool cnst = false, vltl = false, rstrct = false; + bool substitute = false; + QualifiedName name; + + DemangleCVQualifiers(cnst, vltl, rstrct); + + if (cnst || vltl || rstrct) + { + type = DemangleType(); + if (cnst) + type.SetConst(true); + if (vltl) + type.SetVolatile(true); + if (rstrct) + type.SetPointerSuffix({RestrictSuffix}); + PushType(type); + return type; + } + + switch(m_reader.Read()) + { + case 'S': + { + if (isdigit(m_reader.Peek()) || m_reader.Peek() == '_' || isupper(m_reader.Peek())) + { + type = DemangleSubstitution(); + if (m_reader.Peek() == 'I') + { + m_reader.Consume(); + vector args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + substitute = true; + } + } + else + { + if (m_reader.Peek() == 't') + { + m_reader.Consume(1); + type = DemangleUnqualifiedName(); + QualifiedName qn = type.GetTypeName(); + qn.insert(qn.begin(), "std"); + type.SetTypeName(qn); + substitute = true; + } + else + { + type = DemangleSubstitution(); + } + if (m_reader.Peek() == 'I') + { + m_reader.Consume(); + if (substitute) + PushType(type); + vector args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + substitute = true; + } + } + break; + } + case 'T': + { + /* ::= # non-dependent type name, dependent type name, or dependent typename-specifier + ::= Ts # dependent elaborated type specifier using 'struct' or 'class' + ::= Tu # dependent elaborated type specifier using 'union' + ::= Te # dependent elaborated type specifier using 'enum' + */ + if (m_reader.Peek() == 's') + { + m_reader.Consume(); + type = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + StructNamedTypeClass, {DemangleSourceName()})); + break; + } + else if (m_reader.Peek() == 'u') + { + m_reader.Consume(); + type = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + UnionNamedTypeClass, {DemangleSourceName()})); + break; + } + else if (m_reader.Peek() == 'e') + { + m_reader.Consume(); + type = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + EnumNamedTypeClass, {DemangleSourceName()}), + m_arch->GetDefaultIntegerSize(), m_arch->GetDefaultIntegerSize()); + break; + } + + //Template Substitution + type = DemangleTemplateSubstitution(); + substitute = true; + if (m_reader.Peek() == 'I') + { + m_reader.Consume(); + if (substitute) + PushType(type); + vector args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + } + break; + } + case 'P': + type = TypeBuilder::PointerType(m_arch, DemangleType().Finalize(), cnst, vltl, PointerReferenceType); + substitute = true; + break; + case 'R': + type = TypeBuilder::PointerType(m_arch, DemangleType().Finalize(), cnst, vltl, ReferenceReferenceType); + substitute = true; + break; + case 'O': + type = TypeBuilder::PointerType(m_arch, DemangleType().Finalize(), cnst, vltl, RValueReferenceType); + substitute = true; + break; + case 'C': //TODO:complex + case 'G': //TODO:imaginary + case 'U': //TODO:vendor extended type + throw DemangleException(); + case 'v': type = TypeBuilder::VoidType(); break; + case 'w': type = TypeBuilder::IntegerType(4, false, "wchar_t"); break; //TODO: verify + case 'b': type = TypeBuilder::BoolType(); break; + case 'c': type = TypeBuilder::IntegerType(1, true); break; + case 'a': type = TypeBuilder::IntegerType(1, true); break; + case 'h': type = TypeBuilder::IntegerType(1, false); break; + case 's': type = TypeBuilder::IntegerType(2, true); break; + case 't': type = TypeBuilder::IntegerType(2, false); break; + case 'i': type = TypeBuilder::IntegerType(4, true); break; + case 'j': type = TypeBuilder::IntegerType(4, false); break; + case 'l': type = TypeBuilder::IntegerType(m_arch->GetAddressSize(), true); break; //long + case 'm': type = TypeBuilder::IntegerType(m_arch->GetAddressSize(), false); break; //ulong + case 'x': type = TypeBuilder::IntegerType(8, true); break; + case 'y': type = TypeBuilder::IntegerType(8, false); break; + case 'n': type = TypeBuilder::IntegerType(16, true); break; + case 'o': type = TypeBuilder::IntegerType(16, false); break; + case 'f': type = TypeBuilder::FloatType(4); break; + case 'd': type = TypeBuilder::FloatType(8); break; + case 'e': type = TypeBuilder::FloatType(10); break; + case 'g': type = TypeBuilder::FloatType(16); break; + case 'z': type = TypeBuilder::VarArgsType(); break; + case 'M': // TODO: Make into pointer to function member + { + TypeBuilder name = DemangleType(); + TypeBuilder member = DemangleType(); + string fullName = member.GetStringBeforeName() + "(" + name.GetString() + "::*)" + member.GetStringAfterName(); + //member.SetScope(NonStaticScope); + //TypeBuilder ptr = TypeBuilder::PointerType(m_arch, member, cnst, vltl); + //QualifiedName qn({name.GetString(), "*"}); + type = CreateUnknownType(fullName); + break; + } + case 'F': type = DemangleFunction(cnst, vltl); break; + case 'D': + switch (m_reader.Read()) + { + case 'd': type = TypeBuilder::FloatType(8); break; + case 'e': type = TypeBuilder::FloatType(16); break; + case 'f': type = TypeBuilder::FloatType(4); break; + case 'h': type = TypeBuilder::FloatType(2); break; + case 'i': type = TypeBuilder::IntegerType(4, true, "char32_t"); break; + case 's': type = TypeBuilder::IntegerType(2, true, "char16_t"); break; + case 'a': type = CreateUnknownType("auto"); break; //auto type + case 'c': type = CreateUnknownType("decltype(auto)"); break; //decltype(auto) + case 'n': + { + static const QualifiedName stdNullptrTName(vector{"std", "nullptr_t"}); + type = CreateUnknownType(stdNullptrTName); + break; + } + case 'p': type = DemangleType(); break; + case 't': + case 'T': + type = CreateUnknownType(DemangleExpression()); + if (m_reader.Read() != 'E') + throw DemangleException(); + break; + case 'v': + { + // vector of size + uint64_t size = DemangleNumber(); + if (m_reader.Read() != '_') + throw DemangleException(); + type = TypeBuilder::ArrayType(DemangleType().Finalize(), size); + break; + } + default: + MyLogDebug("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + throw DemangleException(); + } + break; + case 'N': + type = DemangleNestedName(); + substitute = true; + break; + case 'A': + // ::= A _ + // ::= A [] _ + if (isdigit(m_reader.Peek())) + { + // _ + uint64_t size = DemangleNumber(); + if (m_reader.Read() != '_') + throw DemangleException(); + type = TypeBuilder::ArrayType(DemangleType().Finalize(), size); + } + else + { + //[] _ + //Since our type system doesn't support expressions as dimensions + //we instead demangle this as just a string. + string dimension = "[]"; + if (m_reader.Peek() != '_') + { + dimension = "[" + DemangleExpression() + "]"; + } + if (m_reader.Read() != '_') + throw DemangleException(); + + const string typeString = DemangleType().GetString() + dimension; + type = CreateUnknownType(typeString); + } + substitute = true; + break; + default: + { + m_reader.UnRead(); + + type = DemangleName(); + auto nameList = type.GetTypeName(); + if (nameList.size() < 1) + throw DemangleException(); + m_lastName = nameList.back(); + substitute = true; + + if (m_reader.Peek() == 'I') + { + substitute = false; + m_reader.Consume(); + PushType(type); + vector args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + PushType(type); + } + } + } + + if (substitute) + PushType(type); + + dedent(); + return type; +} + + +TypeBuilder DemangleGNU3::DemangleSubstitution() +{ + static const QualifiedName stdAllocatorName(vector{"std", "allocator"}); + static const QualifiedName stdBasicStringName(vector{"std", "basic_string"}); + static const QualifiedName stdIostreamName(vector{"std", "iostream"}); + static const QualifiedName stdIstreamName(vector{"std", "istream"}); + static const QualifiedName stdOstreamName(vector{"std", "ostream"}); + static const QualifiedName stdStringName(vector{"std", "string"}); + static const QualifiedName stdName(vector{"std"}); + + indent() + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + char elm; + elm = m_reader.Read(); + QualifiedName name; + size_t number = 0; + switch (elm) + { + case 'a': name = stdAllocatorName; break; + case 'b': name = stdBasicStringName; break; + case 'd': name = stdIostreamName; break; + case 'i': name = stdIstreamName; break; + case 'o': name = stdOstreamName; break; + case 's': name = stdStringName; break; + case 't': name = stdName; break; + default: + if (elm == '_') + { + m_reader.UnRead(1); + number = 0; + } + else if (isdigit(elm)) + { + number = elm - '0' + 1; + } + else if (isupper(elm)) + { + number = elm - 'A' + 11; + } + else + { + // PrintTables(); + throw DemangleException(); + } + + if (m_reader.Read() != '_') + { + throw DemangleException(); + } + + dedent(); + return GetType(number); + } + m_lastName = name.back(); + dedent(); + return CreateUnknownType(name); +} + +string DemangleGNU3::DemangleNumberAsString() +{ + bool negativeFactor = false; + if ( m_reader.Peek() == 'n') + { + negativeFactor = true; + m_reader.Consume(); + } + + string number; + while (isdigit(m_reader.Peek())) + { + number += m_reader.ReadString(1); + } + return (negativeFactor?"-":"") + number; +} + +// number ::= [n] +int64_t DemangleGNU3::DemangleNumber() +{ + return std::stol(DemangleNumberAsString().c_str()); +} + + +string DemangleGNU3::DemangleInitializer() +{ + string out; + if (m_reader.ReadString(2) != "pi") + throw DemangleException(); + out += "("; + while (m_reader.Peek() != 'E') + out += DemangleExpression(); + m_reader.Consume(); + out += ")"; + return out; +} + +static int8_t HexToDec(char c) +{ + if (isdigit(c)) + { + return c - '0'; + } + else if(islower(c) && c <= 'f') + { + return c - 'a' + 10; + } + return -1; +} + +string DemangleGNU3::DemanglePrimaryExpression() +{ + indent(); + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + char elm1 = '\0'; + string out; + QualifiedName tmpList; + string valueString; + float f; double d; long double ld; + bool oldTopLevel; + //expr-primary + if (m_reader.PeekString(2) == "_Z") + { + m_reader.Consume(2); + oldTopLevel = m_topLevel; + m_topLevel = false; + TypeBuilder t = DemangleSymbol(tmpList); + m_topLevel = oldTopLevel; + out += t.GetStringBeforeName(); + out += tmpList.GetString(); + out += t.GetStringAfterName(); + dedent() + return out; + } + switch (m_reader.Read()) + { + case 'b': + elm1 = m_reader.Read(); + if (elm1 == '0') + out += "false"; + else if (elm1 == '1') + out += "true"; + else + throw DemangleException(); + break; + case 'd': //double + valueString = m_reader.ReadString(8); + + for (size_t i = 0; i < valueString.size(); i+=2) + { + ((unsigned char*)&d)[i/2] = (HexToDec(valueString[i]) << 16) + HexToDec(valueString[i+1]); + } + out += to_string(d); + break; + case 'e': //long double + valueString = m_reader.ReadString(10); + + for (size_t i = 0; i < valueString.size(); i+=2) + { + ((unsigned char*)&ld)[i/2] = (HexToDec(valueString[i]) << 16) + HexToDec(valueString[i+1]); + } + out += to_string(ld); + break; + case 'f': //float + valueString = m_reader.ReadString(4); + + for (size_t i = 0; i < valueString.size(); i+=2) + { + ((unsigned char*)&f)[i/2] = (HexToDec(valueString[i]) << 16) + HexToDec(valueString[i+1]); + } + out += to_string(f); + break; + case 'g': //float_128 + valueString = m_reader.ReadString(16); //We read 16 but then just throw away + + for (size_t i = 0; i < 10; i+=2) + { + ((unsigned char*)&ld)[i/2] = (HexToDec(valueString[i]) << 16) + HexToDec(valueString[i+1]); + } + out += to_string(ld); + break; + case 'l': out = DemangleNumberAsString() + "l"; break; //long + case 'x': out = DemangleNumberAsString() + "ll"; break; //long long + case 's': out = "(short)" + DemangleNumberAsString(); break; //short + case 'n': out = "(__uint128)" + DemangleNumberAsString() + "ull"; break; //__int128 + case 'i': out = DemangleNumberAsString(); break; // int + case 'm': out = DemangleNumberAsString() + "ul"; break; //unsigned long + case 't': out = "(unsigned short)" + DemangleNumberAsString(); break; //unsigned short + case 'y': out = DemangleNumberAsString() + "ull"; break; //unsigned long long + case 'j': out = DemangleNumberAsString() + "u"; break; // unsigned int + break; + default: + m_reader.UnRead(1); + out = "(" + DemangleTypeString() + ")" + DemangleNumberAsString(); + break; + } + if (m_reader.Read() != 'E') + throw DemangleException(); + + dedent(); + return out; +} + + +string DemangleGNU3::DemangleUnarySuffixExpression(const string& op) +{ + return "(" + DemangleExpression() + ")" + op; +} + + +string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op) +{ + return op + "(" + DemangleExpression() + ")"; +} + + +string DemangleGNU3::DemangleBinaryExpression(const string& op) +{ + indent(); + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + const string lhs = "(" + DemangleExpression() + ")"; + const string rhs = "(" + DemangleExpression() + ")"; + dedent(); + if (op == ".") + return lhs + op + rhs; + return lhs + " " + op + " " + rhs; +} + + +string DemangleGNU3::DemangleUnaryPrefixType(const string& op) +{ + return op + "(" + DemangleTypeString() + ")"; +} + + +string DemangleGNU3::DemangleTypeString() +{ + return DemangleType().GetString(); +} + + +string DemangleGNU3::DemangleExpressionList() +{ + indent(); + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + string expr; + bool first = true; + m_functionSubstitute.push_back({}); + while (m_reader.Peek() != 'E') + { + if (!first) + expr += ", "; + const string e = DemangleExpression(); + expr += e; + m_functionSubstitute.back().push_back(CreateUnknownType(e)); + first = false; + } + m_functionSubstitute.pop_back(); + m_reader.Consume(); + dedent(); + return expr; +} + +static string 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'): 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('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('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 ""; + } +} + +static BNNameType GetNameType(char elm1, char elm2) +{ + 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 [] + case hash('C','1'): return ConstructorNameType; + case hash('C','2'): return ConstructorNameType; + case hash('C','3'): return ConstructorNameType; + case hash('C','4'): return ConstructorNameType; + case hash('C','5'): return ConstructorNameType; + case hash('D','0'): return DestructorNameType; + case hash('D','1'): return DestructorNameType; + case hash('D','2'): return DestructorNameType; + case hash('D','3'): return DestructorNameType; + case hash('D','4'): return DestructorNameType; + case hash('D','5'): return DestructorNameType; + default: + return NoNameType; + } +} + +TypeBuilder DemangleGNU3::DemangleUnqualifiedName() +{ + indent() + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + + TypeBuilder outType; + char elm1 = m_reader.Read(); + char elm2 = m_reader.Read(); + switch (hash(elm1, elm2)) + { + case hash('n','t'): // ! + case hash('n','g'): // - (unary) + case hash('p','s'): // + (unary) + case hash('a','d'): // & (unary) + case hash('d','e'): // * (unary) + case hash('i','x'): // [] + case hash('p','p'): // ++ (postfix in context) + case hash('m','m'): // -- (postfix in context) + case hash('l','s'): // << + case hash('r','s'): // >> + case hash('a','S'): // = + case hash('e','q'): // == + case hash('n','e'): // != + case hash('p','t'): // -> + case hash('d','t'): // . + case hash('m','l'): // * + case hash('m','i'): // - + case hash('p','l'): // + + case hash('a','n'): // & + case hash('p','m'): // ->* + case hash('d','v'): // / + case hash('r','m'): // % + case hash('l','t'): // < + case hash('l','e'): // <= + case hash('g','t'): // > + case hash('g','e'): // >= + case hash('c','m'): // , + case hash('c','l'): // () + case hash('c','o'): // ~ + case hash('e','o'): // ^ + case hash('o','r'): // | + case hash('a','a'): // && + case hash('o','o'): // || + case hash('m','L'): // *= + case hash('p','L'): // += + case hash('m','I'): // -= + case hash('d','V'): // /= + case hash('r','M'): // %= + case hash('r','S'): // >>= + case hash('l','S'): // <<= + case hash('a','N'): // &= + case hash('o','R'): // |= + case hash('e','O'): // ^= + outType = CreateUnknownType("operator" + GetOperator(elm1, elm2)); + outType.SetNameType(GetNameType(elm1, elm2)); + break; + case hash('t','i'): + case hash('t','e'): + case hash('s','t'): + case hash('s','z'): + case hash('a','t'): + case hash('a','z'): + case hash('n','x'): + case hash('s','Z'): + case hash('s','P'): + case hash('s','p'): + case hash('d','l'): // delete + case hash('d','a'): // delete[] + case hash('n','w'): // new + case hash('n','a'): // new [] + outType = CreateUnknownType("operator " + GetOperator(elm1, elm2)); + outType.SetNameType(GetNameType(elm1, elm2)); + break; + case hash('v','0'): + case hash('v','1'): + case hash('v','2'): + case hash('v','3'): + case hash('v','4'): + case hash('v','5'): + case hash('v','6'): + case hash('v','7'): + case hash('v','8'): + case hash('v','9'): + //TODO: Unsupported vendor extended types + throw DemangleException(); + case hash('C','1'): //Construtor + case hash('C','2'): + case hash('C','3'): + case hash('C','4'): + case hash('C','5'): + outType = CreateUnknownType(m_lastName); + outType.SetNameType(ConstructorNameType); + break; + case hash('D','0'): //Destructor + case hash('D','1'): + case hash('D','2'): + case hash('D','3'): + case hash('D','4'): + case hash('D','5'): + outType = CreateUnknownType("~" + m_lastName); + outType.SetNameType(DestructorNameType); + break; + case hash('D','t'): + case hash('D','T'): + outType = CreateUnknownType(DemangleExpression()); + // if (m_reader.Read() != 'E') + // throw DemangleException(); + break; + case hash('U','l'): //Lambda + { + string name; + name = "'lambda"; + vector params; + do + { + TypeBuilder param = DemangleType(); + if (param.GetClass() == VoidTypeClass) + break; + params.push_back(std::move(param)); + }while (m_reader.Peek() != 'E'); + m_reader.Consume(); + + if (isdigit(m_reader.Peek())) + { + name += DemangleNumberAsString(); + } + if (m_reader.Read() != '_') + throw DemangleException(); + + name += "'("; + for (size_t i = 0; i < params.size(); i++) + { + if (i != 0) + name += ", "; + name += params[i].GetString(); + } + name += ")"; + m_lastName = name; + outType = CreateUnknownType(name); + break; + } + case hash('U','t'): + { + string name; + name = "'unnamed"; + + if (isdigit(m_reader.Peek())) + { + name += DemangleNumberAsString(); + } + name += "\'"; + + if (m_reader.Read() != '_') + throw DemangleException(); + + m_lastName = name; + outType = CreateUnknownType(name); + break; + } + case hash('c','v'): //type (expression) + outType = CreateUnknownType("operator " + DemangleType().GetString()); + break; + default: + m_reader.UnRead(2); + if (isdigit(m_reader.Peek()) || m_reader.Read() == 'L') + { + string name = DemangleSourceName(); + if (name.size() > 11 && name.substr(0, 11) == "_GLOBAL__N_") + name = "(anonymous namespace)"; + outType = CreateUnknownType(name); + } + else + { + throw DemangleException(); + } + } + dedent(); + return outType; +} + + +QualifiedName DemangleGNU3::DemangleBaseUnresolvedName() +{ + // ::= # unresolved name + // ::= on # unresolved operator-function-id + // ::= on # unresolved operator template-id + // ::= dn # destructor or pseudo-destructor; + // # e.g. ~X or ~X + + indent() + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + QualifiedName out; + if (m_reader.Length() > 1) + { + const string str = m_reader.PeekString(2); + if (str == "on") + { + out.push_back(GetOperator(m_reader.Read(), m_reader.Read())); + if (m_reader.Peek() == 'I') + { + m_reader.Consume(); + vector args; + DemangleTemplateArgs(args); + out.back() += GetTemplateString(args); + PushType(CreateUnknownType(out)); + } + } + else if (str == "dn") + { + string name = DemangleUnresolvedType().GetString(); + if (name.empty()) + out.push_back("~" + DemangleSourceName()); + else + out.push_back("~" + name); + } + else + { + // + out.push_back(DemangleSourceName()); + if (m_reader.Peek() == 'I') + { + m_reader.Consume(); + vector args; + DemangleTemplateArgs(args); + out.back() += GetTemplateString(args); + } + } + } + dedent(); + return out; +} + + +TypeBuilder DemangleGNU3::DemangleUnresolvedType() +{ + indent(); + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + // ::= [ ] # T:: or T:: + // ::= # decltype(p):: + // ::= + TypeBuilder type; + if (m_reader.Peek() == 'T') + { + m_reader.Consume(); + type = DemangleTemplateSubstitution(); + if (m_reader.Peek() == 'I') + { + PushType(type); + m_reader.Consume(); + vector args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + PushType(type); + } + } + else if (m_reader.Length() > 2 && (m_reader.PeekString(2) == "Dt" || m_reader.PeekString(2) == "DT")) + { + const string name = "decltype(" + DemangleExpression() + ")"; + type = CreateUnknownType(name); + } + else if (m_reader.Peek() == 'S') + { + m_reader.Consume(); + type = DemangleSubstitution(); + } + else + { + throw DemangleException(); + } + dedent(); + return type; +} + + +string DemangleGNU3::DemangleExpression() +{ + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + /* + ::= + ::= + ::= + ::= pp_ # prefix ++ + ::= mm_ # prefix -- + ::= cl + E # expression (expr-list), call + ::= cv # type (expression), conversion with one argument + ::= cv _ * E # type (expr-list), conversion with other than one argument + ::= tl * E # type {expr-list}, conversion with braced-init-list argument + ::= il E # {expr-list}, braced-init-list in any other context + ::= [gs] nw * _ E # new (expr-list) type + ::= [gs] nw * _ # new (expr-list) type (init) + ::= [gs] na * _ E # new[] (expr-list) type + ::= [gs] na * _ # new[] (expr-list) type (init) + ::= [gs] dl # delete expression + ::= [gs] da # delete[] expression + ::= dc # dynamic_cast (expression) + ::= sc # static_cast (expression) + ::= cc # const_cast (expression) + ::= rc # reinterpret_cast (expression) + ::= ti # typeid (type) + ::= te # typeid (expression) + ::= st # sizeof (type) + ::= sz # sizeof (expression) + ::= at # alignof (type) + ::= az # alignof (expression) + ::= nx # noexcept (expression) + ::= + ::= + ::= dt # expr.name + ::= pt # expr->name + ::= ds # expr.*expr + ::= sZ # sizeof...(T), size of a template parameter pack + ::= sZ # sizeof...(parameter), size of a function parameter pack + ::= sP * E # sizeof...(T), size of a captured template parameter pack from an alias template + ::= sp # expression..., pack expansion + ::= tw # throw expression + ::= tr # throw with no operand (rethrow) + ::= # f(p), N::f(p), ::f(p), + # freestanding dependent name (e.g., T::x), + # objectless nonstatic member reference + ::= + */ + char elm1 = '\0', elm2 = '\0'; + string gs, out; + elm1 = m_reader.Read(); + if (elm1 == 'L') + { + out = DemanglePrimaryExpression(); + return out; + } + else if (elm1 == 'T') // + { + return DemangleTemplateSubstitution().GetString(); + } + + elm2 = m_reader.Read(); + if (hash(elm1, elm2) == hash('g', 's')) + { + elm1 = m_reader.Read(); + elm2 = m_reader.Read(); + switch (hash(elm1, elm2)) + { + case hash('s','r'): + case hash('n','w'): + case hash('n','a'): + case hash('d','l'): + case hash('d','a'): break; + default: + throw DemangleException(); + } + gs = "::"; + } + + switch (hash(elm1, elm2)) + { + case hash('d','c'): + case hash('s','c'): + case hash('c','c'): + case hash('r','c'): + return GetOperator(elm1, elm2) + "<" + DemangleTypeString() + ">(" + DemangleExpression() + ")"; + case hash('t','i'): + case hash('t','e'): + case hash('s','t'): + case hash('s','z'): + 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() + ")"; + case hash('s','P'): + { + vector args; + DemangleTemplateArgs(args); + return "sizeof...(" + GetTemplateString(args) + ")..."; + } + case hash('s','p'): + return "(" + DemangleExpression() + ")..."; + case hash('t','w'): + return GetOperator(elm1, elm2) + DemangleExpression(); + case hash('t','r'): + return GetOperator(elm1, elm2); //rethrow + case hash('n','t'): // ! + case hash('n','g'): // - (unary) + case hash('p','s'): // + (unary) + case hash('a','d'): // & (unary) + case hash('d','e'): // * (unary) + return DemangleUnaryPrefixExpression(GetOperator(elm1, elm2)); + case hash('i','x'): // [] + case hash('p','p'): // ++ (postfix in context) + case hash('m','m'): // -- (postfix in context) + return DemangleUnarySuffixExpression(GetOperator(elm1, elm2)); + case hash('l','s'): // << + case hash('r','s'): // >> + case hash('a','S'): // = + case hash('e','q'): // == + case hash('n','e'): // != + case hash('d','t'): // . + case hash('p','t'): // -> + case hash('m','l'): // * + case hash('m','i'): // - + case hash('p','l'): // + + case hash('a','n'): // & + case hash('p','m'): // ->* + case hash('d','v'): // / + case hash('r','m'): // % + case hash('l','t'): // < + case hash('l','e'): // <= + case hash('g','t'): // > + case hash('g','e'): // >= + case hash('c','m'): // , + case hash('c','o'): // ~ + case hash('e','o'): // ^ + case hash('o','r'): // | + case hash('a','a'): // && + case hash('o','o'): // || + case hash('m','L'): // *= + case hash('p','L'): // += + case hash('m','I'): // -= + case hash('d','V'): // /= + case hash('r','M'): // %= + case hash('r','S'): // >>= + case hash('l','S'): // <<= + case hash('a','N'): // &= + case hash('o','R'): // |= + case hash('e','O'): // ^= + return DemangleBinaryExpression(GetOperator(elm1, elm2)); + case hash('d','l'): // delete + case hash('d','a'): // delete[] + case hash('n','w'): // new + case hash('n','a'): // new [] + return gs + DemangleUnaryPrefixType(GetOperator(elm1, elm2)); + case hash('q','u'): // ternary + return DemangleExpression() + "?" + + DemangleExpression() + ":" + + DemangleExpression(); + case hash('c','l'): // () + return "(" + DemangleExpressionList() + ")"; + case hash('c','v'): //type (expression) + { + TypeBuilder type = DemangleType(); + out = type.GetString(); + if (m_reader.Peek() == '_') + out += " (" + DemangleExpressionList() + ")"; + else + out += " (" + DemangleExpression() + ")"; + return out; + } + case hash('t','l'): //type {expression} + return DemangleTypeString() + " {" + DemangleExpressionList() + "}"; + case hash('i', 'l'): //{expr-list}, braced-init-list in any other context + out = DemangleExpression(); + if (m_reader.Read() != 'E') + throw DemangleException(); + return out; + case hash('f','p'): + case hash('f','L'): + { + // ::= fp _ # L == 0, first parameter + // ::= fp _ # L == 0, second and later parameters + // ::= fL p _ # L > 0, first parameter + // ::= fL p _ # L > 0, second and later parameters + + bool cnst = false, vltl = false, rstrct = false; + TypeBuilder type; + int64_t listNumber = 0; + int64_t elementNum = 0; + char elm; + if (m_functionSubstitute.size() == 0) + throw DemangleException(); + + if (elm2 == 'L') + { + listNumber = DemangleNumber() + 1; + if (listNumber < 0 || + (uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || + m_reader.Read() != 'p') + throw DemangleException(); + } + DemangleCVQualifiers(cnst, vltl, rstrct); + elm = m_reader.Peek(); + if (elm == '_') + { + m_reader.Consume(1); + if ((size_t)elementNum >= m_functionSubstitute[listNumber].size()) + { + throw DemangleException(); + } + type = m_functionSubstitute[listNumber][elementNum]; + } + else if (isdigit(elm) || isupper(elm)) + { + elementNum = DemangleNumber() + 1; + if (m_reader.Read() != '_' || + elementNum < 0 || + (size_t)elementNum >= m_functionSubstitute[listNumber].size()) + { + throw DemangleException(); + } + type = m_functionSubstitute[listNumber][elementNum]; + } + else + { + throw DemangleException(); + } + out = type.GetString(); + break; + } + case hash('s','r'): + /* + ::= + ::= # T::x / decltype(p)::x + ::= N + E + # T::N::x /decltype(p)::N::x + ::= + E + # A::x, N::y, A::z; "gs" means leading "::" + + ::= [ ] # T:: or T:: + ::= # decltype(p):: + ::= + + ::= + ::= # unresolved name + ::= on # unresolved operator-function-id + ::= on # unresolved operator template-id + ::= dn # destructor or pseudo-destructor; + # e.g. ~X or ~X + */ + if (m_reader.Peek() == 'N') + { + m_reader.Consume(); + out += DemangleUnresolvedType().GetString() + "::"; + do + { + out += DemangleSourceName(); + PushType(TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + UnknownNamedTypeClass, out))); + if (m_reader.Peek() == 'I') + { + vector args; + m_reader.Consume(); + // + DemangleTemplateArgs(args); + out += GetTemplateString(args); + } + out += "::"; + }while (m_reader.Peek() != 'E'); + m_reader.Consume(); + + out += DemangleBaseUnresolvedName().GetString(); + return out; + } + if (isdigit(m_reader.Peek())) + { + do + { + out += DemangleSourceName(); + if (m_reader.Peek() == 'I') + { + vector args; + m_reader.Consume(); + // + DemangleTemplateArgs(args); + out += GetTemplateString(args); + } + out += "::"; + }while (m_reader.Peek() != 'E'); + m_reader.Consume(); + out += DemangleBaseUnresolvedName().GetString(); + return out; + } + else + { + out += DemangleUnresolvedType().GetString() + "::"; + out += DemangleBaseUnresolvedName().GetString(); + } + return out; + default: + m_reader.UnRead(2); + out = DemangleSourceName(); + if (m_reader.Peek() == 'I') + { + vector args; + m_reader.Consume(); + // + DemangleTemplateArgs(args); + out += GetTemplateString(args); + } + break; + } + return out; +} + + +void DemangleGNU3::DemangleTemplateArgs(vector& args) +{ + indent(); + MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + TypeBuilder tmp; + bool tmpValid = false; + string expr; + bool topLevel; + const string lastName = m_lastName; + while (m_reader.Peek() != 'E') + { + switch (m_reader.Read()) + { + case 'L': + expr = DemanglePrimaryExpression(); + args.push_back({expr, nullptr, true, Variable()}); + tmp = CreateUnknownType(expr); + tmpValid = true; + break; + case 'X': + args.push_back({DemangleExpression(), nullptr, true, Variable()}); + if (m_reader.Read() != 'E') + throw DemangleException(); + break; + case 'J': + DemangleTemplateArgs(args); + break; + default: + m_reader.UnRead(); + topLevel = m_topLevel; + m_topLevel = false; + tmp = DemangleType(); + m_topLevel = topLevel; + args.push_back({tmp.GetString(), nullptr, true, Variable()}); + tmpValid = true; + } + if (m_topLevel && tmpValid) + { + MyLogDebug("Adding template ref: %s\n", tmp.GetString().c_str()); + PushTemplateType(tmp); + } + } + m_reader.Consume(); + m_lastName = lastName; + dedent(); + return; +} + + +TypeBuilder DemangleGNU3::DemangleNestedName() +{ + /* + 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 + a name or a type. + + ::= N [] [] E + ::= N [] [] E + + ::= # global class or namespace + ::= # nested class or namespace + ::= # class template specialization + ::= # template type parameter + ::= # decltype qualifier + ::= # initializer of a data member + ::= + + ::=