diff options
| author | Glenn Smith <glenn@vector35.com> | 2024-09-23 17:15:17 -0400 |
|---|---|---|
| committer | Glenn Smith <glenn@vector35.com> | 2024-10-17 17:33:52 -0400 |
| commit | 8149774201203326096099394434871992996628 (patch) | |
| tree | d968976df167dc74090f61051c41ca30e5cc7c8e | |
| parent | 5353484086517832418955474757290903351986 (diff) | |
Demangler plugin API
Closes #467
| -rw-r--r-- | binaryninjaapi.h | 115 | ||||
| -rw-r--r-- | binaryninjacore.h | 45 | ||||
| -rw-r--r-- | demangle.cpp | 135 | ||||
| -rw-r--r-- | demangler/gnu3/CMakeLists.txt | 40 | ||||
| -rw-r--r-- | demangler/gnu3/demangle_gnu3.cpp | 2373 | ||||
| -rw-r--r-- | demangler/gnu3/demangle_gnu3.h | 122 | ||||
| -rw-r--r-- | demangler/msvc/CMakeLists.txt | 40 | ||||
| -rw-r--r-- | demangler/msvc/demangle_msvc.cpp | 1887 | ||||
| -rw-r--r-- | demangler/msvc/demangle_msvc.h | 162 | ||||
| -rw-r--r-- | python/demangle.py | 255 | ||||
| -rw-r--r-- | rust/Cargo.lock | 8 | ||||
| -rw-r--r-- | rust/Cargo.toml | 3 | ||||
| -rw-r--r-- | rust/examples/test_demangler/Cargo.toml | 17 | ||||
| -rw-r--r-- | rust/examples/test_demangler/src/lib.rs | 64 | ||||
| -rw-r--r-- | rust/src/demangle.rs | 258 | ||||
| -rw-r--r-- | view/elf/elfview.cpp | 18 | ||||
| -rw-r--r-- | view/macho/machoview.cpp | 35 | ||||
| -rw-r--r-- | view/pe/coffview.cpp | 17 | ||||
| -rw-r--r-- | view/pe/peview.cpp | 49 |
19 files changed, 5547 insertions, 96 deletions
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<BinaryView> Load(Ref<BinaryView> rawData, bool updateAnalysis, std::function<bool(size_t, size_t)> progress, Ref<Metadata> 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<Architecture> arch, const std::string& mangledName, Ref<Type>& outType, QualifiedName& outVarName, + Ref<BinaryView> 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<BNDemangler> + { + 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<Ref<Demangler>> GetList(); + static Ref<Demangler> GetByName(const std::string& name); + + /*! Promote a demangler to the highest-priority position. + + \param demangler Demangler to promote + */ + static void Promote(Ref<Demangler> 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<Architecture> arch, const std::string& name, Ref<Type>& outType, + QualifiedName& outVarName, Ref<BinaryView> 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<Architecture> arch, const std::string& name, Ref<Type>& outType, + QualifiedName& outVarName, Ref<BinaryView> 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 <string> using namespace std; +using namespace BinaryNinja; namespace BinaryNinja { + bool DemangleGeneric(Ref<Architecture> arch, const std::string& name, Ref<Type>& outType, + QualifiedName& outVarName, Ref<BinaryView> 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<Architecture> apiArch = new CoreArchitecture(arch); + Ref<BinaryView> apiView = view ? new BinaryView(BNNewViewReference(view)) : nullptr; + + Ref<Type> 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<Ref<Demangler>> Demangler::GetList() + { + size_t count; + BNDemangler** list = BNGetDemanglerList(&count); + vector<Ref<Demangler>> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreDemangler(list[i])); + BNFreeDemanglerList(list); + return result; + } + + Ref<Demangler> 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> 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<Architecture> arch, const std::string& name, Ref<Type>& outType, + QualifiedName& outVarName, Ref<BinaryView> 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 <stdarg.h> +#include <algorithm> +#include <memory> +#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<FunctionParameter> 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; + //[<cv-qualifier>] + 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<FunctionParameter> 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<FunctionParameter> 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<FunctionParameter> args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + substitute = true; + } + } + break; + } + case 'T': + { + /* <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier + ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class' + ::= Tu <name> # dependent elaborated type specifier using 'union' + ::= Te <name> # 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<FunctionParameter> 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<string>{"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': + // <array-type> ::= A <positive dimension number> _ <element type> + // ::= A [<dimension expression>] _ <element type> + if (isdigit(m_reader.Peek())) + { + //<positive dimension number> _ <element type> + uint64_t size = DemangleNumber(); + if (m_reader.Read() != '_') + throw DemangleException(); + type = TypeBuilder::ArrayType(DemangleType().Finalize(), size); + } + else + { + //[<dimension expression>] _ <element type> + //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<FunctionParameter> 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<string>{"std", "allocator"}); + static const QualifiedName stdBasicStringName(vector<string>{"std", "basic_string"}); + static const QualifiedName stdIostreamName(vector<string>{"std", "iostream"}); + static const QualifiedName stdIstreamName(vector<string>{"std", "istream"}); + static const QualifiedName stdOstreamName(vector<string>{"std", "ostream"}); + static const QualifiedName stdStringName(vector<string>{"std", "string"}); + static const QualifiedName stdName(vector<string>{"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] <decimal> +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 <expression> context) + case hash('m','m'): return "--"; // -- (postfix in <expression> 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 <expression> context) + case hash('m','m'): return OperatorDecrementNameType; // -- (postfix in <expression> 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 <expression> context) + case hash('m','m'): // -- (postfix in <expression> 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<TypeBuilder> 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() +{ + // <base-unresolved-name> ::= <simple-id> # unresolved name + // ::= on <operator-name> # unresolved operator-function-id + // ::= on <operator-name> <template-args> # unresolved operator template-id + // ::= dn <destructor-name> # destructor or pseudo-destructor; + // # e.g. ~X or ~X<N-1> + + 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<FunctionParameter> 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 + { + // <simple-id> + out.push_back(DemangleSourceName()); + if (m_reader.Peek() == 'I') + { + m_reader.Consume(); + vector<FunctionParameter> args; + DemangleTemplateArgs(args); + out.back() += GetTemplateString(args); + } + } + } + dedent(); + return out; +} + + +TypeBuilder DemangleGNU3::DemangleUnresolvedType() +{ + indent(); + MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + //<unresolved-type> ::= <template-param> [ <template-args> ] # T:: or T<X,Y>:: + // ::= <decltype> # decltype(p):: + // ::= <substitution> + TypeBuilder type; + if (m_reader.Peek() == 'T') + { + m_reader.Consume(); + type = DemangleTemplateSubstitution(); + if (m_reader.Peek() == 'I') + { + PushType(type); + m_reader.Consume(); + vector<FunctionParameter> 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()); + /* + <expression> ::= <unary operator-name> <expression> + ::= <binary operator-name> <expression> <expression> + ::= <ternary operator-name> <expression> <expression> <expression> + ::= pp_ <expression> # prefix ++ + ::= mm_ <expression> # prefix -- + ::= cl <expression>+ E # expression (expr-list), call + ::= cv <type> <expression> # type (expression), conversion with one argument + ::= cv <type> _ <expression>* E # type (expr-list), conversion with other than one argument + ::= tl <type> <expression>* E # type {expr-list}, conversion with braced-init-list argument + ::= il <expression> E # {expr-list}, braced-init-list in any other context + ::= [gs] nw <expression>* _ <type> E # new (expr-list) type + ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) + ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type + ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) + ::= [gs] dl <expression> # delete expression + ::= [gs] da <expression> # delete[] expression + ::= dc <type> <expression> # dynamic_cast<type> (expression) + ::= sc <type> <expression> # static_cast<type> (expression) + ::= cc <type> <expression> # const_cast<type> (expression) + ::= rc <type> <expression> # reinterpret_cast<type> (expression) + ::= ti <type> # typeid (type) + ::= te <expression> # typeid (expression) + ::= st <type> # sizeof (type) + ::= sz <expression> # sizeof (expression) + ::= at <type> # alignof (type) + ::= az <expression> # alignof (expression) + ::= nx <expression> # noexcept (expression) + ::= <template-param> + ::= <function-param> + ::= dt <expression> <unresolved-name> # expr.name + ::= pt <expression> <unresolved-name> # expr->name + ::= ds <expression> <expression> # expr.*expr + ::= sZ <template-param> # sizeof...(T), size of a template parameter pack + ::= sZ <function-param> # sizeof...(parameter), size of a function parameter pack + ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template + ::= sp <expression> # expression..., pack expansion + ::= tw <expression> # throw expression + ::= tr # throw with no operand (rethrow) + ::= <unresolved-name> # f(p), N::f(p), ::f(p), + # freestanding dependent name (e.g., T::x), + # objectless nonstatic member reference + ::= <expr-primary> + */ + char elm1 = '\0', elm2 = '\0'; + string gs, out; + elm1 = m_reader.Read(); + if (elm1 == 'L') + { + out = DemanglePrimaryExpression(); + return out; + } + else if (elm1 == 'T') //<template-param> + { + 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<FunctionParameter> 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 <expression> context) + case hash('m','m'): // -- (postfix in <expression> 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'): + { + //<function-param> ::= fp <CV> _ # L == 0, first parameter + // ::= fp <CV> <prm-2 num> _ # L == 0, second and later parameters + // ::= fL <L-1 num> p <CV> _ # L > 0, first parameter + // ::= fL <L-1 num> p <CV> <prm-2 num> _ # 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'): + /* + <unresolved-name> ::= + ::= <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x + ::= N <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> + # T::N::x /decltype(p)::N::x + ::= <unresolved-qualifier-level>+ E <base-unresolved-name> + # A::x, N::y, A<T>::z; "gs" means leading "::" + + <unresolved-type> ::= <template-param> [ <template-args> ] # T:: or T<X,Y>:: + ::= <decltype> # decltype(p):: + ::= <substitution> + + <unresolved-qualifier-level> ::= <simple-id> + <base-unresolved-name> ::= <simple-id> # unresolved name + ::= on <operator-name> # unresolved operator-function-id + ::= on <operator-name> <template-args> # unresolved operator template-id + ::= dn <destructor-name> # destructor or pseudo-destructor; + # e.g. ~X or ~X<N-1> + */ + 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<FunctionParameter> args; + m_reader.Consume(); + //<tmplate-args> + 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<FunctionParameter> args; + m_reader.Consume(); + //<tmplate-args> + 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<FunctionParameter> args; + m_reader.Consume(); + //<tmplate-args> + DemangleTemplateArgs(args); + out += GetTemplateString(args); + } + break; + } + return out; +} + + +void DemangleGNU3::DemangleTemplateArgs(vector<FunctionParameter>& 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. + + <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E + ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E + + <prefix> ::= <unqualified-name> # global class or namespace + ::= <prefix> <unqualified-name> # nested class or namespace + ::= <template-prefix> <template-args> # class template specialization + ::= <template-param> # template type parameter + ::= <decltype> # decltype qualifier + ::= <prefix> <data-member-prefix> # initializer of a data member + ::= <substitution> + + <template-prefix> ::= <template unqualified-name> # global template + ::= <prefix> <template unqualified-name> # nested template + ::= <template-param> # template template parameter + ::= <substitution> + + <unqualified-name> ::= <operator-name> + ::= <ctor-dtor-name> + ::= <source-name> + ::= <unnamed-type-name> + + <source-name> ::= <positive length number> <identifier> + <identifier> ::= <unqualified source code identifier> + */ + + indent(); + MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + TypeBuilder type = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + UnknownNamedTypeClass, QualifiedName())); + bool cnst = false, vltl = false, rstrct = false; + bool ref = false; + bool rvalueRef = false; + bool substitute = true; + TypeBuilder newType; + bool base = false; + bool isTemplate = false; + bool hasB = false; + //[<CV-qualifiers>] + DemangleCVQualifiers(cnst, vltl, rstrct); + + //[<ref-qualifier>] + if (m_reader.Peek() == 'R') + { + m_reader.Consume(); + ref = true; + } + else if (m_reader.Peek() == 'O') + { + m_reader.Consume(); + ref = true; + rvalueRef = true; + } + + while (m_reader.Peek() != 'E') + { + if (m_reader.Peek() == 'B') + { + hasB = true; + break; + } + isTemplate = false; + substitute = true; + size_t startSize = m_templateSubstitute.size(); + switch (m_reader.Read()) + { + case 'S': //<substitution> + newType = DemangleSubstitution(); + substitute = false; + break; + case 'T': //<template-param> + newType = DemangleTemplateSubstitution(); + break; + case 'I': //<template-prefix> <template-args> + { + if (!base) + throw DemangleException(); + vector<FunctionParameter> args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + isTemplate = true; + break; + } + default: //<unqualified-name> || <decltype> + m_reader.UnRead(1); + newType = DemangleUnqualifiedName(); + break; + } + + base = true; + if (!isTemplate) + { + type.SetNameType(newType.GetNameType()); + QualifiedName newName = type.GetTypeName() + newType.GetTypeName(); + if (newName.StringSize() > MAX_DEMANGLE_LENGTH) + throw DemangleException("Detected adversarial mangled string"); + type.SetTypeName(newName); + type.SetHasTemplateArguments(false); + } + if (substitute && m_reader.Peek() != 'E') + { + //Those template arguments were not the primary arguments so clear them from the sub listType + while (m_templateSubstitute.size() > startSize) + { + m_templateSubstitute.pop_back(); + } + PushType(type); + } + MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + } + if (!hasB) + m_reader.Consume(); + + if (cnst || vltl || rstrct) + { + type.SetConst(cnst); + type.SetVolatile(vltl); + if (rstrct) + type.AddPointerSuffix(RestrictSuffix); + } + + if (ref) + { + type.AddPointerSuffix(rvalueRef?LvalueSuffix:ReferenceSuffix); + PushType(type); + } + dedent(); + return type; +} + + +TypeBuilder DemangleGNU3::DemangleLocalName() +{ + indent(); + MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + TypeBuilder type; + QualifiedName varName; + bool oldTopLevel = m_topLevel; + m_topLevel = false; + type = DemangleSymbol(varName); + m_topLevel = oldTopLevel; + + if (varName.size() > 0) + varName.back() += (type.GetStringAfterName()); + else + varName.push_back(type.GetString()); + + if (m_reader.Peek() != 's') + { + //<entity name> + TypeBuilder tmpType = DemangleName(); + type = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, varName)); + QualifiedName newName = type.GetTypeName() + tmpType.GetTypeName(); + if (newName.StringSize() > MAX_DEMANGLE_LENGTH) + throw DemangleException("Detected adversarial mangled string"); + type.SetTypeName(newName); + type.SetConst(tmpType.IsConst()); + type.SetVolatile(tmpType.IsVolatile()); + type.SetPointerSuffix(tmpType.GetPointerSuffix()); + } + else + { + m_reader.Consume(); + type = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, varName)); + } + // [<discriminator>] + //TODO: What do we do with discriminators? + if (m_reader.Peek() == '_') + { + m_reader.Consume(); + if (m_reader.Peek() == '_') + { + m_reader.Consume(); + DemangleNumberAsString(); + if (m_reader.Read() != '_') + throw DemangleException(); + } + else + { + DemangleNumberAsString(); + } + } + dedent(); + return type; +} + + +TypeBuilder DemangleGNU3::DemangleName() +{ + indent(); + MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + /* + <name> ::= <nested-name> + ::= <unscoped-name> + ::= <unscoped-template-name> <template-args> + ::= <local-name> # See Scope Encoding below + + <unscoped-name> ::= <unqualified-name> + ::= St <unqualified-name> # ::std:: + + <unscoped-template-name> ::= <unscoped-name> + ::= <substitution> + */ + TypeBuilder type; + bool substitute = false; + switch (m_reader.Read()) + { + case 'S': + 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<FunctionParameter> args; + DemangleTemplateArgs(args); + ExtendTypeName(type, GetTemplateString(args)); + type.SetHasTemplateArguments(true); + } + break; + case 'N': //<nested-name> + type = DemangleNestedName(); + break; + case 'Z': //<local-name> + type = DemangleLocalName(); + break; + default: //<unscoped-name> | <substitution> + /* + <unscoped-name> ::= <unqualified-name> + ::= St <unqualified-name> # ::std:: + <unscoped-template-name> ::= <unscoped-name> + ::= <substitution> + */ + m_reader.UnRead(); + if (m_reader.Peek() == 'L') + m_reader.Consume(); + type = DemangleUnqualifiedName(); + if (m_reader.Peek() == 'I') + { + PushType(type); + //<unscoped-template-name> + vector<FunctionParameter> args; + m_reader.Consume(); + //<tmplate-args> + DemangleTemplateArgs(args); + LogDebug("Typename: %s", type.GetTypeName()[0].c_str()); + ExtendTypeName(type, GetTemplateString(args)); + LogDebug("Typename: %s", type.GetTypeName()[0].c_str()); + type.SetHasTemplateArguments(true); + } + } + dedent(); + return type; +} + + +TypeBuilder DemangleGNU3::DemangleSymbol(QualifiedName& varName) +{ + indent(); + MyLogDebug("%s: %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + TypeBuilder returnType; + bool isReturnTypeUnknown = false; + TypeBuilder type; + vector<FunctionParameter> params; + bool cnst = false, vltl = false, rstrct = false; + bool oldTopLevel; + QualifiedName name; + + /* + <encoding> ::= <function name> <bare-function-type> + ::= <data name> + ::= <special-name> + */ + //<special-name> + switch (m_reader.Peek()) + { + case 'G': + m_reader.Consume(); + switch (m_reader.Read()) + { + case 'A': //TODO hidden alias + LogWarn("Unsupported demangle type: hidden alias\n"); + throw DemangleException(); + case 'R': //TODO reference temporaries + LogWarn("Unsupported demangle type: reference temporary\n"); + throw DemangleException(); + case 'T': //TODO transaction clones + LogWarn("Unsupported demangle type: transaction clone\n"); + throw DemangleException(); + case 'V': + { + TypeBuilder t = DemangleSymbol(name); + varName.push_back("guard_variable_for_" + t.GetTypeAndName(name)); + type = TypeBuilder::IntegerType(1, false); + if (m_reader.Length() == 0) + return type; + //function parameters + string paramList; + paramList += "("; + bool first = true; + do + { + if (m_reader.Peek() == 'v') + { + m_reader.Consume(); + break; + } + if (!first) + paramList += ", "; + paramList += DemangleTypeString(); + }while (m_reader.Peek() != 'E'); + m_reader.Consume(); + varName.back() += paramList + ")"; + varName.push_back(DemangleSourceName()); + return type; + } + default: + throw DemangleException(); + } + case 'T': + /* + <special-name> ::= TV <type> # virtual table + ::= TT <type> # VTT structure (construction vtable index) + ::= TI <type> # typeinfo structure + ::= TS <type> # typeinfo name (null-terminated byte string) + ::= T <call-offset> <base encoding> + # base is the nominal target function of thunk + <call-offset> ::= h <nv-offset> _ + ::= v <v-offset> _ + <nv-offset> ::= <offset number> # non-virtual base override + <v-offset> ::= <offset number> _ <virtual offset number> + # virtual base override, with vcall offset + */ + m_reader.Consume(); + switch (m_reader.Read()) + { + case 'c': + LogWarn("Unsupported: 'virtual function covariant override thunk'\n"); + throw DemangleException(); + case 'C': + { + TypeBuilder t = DemangleType(); + DemangleNumberAsString(); + if (m_reader.Read() != '_') + throw DemangleException(); + + return TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, + {"construction_vtable_for_" + DemangleTypeString() + "-in-" + t.GetString()})); + } + case 'D': + LogWarn("Unsupported: 'typeinfo common proxy'\n"); + throw DemangleException(); + case 'F': + LogWarn("Unsupported: 'typeinfo fn'\n"); + throw DemangleException(); + case 'h': //TODO: Convert to whatever the actual type is! + { + DemangleNumberAsString(); + if (m_reader.Read() != '_') + throw DemangleException(); + oldTopLevel = m_topLevel; + m_topLevel = false; + TypeBuilder t = DemangleSymbol(name); + m_topLevel = oldTopLevel; + return TypeBuilder::NamedType( + NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, + {"non-virtual_thunk_to_" + name.GetString() + t.GetStringAfterName()})); + } + case 'H': + LogWarn("Unsupported: 'TLS init function'\n"); + throw DemangleException(); + case 'I': + return TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(StructNamedTypeClass, + {"typeinfo_for_" + DemangleTypeString()})); + case 'J': + LogWarn("Unsupported: 'java class'\n"); + throw DemangleException(); + case 'S': + { + TypeBuilder t = DemangleType(); + varName = vector<string>{"typeinfo_name_for_" + t.GetString()}; + return TypeBuilder::ArrayType(Type::IntegerType(1, true), 0); + } + case 'T': //VTT + { + TypeBuilder t = DemangleType(); + return TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(StructNamedTypeClass, + {"VTT_for_" + t.GetString()})); + } + case 'v': //TODO: Convert to whatever the actual type is! + { + DemangleNumberAsString(); + if (m_reader.Read() != '_') + throw DemangleException(); + DemangleNumberAsString(); + if (m_reader.Read() != '_') + throw DemangleException(); + oldTopLevel = m_topLevel; + m_topLevel = false; + TypeBuilder t = DemangleSymbol(name); + m_topLevel = oldTopLevel; + return TypeBuilder::NamedType( + NamedTypeReference::GenerateAutoDemangledTypeReference(UnknownNamedTypeClass, + {"virtual_thunk_to_" + name.GetString() + t.GetStringAfterName()})); + } + case 'V': //Vtable + return TypeBuilder::NamedType( + NamedTypeReference::GenerateAutoDemangledTypeReference(StructNamedTypeClass, + {"vtable_for_" + DemangleTypeString()})); + case 'W': + MyLogDebug("Unsupported: 'TLS wrapper function'\n"); + throw DemangleException(); + default: + throw DemangleException(); + } + default: break; + } + + //<function name> or <data name> + type = DemangleName(); + if (m_reader.Length() == 0) + { + return type; + } + + if (m_reader.Peek() == 'E') + { + m_reader.Consume(); + return type; + } + + varName = type.GetTypeName(); + cnst = type.IsConst(); + vltl = type.IsVolatile(); + set<BNPointerSuffix> suffix = type.GetPointerSuffix(); + if (m_reader.Peek() == 'J') + { + m_reader.Consume(); + // TODO: If we get here we have a return type. What can we do with this info? + } + if (m_reader.Peek() == 'B') + { + m_reader.Consume(); + TypeBuilder t = DemangleUnqualifiedName(); + + if (t.GetString() == "cxx11") + { + static const QualifiedName stdCxx11StringName(vector<string>{"std", "cxx11", "string"}); + returnType = CreateUnknownType(stdCxx11StringName); + } + } + else if (m_isOperatorOverload || + type.GetNameType() == ConstructorNameType || + type.GetNameType() == DestructorNameType) + { + returnType = TypeBuilder::VoidType(); + } + else if (m_isParameter || type.HasTemplateArguments()) + { + returnType = DemangleType(); + } + else + { + isReturnTypeUnknown = true; + returnType = TypeBuilder::IntegerType(m_arch->GetAddressSize(), true); + } + + m_functionSubstitute.push_back({}); + for (size_t i = 0; m_reader.Length() > 0; i++) + { + if (m_reader.Peek() == 'E') + { + m_reader.Consume(); + break; + } + if (m_reader.Peek() == '.') + { + // Extension, consume the rest + string ext = m_reader.ReadString(m_reader.Length()); + + if (ext == ".eh") ext = "exception handler"; + else if (ext == ".eh_frame") ext = "exception handler frame"; + else if (ext == ".eh_frame_hdr") ext = "exception handler frame header"; + else if (ext == ".debug_frame") ext = "debug frame"; + varName.back() += ext; + break; + } + + m_isParameter = true; + MyLogDebug("Var_%d: %s\n", i, m_reader.GetRaw().c_str()); + if (m_reader.PeekString(2) == "@@") + break; + TypeBuilder param = DemangleType(); + if (param.GetClass() == VoidTypeClass) + { + if (m_reader.Peek() == 'E') + { + m_reader.Consume(); + break; + } + break; + } + m_functionSubstitute.back().push_back(param); + params.push_back({"", param.Finalize(), true, Variable()}); + if (param.GetClass() == VarArgsTypeClass) + { + if (m_reader.Peek() == 'E') + { + m_reader.Consume(); + } + + break; + } + } + + m_functionSubstitute.pop_back(); + m_isParameter = false; + type = TypeBuilder::FunctionType(returnType.Finalize()-> + WithConfidence(isReturnTypeUnknown ? BN_MINIMUM_CONFIDENCE : BN_DEFAULT_CONFIDENCE), nullptr, params); + + type.SetPointerSuffix(suffix); + type.SetConst(cnst); + type.SetVolatile(vltl); + if (rstrct) + type.SetPointerSuffix({RestrictSuffix}); + + // PrintTables(); + MyLogDebug("Done: %s%s%s\n", type.GetStringBeforeName().c_str(), varName.GetString().c_str(), + type.GetStringAfterName().c_str()); + + dedent(); + return type; +} + + +bool DemangleGNU3::IsGNU3MangledString(const string& name) +{ + string headerless = name; + string header; + if (DemangleGlobalHeader(headerless, header)) + return true; + + if (!headerless.compare(0, 2, "_Z") || !headerless.compare(0, 3, "__Z")) + return true; + + return false; +} + + +bool DemangleGNU3::DemangleGlobalHeader(string& name, string& header) +{ + size_t strippedCount = 0; + string encoded = name; + while (encoded[0] == '_') + { + encoded.erase(0, 1); + strippedCount ++; + } + + if (strippedCount == 0) + return false; + + static const vector<pair<string, string>> headers = { + {"GLOBAL__sub_I_", "(static initializer)"}, + {"GLOBAL__I_", "(global initializer)"}, + {"GLOBAL__D_", "(global destructor)"}, + }; + + for (auto& i: headers) + { + if (encoded.size() > i.first.size() && encoded.substr(0, i.first.size()) == i.first) + { + name = name.substr(i.first.size() + strippedCount); + header = i.second; + return true; + } + } + + return false; +} + + +bool DemangleGNU3::DemangleStringGNU3(Architecture* arch, const string& name, Ref<Type>& outType, QualifiedName& outVarName, const Ref<BinaryView>& view) +{ + return DemangleStringGNU3(arch, name, outType, outVarName); +} + + +bool DemangleGNU3::DemangleStringGNU3(Architecture* arch, const string& name, Ref<Type>& outType, QualifiedName& outVarName, BinaryView* view) +{ + return DemangleStringGNU3(arch, name, outType, outVarName); +} + + +bool DemangleGNU3::DemangleStringGNU3(Architecture* arch, const string& name, Ref<Type>& outType, QualifiedName& outVarName) +{ + string encoding = name; + string header; + bool foundHeader = DemangleGlobalHeader(encoding, header); + + if (!encoding.compare(0, 2, "_Z")) + encoding = encoding.substr(2); + else if (!encoding.compare(0, 3, "__Z")) + encoding = encoding.substr(3); + else if (foundHeader && !header.empty()) + { + // Some variable constructors/destructors are __GLOBAL__I_name + // And there are even __GLOBAL__sub_I_file_name.cpp + outVarName.clear(); + outVarName.push_back(header); + outVarName.push_back(encoding); + outType = CreateUnknownType(outVarName).Finalize(); + return true; + } + else + return false; + + DemangleGNU3 demangle(arch, encoding); + try + { + outType = demangle.DemangleSymbol(outVarName).Finalize(); + + if (outVarName.size() == 0) + { + if (outType->GetClass() == NamedTypeReferenceClass && outType->GetNamedTypeReference()->GetTypeReferenceClass() == UnknownNamedTypeClass) + { + outVarName = outType->GetTypeName(); + outType = nullptr; + } + else if (outType->GetClass() == NamedTypeReferenceClass) + { + auto typeName = outType->GetTypeName(); + if (typeName.size() > 0) + outVarName = "_" + typeName[typeName.size() - 1]; + } + } + + if (foundHeader && !header.empty()) + { + outVarName.insert(outVarName.begin(), header); + } + } + catch (std::exception&) + { + return false; + } + return true; +} + + +class GNU3Demangler: public Demangler +{ +public: + GNU3Demangler(): Demangler("GNU3") + { + } + ~GNU3Demangler() override {} + + virtual bool IsMangledString(const string& name) override + { + return DemangleGNU3::IsGNU3MangledString(name); + } + + virtual bool Demangle(Ref<Architecture> arch, const string& name, Ref<Type>& outType, QualifiedName& outVarName, + Ref<BinaryView> view) override + { + if (view) + return DemangleGNU3::DemangleStringGNU3(arch, name, outType, outVarName, view); + return DemangleGNU3::DemangleStringGNU3(arch, name, outType, outVarName); + } +}; + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_EDITION + bool DemangleGNU3PluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + static GNU3Demangler* demangler = new GNU3Demangler(); + Demangler::Register(demangler); + return true; + } +} diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h new file mode 100644 index 00000000..ad1303e6 --- /dev/null +++ b/demangler/gnu3/demangle_gnu3.h @@ -0,0 +1,122 @@ +// 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. + +#pragma once +#include <stdexcept> +#include <exception> +#include "binaryninjaapi.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(); } +}; + +class DemangleGNU3 +{ + class Reader + { + public: + Reader(const std::string& data); + std::string PeekString(size_t count=1); + char Peek(); + bool NextIsOneOf(const std::string& list); + std::string GetRaw(); + char Read(); + std::string ReadString(size_t count=1); + std::string ReadUntil(char sentinal); + void Consume(size_t count=1); + size_t Length() const; + void UnRead(size_t count=1); + private: + std::string m_data; + size_t m_offset; + }; + + class SubstitutionList + { + std::vector<BinaryNinja::TypeBuilder> m_typeList; + public: + SubstitutionList(); + ~SubstitutionList(); + void PushType(BinaryNinja::TypeBuilder t); + void PopType(); + const BinaryNinja::TypeBuilder& GetType(size_t reference) const; + void PrintSubstitutionTable() const; + size_t Size() const { return m_typeList.size(); } + void Clear() { m_typeList.clear(); } + }; + + BinaryNinja::QualifiedName m_varName; + Reader m_reader; + BinaryNinja::Architecture* m_arch; + std::vector<BinaryNinja::TypeBuilder> m_substitute; + std::vector<BinaryNinja::TypeBuilder> m_templateSubstitute; + std::vector<std::vector<BinaryNinja::TypeBuilder>> m_functionSubstitute; + 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; + enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name}; + BinaryNinja::QualifiedName DemangleBaseUnresolvedName(); + BinaryNinja::TypeBuilder DemangleUnresolvedType(); + std::string DemangleUnarySuffixExpression(const std::string& op); + std::string DemangleUnaryPrefixExpression(const std::string& op); + std::string DemangleBinaryExpression(const std::string& op); + std::string DemangleUnaryPrefixType(const std::string& op); + std::string DemangleTypeString(); + std::string DemangleExpressionList(); + BinaryNinja::TypeBuilder DemangleUnqualifiedName(); + std::string DemangleSourceName(); + std::string DemangleNumberAsString(); + std::string DemangleInitializer(); + std::string DemangleExpression(); + std::string DemanglePrimaryExpression(); + BinaryNinja::TypeBuilder DemangleName(); + BinaryNinja::TypeBuilder DemangleLocalName(); + + void DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct); + BinaryNinja::TypeBuilder DemangleSubstitution(); + const BinaryNinja::TypeBuilder& DemangleTemplateSubstitution(); + void DemangleTemplateArgs(std::vector<BinaryNinja::FunctionParameter>& args); + bool DemangleEncoding(BinaryNinja::Type** type, BinaryNinja::QualifiedName& outName); + BinaryNinja::TypeBuilder DemangleFunction(bool cnst, bool vltl); + BinaryNinja::TypeBuilder DemangleType(); + int64_t DemangleNumber(); + BinaryNinja::TypeBuilder DemangleNestedName(); + void PushTemplateType(BinaryNinja::TypeBuilder type); + const BinaryNinja::TypeBuilder& GetTemplateType(size_t ref); + void PushType(BinaryNinja::TypeBuilder type); + const BinaryNinja::TypeBuilder& GetType(size_t ref); + static bool DemangleGlobalHeader(std::string& name, std::string& header); + +public: + DemangleGNU3(BinaryNinja::Architecture* arch, const std::string& mangledName); + BinaryNinja::TypeBuilder DemangleSymbol(BinaryNinja::QualifiedName& varName); + BinaryNinja::QualifiedName GetVarName() const { return m_varName; } + static bool IsGNU3MangledString(const std::string& name); + + // Tread lightly on this landmine; a BinaryView* will be converted to a bool; use an explicit (BinaryNinja::Ref<BinaryNinja::BinaryView>)view cast + static bool DemangleStringGNU3(BinaryNinja::Architecture* arch, const std::string& name, BinaryNinja::Ref<BinaryNinja::Type>& outType, BinaryNinja::QualifiedName& outVarName, const BinaryNinja::Ref<BinaryNinja::BinaryView>& view); + static bool DemangleStringGNU3(BinaryNinja::Architecture* arch, const std::string& name, BinaryNinja::Ref<BinaryNinja::Type>& outType, BinaryNinja::QualifiedName& outVarName, BinaryNinja::BinaryView* view); + static bool DemangleStringGNU3(BinaryNinja::Architecture* arch, const std::string& name, BinaryNinja::Ref<BinaryNinja::Type>& outType, BinaryNinja::QualifiedName& outVarName); + void PrintTables(); +}; diff --git a/demangler/msvc/CMakeLists.txt b/demangler/msvc/CMakeLists.txt new file mode 100644 index 00000000..222b4303 --- /dev/null +++ b/demangler/msvc/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(demangle_msvc) + +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/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp new file mode 100644 index 00000000..9571efe7 --- /dev/null +++ b/demangler/msvc/demangle_msvc.cpp @@ -0,0 +1,1887 @@ +// 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 "demangle_msvc.h" +#include <memory> + +using namespace BinaryNinja; +using namespace std; + +#define MAX_DEMANGLE_LENGTH 4096 + +Demangle::Reader::Reader(string data) +{ + m_data = data; + //Check for non-ascii characters + for (auto a : m_data) + { + if (a < 0x20 || a > 0x7e) + throw DemangleException(); + } +} + + +string Demangle::Reader::PeekString(size_t count) +{ + if (count > Length()) + throw DemangleException(); + return m_data.substr(0, count); +} + + +char Demangle::Reader::Peek() +{ + if (1 > Length()) + throw DemangleException(); + return (char)m_data[0]; +} + + +const char* Demangle::Reader::GetRaw() +{ + return m_data.c_str(); +} + + +char Demangle::Reader::Read() +{ + if (1 > Length()) + throw DemangleException(); + char out = m_data[0]; + m_data = m_data.substr(1); + return out; +} + + +string Demangle::Reader::ReadString(size_t count) +{ + if (count > Length()) + throw DemangleException(); + string out = m_data.substr(0, count); + m_data = m_data.substr(count + 1); + return out; +} + + +string Demangle::Reader::ReadUntil(char sentinal) +{ + size_t pos = m_data.find_first_of(sentinal); + if (pos == string::npos) + throw DemangleException(); + return ReadString(pos); +} + + +void Demangle::Reader::Consume(size_t count) +{ + if (count > Length()) + throw DemangleException(); + m_data = m_data.substr(count); +} + + +size_t Demangle::Reader::Length() +{ + return m_data.length(); +} + + +const TypeBuilder& Demangle::BackrefList::GetTypeBackref(size_t reference) +{ + if (reference < typeList.size()) + return typeList[reference]; + // LogDebug("type: %llx - : %d/%d\n", this, typeList.size(), reference); + throw DemangleException(string("Backref too large " + std::to_string(reference))); +} + + +string Demangle::BackrefList::GetStringBackref(size_t reference) +{ + // LogDebug("type: %llx - ref: %d\n", this, reference); + if (reference < nameList.size()) + return nameList[reference]; + LogDebug("type: %llx - Backref too large: %d/%d\n", this, nameList.size(), reference); + throw DemangleException(string("Backref too large " + std::to_string(reference))); +} + + +void Demangle::BackrefList::PushTypeBackref(TypeBuilder t) +{ + // LogDebug("this: %llx - TypeBackref: %lld %s\n", this, nameList.size(), t.GetString().c_str()); + if (typeList.size() <= 9) + typeList.push_back(t); +} + + +void Demangle::BackrefList::PushStringBackref(string& s) +{ + if (s.size() > MAX_DEMANGLE_LENGTH) + throw DemangleException(); + LogDebug("this: %llx - Backref: %lld - %s\n", this, nameList.size(), s.c_str()); + for (const auto& name : nameList) + if (name == s) + return; + nameList.push_back(s); +} + + +void Demangle::BackrefList::PushFrontStringBackref(string& s) +{ + if (s.size() > MAX_DEMANGLE_LENGTH) + throw DemangleException(); + // LogDebug("this: %llx - F-Backref: %lld - %s\n", this, nameList.size(), s.c_str()); + nameList.insert(nameList.begin(), s); +} + + +Demangle::Demangle(Architecture* arch, string mangledName) : + reader(mangledName), + m_arch(arch), + m_platform(nullptr), + m_view(nullptr) +{ + m_logger = LogRegistry::CreateLogger("MSVCDemangle"); + m_logger->ResetIndent(); +} + + +Demangle::Demangle(Ref<Platform> platform, string mangledName) : + reader(mangledName), + m_arch(platform->GetArchitecture()), + m_platform(platform), + m_view(nullptr) +{ + m_logger = LogRegistry::CreateLogger("MSVCDemangle"); + m_logger->ResetIndent(); +} + + +Demangle::Demangle(Ref<BinaryView> view, string mangledName) : + reader(mangledName), + m_view(view) +{ + m_platform = view->GetDefaultPlatform(); + if (!m_platform) + throw DemangleException(); + m_arch = m_platform->GetArchitecture(); + m_logger = LogRegistry::CreateLogger("MSVCDemangle"); + m_logger->ResetIndent(); +} + + +TypeBuilder Demangle::DemangleVarType(BackrefList& varList, bool isReturn, QualifiedName& name) +{ + m_logger->LogDebug("%s: '%s' - %lu\n", __FUNCTION__, reader.GetRaw(), varList.nameList.size()); + TypeBuilder newType; + bool _const = false, _volatile = false, isMember = false; //TODO: use this info, _signed = false; + BNReferenceType refType; + BNTypeClass typeClass = IntegerTypeClass; + BNStructureVariant structType; + QualifiedName varName; + QualifiedName typeName; + BNNameType classFunctionType; + + size_t width; + char elm = reader.Read(); + switch (elm) + { + case 'A': + typeClass = PointerTypeClass; + refType = ReferenceReferenceType; + _const = false; + _volatile = false; + break; + case 'B': + typeClass = PointerTypeClass; + refType = ReferenceReferenceType; + _const = false; + _volatile = true; + break; + case 'C': return TypeBuilder::IntegerType(1, true); + case 'D': return TypeBuilder::IntegerType(1, true); + case 'E': return TypeBuilder::IntegerType(1, false); + case 'F': return TypeBuilder::IntegerType(2, true); + case 'G': return TypeBuilder::IntegerType(2, false); + case 'H': return TypeBuilder::IntegerType(4, true); + case 'I': return TypeBuilder::IntegerType(4, false); + case 'J': return TypeBuilder::IntegerType(4, true, "long"); + case 'K': return TypeBuilder::IntegerType(4, false, "unsigned long"); + case 'M': return TypeBuilder::FloatType(4); + case 'N': return TypeBuilder::FloatType(8); + case 'O': return TypeBuilder::FloatType(10, "long double"); + case 'P': // * + typeClass = PointerTypeClass; + refType = PointerReferenceType; + _const = false; + _volatile = false; + break; + case 'Q': // const * + typeClass = PointerTypeClass; + refType = PointerReferenceType; + _const = true; + _volatile = false; + break; + case 'R': // volatile * + typeClass = PointerTypeClass; + refType = PointerReferenceType; + _const = false; + _volatile = true; + break; + case 'S': // const volatile * + typeClass = PointerTypeClass; + refType = PointerReferenceType; + _const = true; + _volatile = true; + break; + case 'T': typeClass = StructureTypeClass; structType = UnionStructureType; break; + case 'U': typeClass = StructureTypeClass; structType = StructStructureType; break; + case 'V': typeClass = StructureTypeClass; structType = ClassStructureType; break; + case 'W': + typeClass = EnumerationTypeClass; + switch (reader.Read()) + { + case '0': width = 1; /* TODO: use these _signed = true; */ break; + case '1': width = 1; /* TODO: use these _signed = false; */ break; + case '2': width = 2; /* TODO: use these _signed = true; */ break; + case '3': width = 2; /* TODO: use these _signed = false; */ break; + case '4': width = 4; /* TODO: use these _signed = true; */ break; + case '5': width = 4; /* TODO: use these _signed = false; */ break; + case '6': width = 4; /* TODO: use these _signed = true; */ break; + case '7': width = 4; /* TODO: use these _signed = false; */ break; + default: throw DemangleException(); + } + break; + case 'X': return TypeBuilder::VoidType(); break; + case 'Y': + throw DemangleException(); //TODO: handle cointerfaces + case 'Z': return TypeBuilder::VarArgsType(); break; + case '_': + switch (reader.Read()) + { + case 'D': newType = TypeBuilder::IntegerType(1, true); break; + case 'E': newType = TypeBuilder::IntegerType(1, false); break; + case 'F': newType = TypeBuilder::IntegerType(2, true); break; + case 'G': newType = TypeBuilder::IntegerType(2, false); break; + case 'H': newType = TypeBuilder::IntegerType(4, true); break; + case 'I': newType = TypeBuilder::IntegerType(4, false); break; + case 'J': newType = TypeBuilder::IntegerType(8, true); break; + case 'K': newType = TypeBuilder::IntegerType(8, false); break; + case 'L': newType = TypeBuilder::IntegerType(16, true); break; + case 'M': newType = TypeBuilder::IntegerType(16, false); break; + case 'N': newType = TypeBuilder::BoolType(); break; + case 'O': + { + QualifiedName name; + m_logger->Indent(); + auto childType = DemangleVarType(varList, false, name); + m_logger->Dedent(); + newType = TypeBuilder::ArrayType(childType.Finalize(), 0); + break; + } + case 'S': newType = TypeBuilder::IntegerType(2, true, "char16_t"); break; + case 'U': newType = TypeBuilder::IntegerType(4, true, "char32_t"); break; + case 'W': newType = TypeBuilder::IntegerType(2, false, "wchar_t"); break; + case 'X': typeClass = StructureTypeClass; structType = ClassStructureType; break; //Coclass + case 'Y': typeClass = StructureTypeClass; structType = ClassStructureType; break; //Cointerface + default: + throw DemangleException(); + } + break; + case '$': + if (reader.PeekString(2) == "$Q") // && + { + reader.Consume(2); + typeClass = PointerTypeClass; + refType = RValueReferenceType; + _const = false; + _volatile = false; + } + else if (reader.PeekString(2) == "$R") // && volatile + { + reader.Consume(2); + typeClass = PointerTypeClass; + refType = RValueReferenceType; + _const = false; + _volatile = true; + } + else if (reader.PeekString(2) == "$A") + { + reader.Consume(2); + char num = reader.Read(); + if (num == 8) + return DemangleFunction(NoNameType, true, varList); + if (num == '6' || num == '7') + return DemangleFunction(NoNameType, false, varList); + throw DemangleException(); + } + else if (reader.PeekString(2) == "$C") + { + reader.Consume(2); + DemangleModifiers(_const, _volatile, isMember); + QualifiedName name; + m_logger->Indent(); + newType = DemangleVarType(varList, false, name); + m_logger->Dedent(); + newType.SetConst(_const); + newType.SetVolatile(_volatile); + return newType; + } + else if (reader.PeekString(2) == "$T") + { + reader.Consume(2); + return TypeBuilder::ValueType("std::nullptr"); + } + else if (reader.Peek() == '0') + { + reader.Consume(); + int64_t value; + DemangleNumber(value); + return TypeBuilder::ValueType(to_string(value)); + } + else if (reader.Peek() == '1') + { + reader.Consume(); + auto context = DemangleSymbol(); + return TypeBuilder::PointerType(m_arch, context.type.Finalize()); + } + else + throw DemangleException(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + //Make a copy of the item in the backref list. Exit early since we don't want this added to the backref list. + m_logger->LogDebug("Backref %u %lu", elm - '0', varList.typeList.size()); + return varList.GetTypeBackref(elm - '0'); + default: + throw DemangleException(); + } + + switch (typeClass) + { + case PointerTypeClass: + { + switch (reader.Peek()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + throw DemangleException(); + case '6': + { + if (refType != PointerReferenceType) //No references to functions + { + throw DemangleException(); + } + reader.Consume(); + auto childType = DemangleFunction(NoNameType, false, varList); + newType = TypeBuilder::PointerType(m_arch, + childType.Finalize(), + _const, + _volatile, + refType); + break; + } + case '7': //Function pointer + case '9': //Class Function pointer + { + if (refType != PointerReferenceType) //No references to functions + { + throw DemangleException(); + } + reader.Consume(); + auto childType = DemangleFunction(NoNameType, true, varList); + newType = TypeBuilder::PointerType(m_arch, + childType.Finalize(), + _const, + _volatile, + refType); + break; + } + case '8': //Named class function pointer + { + if (refType != PointerReferenceType) //No references to functions + { + throw DemangleException(); + } + reader.Consume(); + DemangleName(name, classFunctionType, varList); + name.push_back(""); + auto childType = DemangleFunction(NoNameType, true, varList); + newType = TypeBuilder::PointerType(m_arch, + childType.Finalize(), + _const, + _volatile, + refType); + break; + } + default: // Non-numeric + { + m_logger->LogDebug("Demangle pointer subtype: '%s'\n", reader.GetRaw()); + TypeBuilder child; + bool _const2 = false, _volatile2 = false, isMember = false; + auto suffix = DemanglePointerSuffix(); + DemangleModifiers(_const2, _volatile2, isMember); + if (reader.Peek() == 'Y') //Multi-dimentional array + { + m_logger->LogDebug("Demangle multi-dimentional array"); + int64_t nDimentions; + reader.Consume(); + DemangleNumber(nDimentions); + vector<uint64_t> elementList; + while (nDimentions--) + { + int64_t element = 0; + DemangleNumber(element); + elementList.push_back(element); + } + QualifiedName name; + m_logger->Indent(); + child = DemangleVarType(varList, false, name); + m_logger->Dedent(); + + for (auto i = elementList.rbegin(); i != elementList.rend(); i++) + { + child = TypeBuilder::ArrayType(child.Finalize(), *i); + } + } + else + { + QualifiedName name; + m_logger->Indent(); + child = DemangleVarType(varList, true, name); + m_logger->Dedent(); + } + + child.SetConst(_const2); + child.SetVolatile(_volatile2); + newType = TypeBuilder::PointerType(m_arch, + child.Finalize(), + _const, + _volatile, + refType); + + newType.SetPointerSuffix(suffix); + m_logger->LogDebug("Name: %s\n", newType.GetString().c_str()); + break; + } + } + break; + } + case EnumerationTypeClass: + m_logger->LogDebug("Demangle enumeration\n"); + m_logger->Indent(); + DemangleName(typeName, classFunctionType, varList); + m_logger->Dedent(); + newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference(EnumNamedTypeClass, typeName), + width, width); + break; + case StructureTypeClass: + m_logger->LogDebug("Demangle structure\n"); + m_logger->Indent(); + DemangleName(typeName, classFunctionType, varList); + m_logger->Dedent(); + switch (structType) + { + case ClassStructureType: + newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + ClassNamedTypeClass, typeName)); + break; + case StructStructureType: + newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + StructNamedTypeClass, typeName)); + break; + case UnionStructureType: + newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + UnionNamedTypeClass, typeName)); + break; + default: + newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + UnknownNamedTypeClass, typeName)); + break; + } + break; + default: + break; + } + if (!isReturn) + { + varList.PushTypeBackref(newType); + } + return newType; +} + + +void Demangle::DemangleNumber(int64_t& num) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + num = 0; + int mult = 1; + if (reader.Peek() == '?') + { + mult = -1; + reader.Consume(); + } + + //The number is decimal 1-10 + if (reader.Peek() >= '0' && reader.Peek() <= '9') + { + num = mult * (reader.Read() + 1 - '0'); + return; + } + else + { + //The number is hexidecimal + string strnum = reader.ReadUntil('@'); + for (auto a : strnum) + { + num *= 16; + if (a >= 'A' && a <= 'P') + num += a - 'A'; + else + throw DemangleException(); + } + num *= mult; + return; + } +} + + +void Demangle::DemangleChar(char& ch) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + // Basic char is just the char + if (reader.Peek() != '?') + { + ch = reader.Peek(); + reader.Consume(); + return; + } + reader.Consume(); + + // Hex char is ?$XX for 2 hex digits XX + if (reader.Peek() == '$') + { + m_logger->LogDebug("%s: Hex digit '%s'\n", __FUNCTION__, reader.GetRaw()); + + reader.Consume(); + char c1 = reader.Peek(); + reader.Consume(); + char c2 = reader.Peek(); + reader.Consume(); + + if (c1 < 'A' || c1 > 'P') + throw DemangleException("Invalid character"); + if (c2 < 'A' || c2 > 'P') + throw DemangleException("Invalid character"); + + uint8_t b1 = c1 - 'A'; + uint8_t b2 = c2 - 'A'; + + ch = (char)((b1 << 4) | b2); + return; + } + + m_logger->LogDebug("%s: Table lookup '%s'\n", __FUNCTION__, reader.GetRaw()); + + // Otherwise it's a lookup based on some big table + // Thanks, LLVM! + switch (reader.Peek()) + { + case '0': ch = ','; reader.Consume(); return; + case '1': ch = '/'; reader.Consume(); return; + case '2': ch = '\\'; reader.Consume(); return; + case '3': ch = ':'; reader.Consume(); return; + case '4': ch = '.'; reader.Consume(); return; + case '5': ch = ' '; reader.Consume(); return; + case '6': ch = '\n'; reader.Consume(); return; + case '7': ch = '\t'; reader.Consume(); return; + case '8': ch = '\''; reader.Consume(); return; + case '9': ch = '-'; reader.Consume(); return; + case 'a': ch = '\xE1'; reader.Consume(); return; + case 'b': ch = '\xE2'; reader.Consume(); return; + case 'c': ch = '\xE3'; reader.Consume(); return; + case 'd': ch = '\xE4'; reader.Consume(); return; + case 'e': ch = '\xE5'; reader.Consume(); return; + case 'f': ch = '\xE6'; reader.Consume(); return; + case 'g': ch = '\xE7'; reader.Consume(); return; + case 'h': ch = '\xE8'; reader.Consume(); return; + case 'i': ch = '\xE9'; reader.Consume(); return; + case 'j': ch = '\xEA'; reader.Consume(); return; + case 'k': ch = '\xEB'; reader.Consume(); return; + case 'l': ch = '\xEC'; reader.Consume(); return; + case 'm': ch = '\xED'; reader.Consume(); return; + case 'n': ch = '\xEE'; reader.Consume(); return; + case 'o': ch = '\xEF'; reader.Consume(); return; + case 'p': ch = '\xF0'; reader.Consume(); return; + case 'q': ch = '\xF1'; reader.Consume(); return; + case 'r': ch = '\xF2'; reader.Consume(); return; + case 's': ch = '\xF3'; reader.Consume(); return; + case 't': ch = '\xF4'; reader.Consume(); return; + case 'u': ch = '\xF5'; reader.Consume(); return; + case 'v': ch = '\xF6'; reader.Consume(); return; + case 'w': ch = '\xF7'; reader.Consume(); return; + case 'x': ch = '\xF8'; reader.Consume(); return; + case 'y': ch = '\xF9'; reader.Consume(); return; + case 'z': ch = '\xFA'; reader.Consume(); return; + case 'A': ch = '\xC1'; reader.Consume(); return; + case 'B': ch = '\xC2'; reader.Consume(); return; + case 'C': ch = '\xC3'; reader.Consume(); return; + case 'D': ch = '\xC4'; reader.Consume(); return; + case 'E': ch = '\xC5'; reader.Consume(); return; + case 'F': ch = '\xC6'; reader.Consume(); return; + case 'G': ch = '\xC7'; reader.Consume(); return; + case 'H': ch = '\xC8'; reader.Consume(); return; + case 'I': ch = '\xC9'; reader.Consume(); return; + case 'J': ch = '\xCA'; reader.Consume(); return; + case 'K': ch = '\xCB'; reader.Consume(); return; + case 'L': ch = '\xCC'; reader.Consume(); return; + case 'M': ch = '\xCD'; reader.Consume(); return; + case 'N': ch = '\xCE'; reader.Consume(); return; + case 'O': ch = '\xCF'; reader.Consume(); return; + case 'P': ch = '\xD0'; reader.Consume(); return; + case 'Q': ch = '\xD1'; reader.Consume(); return; + case 'R': ch = '\xD2'; reader.Consume(); return; + case 'S': ch = '\xD3'; reader.Consume(); return; + case 'T': ch = '\xD4'; reader.Consume(); return; + case 'U': ch = '\xD5'; reader.Consume(); return; + case 'V': ch = '\xD6'; reader.Consume(); return; + case 'W': ch = '\xD7'; reader.Consume(); return; + case 'X': ch = '\xD8'; reader.Consume(); return; + case 'Y': ch = '\xD9'; reader.Consume(); return; + case 'Z': ch = '\xDA'; reader.Consume(); return; + default: + throw DemangleException("Unknown character"); + } +} + + +void Demangle::DemangleWideChar(uint16_t& wch) +{ + char c1, c2; + DemangleChar(c1); + DemangleChar(c2); + + wch = (uint16_t)(((uint16_t)c1 << 8) | (uint16_t)c2); +} + + +void Demangle::DemangleVariableList(vector<FunctionParameter>& paramList, BackrefList& varList) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + bool _const = false, _volatile = false, isMember = false; + set<BNPointerSuffix> suffix; + for (size_t i = 0; reader.Peek() != 'Z'; i++) + { + bool hasModifiers = false; + if (reader.Peek() == '@') + { + reader.Consume(); + break; + } + else if (reader.Peek() == '?') + { + reader.Consume(); + suffix = DemanglePointerSuffix(); + DemangleModifiers(_const, _volatile, isMember); + hasModifiers = true; + } + + FunctionParameter vt; + QualifiedName name; + m_logger->LogDebug("Argument %d: %s", i, reader.GetRaw()); + m_logger->Indent(); + TypeBuilder type = DemangleVarType(varList, false, name); + m_logger->Dedent(); + if (hasModifiers) + { + type.SetConst(_const); + type.SetVolatile(_volatile); + type.SetPointerSuffix(suffix); + } + vt.name = name.GetString(); + vt.type = type.Finalize(); + vt.defaultLocation = true; + + paramList.push_back(vt); + m_logger->LogDebug("Argument %zu: '%s' - '%s'\n", i, vt.type->GetString().c_str(), reader.GetRaw()); + } + if (reader.Peek() == 'Z') + reader.Consume(); + m_logger->LogDebug("%s: done '%s'\n", __FUNCTION__, reader.GetRaw()); +} + + +Demangle::NameType Demangle::GetNameType() +{ + if (reader.Peek() == '?') + { + reader.Consume(); + if (reader.Peek()== '?') + { + reader.Consume(); + return GetNameType(); + } + else if (reader.Peek() == '$') + { + reader.Consume(); + return NameTemplate; + } + else if (reader.Peek() == '0') + { + reader.Consume(); + return NameConstructor; + } + else if (reader.Peek() == '1') + { + reader.Consume(); + return NameDestructor; + } + else if (reader.Peek() == 'B') + { + reader.Consume(); + return NameReturn; + } + else if (reader.PeekString(2) == "_R") + { + reader.Consume(2); + return NameRtti; + } + // else if (reader.PeekString(3) == "__E") + // { + // reader.Consume(2); + // return NameDynamicInitializer; + // } + else + { + return NameLookup; + } + } + else if (reader.Peek() >= '0' && reader.Peek() <= '9') + { + return NameBackref; + } + return NameString; +} + + +void Demangle::DemangleNameTypeString(string& out) +{ + out = reader.ReadUntil('@'); +} + + +void Demangle::DemangleNameTypeRtti(BNNameType& classFunctionType, + BackrefList& nameBackrefList, + string& out) +{ + TypeBuilder rtti; + switch (reader.Read()) + { + case '0': + { + if (reader.Peek() != '?') + throw DemangleException(); + reader.Consume(); + + bool _const = false, _volatile = false, isMember = false; + auto suffix = DemanglePointerSuffix(); + DemangleModifiers(_const, _volatile, isMember); + + QualifiedName name; + m_logger->Indent(); + rtti = DemangleVarType(nameBackrefList, false, name); + m_logger->Dedent(); + rtti.SetConst(_const); + rtti.SetVolatile(_volatile); + rtti.SetPointerSuffix(suffix); + out = rtti.GetString() + " `RTTI Type Descriptor' "; + classFunctionType = RttiTypeDescriptor; + break; + } + case '1': + out = "`RTTI Base Class Descriptor at ("; + for (int i = 0; i < 4; i++) + { + int64_t num = 0; + DemangleNumber(num); + if (i > 0) + { + out += ","; + } + out += to_string(num); + } + out += ")'"; + classFunctionType = RttiBaseClassDescriptor; + break; + case '2': + out = "`RTTI Base Class Array'"; + classFunctionType = RttiBaseClassArray; + break; + case '3': + out = "`RTTI Class Hierarchy Descriptor'"; + classFunctionType = RttiClassHierarchyDescriptor; + break; + case '4': + out = "`RTTI Complete Object Locator'"; + classFunctionType = RttiCompleteObjectLocator; + break; + default: throw DemangleException(); + } +} + + +void Demangle::DemangleTypeNameLookup(string& out, BNNameType& functionType) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + switch (reader.Read()) + { + case '?': functionType = NoNameType; break; + case '2': functionType = OperatorNewNameType; break; + case '3': functionType = OperatorDeleteNameType; break; + case '4': functionType = OperatorAssignNameType; break; + case '5': functionType = OperatorRightShiftNameType; break; + case '6': functionType = OperatorLeftShiftNameType; break; + case '7': functionType = OperatorNotNameType; break; + case '8': functionType = OperatorEqualNameType; break; + case '9': functionType = OperatorNotEqualNameType; break; + case 'A': functionType = OperatorArrayNameType; break; + case 'C': functionType = OperatorArrowNameType; break; + case 'D': functionType = OperatorStarNameType; break; + case 'E': functionType = OperatorIncrementNameType; break; + case 'F': functionType = OperatorDecrementNameType; break; + case 'G': functionType = OperatorMinusNameType; break; + case 'H': functionType = OperatorPlusNameType; break; + case 'I': functionType = OperatorBitAndNameType; break; + case 'J': functionType = OperatorArrowStarNameType; break; + case 'K': functionType = OperatorDivideNameType; break; + case 'L': functionType = OperatorModulusNameType; break; + case 'M': functionType = OperatorLessThanNameType; break; + case 'N': functionType = OperatorLessThanEqualNameType; break; + case 'O': functionType = OperatorGreaterThanNameType; break; + case 'P': functionType = OperatorGreaterThanEqualNameType; break; + case 'Q': functionType = OperatorCommaNameType; break; + case 'R': functionType = OperatorParenthesesNameType; break; + case 'S': functionType = OperatorTildeNameType; break; + case 'T': functionType = OperatorXorNameType; break; + case 'U': functionType = OperatorBitOrNameType; break; + case 'V': functionType = OperatorLogicalAndNameType; break; + case 'W': functionType = OperatorLogicalOrNameType; break; + case 'X': functionType = OperatorStarEqualNameType; break; + case 'Y': functionType = OperatorPlusEqualNameType; break; + case 'Z': functionType = OperatorMinusEqualNameType; break; + case '_': + { + m_logger->LogDebug(" %s: '%s'\n", __FUNCTION__, reader.GetRaw()); + switch (reader.Read()) + { + case '0': functionType = OperatorDivideEqualNameType; break; + case '1': functionType = OperatorModulusEqualNameType; break; + case '2': functionType = OperatorRightShiftEqualNameType; break; + case '3': functionType = OperatorLeftShiftEqualNameType; break; + case '4': functionType = OperatorAndEqualNameType; break; + case '5': functionType = OperatorOrEqualNameType; break; + case '6': functionType = OperatorXorEqualNameType; break; + case '7': functionType = VFTableNameType; break; + case '8': functionType = VBTableNameType; break; + case '9': functionType = VCallNameType; break; + case 'A': functionType = TypeofNameType; break; + case 'B': functionType = LocalStaticGuardNameType; break; + case 'C': functionType = StringNameType; break; + case 'D': functionType = VBaseDestructorNameType; break; + case 'E': functionType = VectorDeletingDestructorNameType; break; + case 'F': functionType = DefaultConstructorClosureNameType; break; + case 'G': functionType = ScalarDeletingDestructorNameType; break; + case 'H': functionType = VectorConstructorIteratorNameType; break; + case 'I': functionType = VectorDestructorIteratorNameType; break; + case 'J': functionType = VectorVBaseConstructorIteratorNameType; break; + case 'K': functionType = VirtualDisplacementMapNameType; break; + case 'L': functionType = EHVectorConstructorIteratorNameType; break; + case 'M': functionType = EHVectorDestructorIteratorNameType; break; + case 'N': functionType = EHVectorVBaseConstructorIteratorNameType; break; + case 'O': functionType = CopyConstructorClosureNameType; break; + case 'P': functionType = UDTReturningNameType; break; + case 'S': functionType = LocalVFTableNameType; break; + case 'T': functionType = LocalVFTableConstructorClosureNameType; break; + case 'U': functionType = OperatorNewArrayNameType; break; + case 'V': functionType = OperatorDeleteArrayNameType; break; + case 'X': functionType = PlacementDeleteClosureNameType; break; + case 'Y': functionType = PlacementDeleteClosureArrayNameType; break; + case 'Q': // Fallthrough + case 'W': // Fallthrough + case 'Z': functionType = NoNameType; break; + case '_': + m_logger->LogDebug(" %s: '%s'\n", __FUNCTION__, reader.GetRaw()); + switch (reader.Read()) + { + case 'A': functionType = ManagedVectorConstructorIteratorNameType; break; + case 'B': functionType = ManagedVectorDestructorIteratorNameType; break; + case 'C': functionType = EHVectorCopyConstructorIteratorNameType; break; + case 'D': functionType = EHVectorVBaseConstructorIteratorNameType; break; + case 'E': functionType = DynamicInitializerNameType; break; + case 'F': functionType = DynamicAtExitDestructorNameType; break; + case 'G': functionType = VectorCopyConstructorIteratorNameType; break; + case 'H': functionType = VectorVBaseCopyConstructorIteratorNameType; break; + case 'I': functionType = ManagedVectorCopyConstructorIteratorNameType; break; + case 'J': functionType = LocalStaticGuardNameType; break; + case 'K': functionType = UserDefinedLiteralOperatorNameType; break; + default: throw DemangleException("Demangle Lookup Failed"); // fall through + } + break; + default: + throw DemangleException("Demangle Lookup Failed"); + } + break; + } + default: throw DemangleException("Demangle Lookup Failed"); + } + out = Type::GetNameTypeString(functionType); +} + + +string Demangle::DemangleTemplateInstantiationName(BackrefList& nameBackrefList) +{ + string out; + BackrefList templateBackref; + reader.Consume(2); + m_logger->LogDebug("DemangleTemplateInstantiationName: '%s'\n", reader.GetRaw()); + if (reader.Peek() >= '0' && reader.Peek() <= '9') + { + out = nameBackrefList.GetStringBackref(reader.Read() - '0'); + } + else + { + DemangleNameTypeString(out); + } + nameBackrefList.PushStringBackref(out); + return out; +} + + +string Demangle::DemangleTemplateParams(vector<FunctionParameter>& params, BackrefList& nameBackrefList, string& out) +{ + m_logger->Indent(); + DemangleVariableList(params, nameBackrefList); + m_logger->Dedent(); + m_logger->LogDebug("VariableList done\n"); + out += "<"; + for (size_t i = 0; i < params.size(); i++) + { + if (i == 0) + { + out += params[i].type->GetString(); + } + else + { + out += "," + params[i].type->GetString(); + } + } + if (out[out.size()-1] == '>') + out += " "; //Be c++03 compliant where we can + out += ">"; + + nameBackrefList.PushStringBackref(out); + return out; +} + +// void Demangle::DemangleInitFiniStub(bool destructor, QualifiedName& nameList, BackrefList& nameBackrefList, BNNameType& classFunctionType) +// { +// bool isStatic = false; +// if (reader.Peek() == '?') +// { +// reader.Consume(); +// isStatic = true; +// } +// string out = DemangleUnqualifiedSymbolName(nameList, nameBackrefList, classFunctionType); +// } + + +string Demangle::DemangleUnqualifiedSymbolName(QualifiedName& nameList, BackrefList& nameBackrefList, BNNameType& classFunctionType) +{ + string out; + if (reader.PeekString(2) == "?$") + { + reader.Consume(2); + out = DemangleTemplateInstantiationName(nameBackrefList); + nameList.insert(nameList.begin(), out); + } + else if (reader.Peek() == '?') + { + reader.Consume(); + DemangleTypeNameLookup(out, classFunctionType); + } + else if (reader.Peek() >= '0' && reader.Peek() <= '9') + { + out = nameBackrefList.GetStringBackref(reader.Read() - '0'); + } + else + { + DemangleNameTypeString(out); + } + return out; +} + + +TypeBuilder Demangle::DemangleString() +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + // ??_C@_<length><crc32>@<name> + if (reader.Peek() != '_') + { + throw DemangleException("Invalid mangled string name"); + } + reader.Consume(); + + // Wide char flag (1 yes / 0 no) + bool isWideChar = false; + switch (reader.Peek()) + { + case '1': + isWideChar = true; + break; + case '0': + break; + default: + throw DemangleException("Invalid mangled string name"); + } + reader.Consume(); + + // Length is just a number + + int64_t lengthRaw; + DemangleNumber(lengthRaw); + if (lengthRaw < 0) + { + throw DemangleException("Invalid mangled string name"); + } + uint64_t length = (uint64_t)lengthRaw; + + m_logger->LogDebug("%s: Before CRC32 '%s'\n", __FUNCTION__, reader.GetRaw()); + + // CRC32 (ignored) + while (reader.Peek() != '@') + { + // Usually 8 bytes but I've seen it be 7 for some ungodly reason + reader.Consume(); + } + + reader.Consume(); + + bool truncated = false; + string name = ""; + TypeBuilder type; + + // String bytes + if (isWideChar) + { + m_logger->LogDebug("%s: Wide string '%s'\n", __FUNCTION__, reader.GetRaw()); + string utf8name; + truncated = (length > 64); + while (reader.Peek() != '@') + { + uint16_t wch; + DemangleWideChar(wch); + + uint8_t chs[2]; + chs[0] = wch & 0xFF; + chs[1] = wch >> 8; + + // TODO: This is actually UCS2 but we don't have an easy decoder for that + utf8name += Unicode::UTF16ToUTF8(&chs[0], 2); + } + reader.Consume(); + + name = Unicode::ToEscapedString(Unicode::GetBlocksForNames({}), false, utf8name.data(), utf8name.size()); + type = Type::ArrayType(Type::WideCharType(2), length / 2); + } + else + { + m_logger->LogDebug("%s: Non-wide string '%s'\n", __FUNCTION__, reader.GetRaw()); + uint64_t numNulls = 0; + size_t endNulls = 0; + vector<uint8_t> chars; + while (reader.Peek() != '@') + { + char ch; + DemangleChar(ch); + if (ch == 0) + { + numNulls++; + endNulls++; + } + else + { + endNulls = 0; + } + chars.push_back(ch); + } + reader.Consume(); + + if (length > (uint64_t)chars.size() + 1) + { + truncated = true; + } + + // Now time to guess encoding + if (chars.size() % 1 != 0) + { + m_logger->LogDebug("%s: Looks like UTF8 '%s'\n", __FUNCTION__, reader.GetRaw()); + name = Unicode::ToEscapedString(Unicode::GetBlocksForNames({}), false, chars.data(), chars.size() - endNulls); + type = Type::ArrayType(Type::IntegerType(1, true), length); + } + else + { + if (chars.size() % 4 == 0 && numNulls > length * 2 / 3) + { + m_logger->LogDebug("%s: Looks like UTF32 '%s'\n", __FUNCTION__, reader.GetRaw()); + string utf8name; + for (size_t i = 0; i < chars.size() - endNulls; i += 4) + { + utf8name += Unicode::UTF32ToUTF8(chars.data() + i); + } + name = Unicode::ToEscapedString(Unicode::GetBlocksForNames({}), false, utf8name.data(), utf8name.size()); + type = Type::ArrayType(Type::WideCharType(4), length / 4); + } + else if (numNulls > length / 3) + { + m_logger->LogDebug("%s: Looks like UTF16 '%s'\n", __FUNCTION__, reader.GetRaw()); + string utf8name; + for (size_t i = 0; i < chars.size() - endNulls; i += 2) + { + utf8name += Unicode::UTF16ToUTF8(chars.data() + i, 2); + } + name = Unicode::ToEscapedString(Unicode::GetBlocksForNames({}), false, utf8name.data(), utf8name.size()); + type = Type::ArrayType(Type::WideCharType(2), length / 2); + } + else + { + m_logger->LogDebug("%s: Looks like UTF8 '%s'\n", __FUNCTION__, reader.GetRaw()); + + name = Unicode::ToEscapedString(Unicode::GetBlocksForNames({}), false, chars.data(), chars.size() - endNulls); + type = Type::ArrayType(Type::IntegerType(1, true), length); + } + } + } + if (truncated) + { + name += "..."; + } + m_varName.push_back(name); + return type; +} + + +TypeBuilder Demangle::DemangleTypeInfoName() +{ + if (reader.Read() != '?') + throw DemangleException("Unknown raw name type"); + bool _const = false; + bool _volatile = false; + bool isMember = false; + DemangleModifiers(_const, _volatile, isMember); + + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + + QualifiedName name; + TypeBuilder type = DemangleVarType(m_backrefList, false, name); + type.SetConst(_const); + type.SetVolatile(_volatile); + + switch (type.GetClass()) + { + case NamedTypeReferenceClass: + m_varName = type.GetNamedTypeReference()->GetName(); + return type; + default: + throw DemangleException("Unexpected type of RTTI Type Name"); + } +} + + +void Demangle::DemangleName(QualifiedName& nameList, + BNNameType& classFunctionType, + BackrefList& nameBackrefList) +{ + string out; + BNNameType functionType; + BNNameType dummyFunctionType; + vector<FunctionParameter> params; + while(1) + { + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + switch (GetNameType()) + { + case NameString: + m_logger->LogDebug("Demangle String\n"); + DemangleNameTypeString(out); + nameList.insert(nameList.begin(), out); + m_logger->LogDebug("Pushing backref NameString %s", out.c_str()); + nameBackrefList.PushStringBackref(out); + m_logger->LogDebug("nameList.front(): %s\n", nameList.front().c_str()); + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + break; + case NameLookup: + m_logger->LogDebug("Demangle Lookup\n"); + DemangleTypeNameLookup(out, functionType); + classFunctionType = functionType; + nameList.insert(nameList.begin(), out); + break; + case NameBackref: + m_logger->LogDebug("Demangle Backref"); + out = nameBackrefList.GetStringBackref(reader.Read() - '0'); + m_logger->LogDebug("Demangle Backref: %s", out.c_str()); + nameList.insert(nameList.begin(), out); + break; + case NameTemplate: + { + m_logger->LogDebug("Demangle Template: '%s'\n", reader.GetRaw()); + BackrefList templateBackref; + out = DemangleUnqualifiedSymbolName(nameList, templateBackref, functionType); + m_logger->LogDebug("Pushing backref NameTemplate %s", out.c_str()); + templateBackref.PushStringBackref(out); + m_logger->LogDebug("Demangling Template variables %s\n", reader.GetRaw()); + DemangleTemplateParams(params, templateBackref, out); + nameList.insert(nameList.begin(), out); + nameBackrefList.PushStringBackref(out); + break; + } + case NameConstructor: + m_logger->LogDebug("NameConstructor\n"); + classFunctionType = ConstructorNameType; + DemangleName(nameList, dummyFunctionType, nameBackrefList); + if (nameList.size() == 0) + throw DemangleException(); + nameList.push_back(nameList[nameList.size()-1]); + return; + case NameDestructor: + classFunctionType = ConstructorNameType; + m_logger->LogDebug("NameDestructor\n"); + DemangleName(nameList, dummyFunctionType, nameBackrefList); + if (nameList.size() == 0) + throw DemangleException(); + nameList.push_back("~" + nameList[nameList.size()-1]); + return; + case NameRtti: + m_logger->LogDebug("NameRtti\n"); + DemangleNameTypeRtti(classFunctionType, nameBackrefList, out); + nameList.insert(nameList.begin(), out); + break; + // case NameDynamicInitializer: + // m_logger->LogDebug("NameDynamicInitializer\n"); + // DemangleInitFiniStub(false); + // break; + // case NameDynamicAtExitDestructor: + // m_logger->LogDebug("NameDynamicAtExitDestructor\n"); + // DemangleInitFiniStub(false); + // break; + case NameReturn: + m_logger->LogDebug("NameReturn\n"); + classFunctionType = OperatorReturnTypeNameType; + if (reader.PeekString(2) == "?$") + { + out = DemangleTemplateInstantiationName(nameBackrefList); + DemangleTemplateParams(params, nameBackrefList, out); + } + else + { + DemangleNameTypeString(out); + nameBackrefList.PushStringBackref(out); + } + nameList.insert(nameList.begin(), out); + break; + default: + throw DemangleException(); + } + if (nameList.StringSize() > MAX_DEMANGLE_LENGTH) + throw DemangleException(); + if (reader.Peek() == '@') + { + reader.Consume(); + return; + } + } +} + +Ref<CallingConvention> Demangle::GetCallingConventionForType(BNCallingConventionName ccName) +{ + string name; + switch (ccName) + { + case NoCallingConvention: name = ""; break; + case CdeclCallingConvention: name = "cdecl"; break; + case PascalCallingConvention: name = "pascal"; break; + case ThisCallCallingConvention: name = "thiscall"; break; + case STDCallCallingConvention: name = "stdcall"; break; + case FastcallCallingConvention: name = "fastcall"; break; + case CLRCallCallingConvention: name = "clrcall"; break; + case EabiCallCallingConvention: name = "eabi"; break; + case VectorCallCallingConvention: name = "vectorcall"; break; + case SwiftCallingConvention: name = "swiftcall"; break; + case SwiftAsyncCallingConvention: name = "swiftasync"; break; + default: break; + } + + if (m_platform) + { + for (const auto& cc : m_platform->GetCallingConventions()) + { + if (cc->GetName() == name) + return cc; + } + } + + for (const auto& cc : m_arch->GetCallingConventions()) + { + if (cc->GetName() == name) + return cc; + } + return nullptr; +} + +BNCallingConventionName Demangle::DemangleCallingConvention() +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + switch (reader.Read()) + { + case 'A': //Exported function + case 'B': return CdeclCallingConvention; + case 'C': //Exported function + case 'D': return PascalCallingConvention; + case 'E': //Exported function + case 'F': return ThisCallCallingConvention; + case 'G': //Exported function + case 'H': return STDCallCallingConvention; + case 'I': //Exported function + case 'J': return FastcallCallingConvention; + case 'K': //Exported function + case 'L': return NoCallingConvention; + case 'M': //Exported function + case 'N': return CLRCallCallingConvention; + case 'O': //Exported function + case 'P': return EabiCallCallingConvention; + case 'Q': return VectorCallCallingConvention; + case 'S': return SwiftCallingConvention; + case 'W': return SwiftAsyncCallingConvention; + default:throw DemangleException(); + } +} + +set<BNPointerSuffix> Demangle::DemanglePointerSuffix() +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + set<BNPointerSuffix> suffix; + if (reader.Peek() == '@') + return suffix; + + char elm = reader.Peek(); + for (int i = 0; i < 5; i++, elm = reader.Peek()) + { + if (elm == 'E') + suffix.insert(suffix.end(), Ptr64Suffix); + else if (elm == 'F') + suffix.insert(suffix.end(), UnalignedSuffix); + else if (elm == 'G') + suffix.insert(suffix.end(), ReferenceSuffix); + else if (elm == 'H') + suffix.insert(suffix.end(), LvalueSuffix); + else if (elm == 'I') + suffix.insert(suffix.end(), RestrictSuffix); + else + break; + reader.Consume(1); + } + return suffix; +} + +void Demangle::DemangleModifiers(bool& _const, bool& _volatile, bool &isMember) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + if (reader.Peek() == '@') + return; + + _const = false; + _volatile = false; + isMember = false; + char elm = reader.Read(); + switch (elm) + { + case 'A': break; + case 'B': _const = true; break; + case 'J': _const = true; break; + case 'C': _volatile = true; break; + case 'G': _volatile = true; break; + case 'K': _volatile = true; break; + case 'D': _const = true; _volatile = true; break; + case 'H': _const = true; _volatile = true; break; + case 'L': _const = true; _volatile = true; break; + case '6': break; + case '7': break; + case 'M': break; + case 'N': break; + case 'O': _volatile = true; break; + case 'P': _volatile = true; _const = true; break; + case 'Q': isMember = true; break; + case 'U': break; + case 'Y': break; + case 'R': _const = true; isMember = true; break; + case 'V': _const = true; break; + case 'Z': _const = true; break; + case 'S': _volatile = true; isMember = true; break; + case 'W': _volatile = true; break; + case '0': _volatile = true; break; + case 'T': _const = true; _volatile = true; isMember = true; break; + case 'X': _const = true; _volatile = true; break; + case '1': _const = true; _volatile = true; break; + case '8': break; + case '9': break; + case '2': break; + case '3': _const = true; break; + case '4': _volatile = true; break; + case '5': _const = true; _volatile = true; break; + case '_': + elm = reader.Read(); + if (elm == 'A' || elm == 'B') + { + //For unhandled "member" and "based" parameters + break; + } + else if (elm == 'C' || elm == 'D') + { + //For unhandled "member" and "based" parameters + break; + } + else + { + throw DemangleException(); + } + break; + default: throw DemangleException(); + } + return; +} + + +TypeBuilder Demangle::DemangleFunction(BNNameType classFunctionType, bool pointerSuffix, BackrefList& nameBackrefList, int funcClass) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + bool _const = false, _volatile = false, isMember = false; + set<BNPointerSuffix> suffix; + TypeBuilder returnType; + BNCallingConventionName cc; + + //Demangle adjustor which we don't do anything with for now + if ((funcClass & StaticThunkFunctionClass) == StaticThunkFunctionClass) + { + int64_t adjustor; + DemangleNumber(adjustor); + m_varName.back() += "`adjustor{" + to_string(adjustor) + "}'"; + } + else if ((funcClass & VirtualThunkFunctionClass) == VirtualThunkFunctionClass) + { + if ((funcClass & VirtualThunkExFunctionClass) == VirtualThunkExFunctionClass) + { + int64_t vbptrOffset; + int64_t vbOffsetOffset; + int64_t vtorDispOffset; + int64_t staticOffset; + DemangleNumber(vbptrOffset); + DemangleNumber(vbOffsetOffset); + DemangleNumber(vtorDispOffset); + DemangleNumber(staticOffset); + m_varName.back() += "`vtordispex{" + to_string(vbptrOffset) + ", " + to_string(vbOffsetOffset) + ", " + to_string(vtorDispOffset) + ", " + to_string(staticOffset) + "}'"; + } + else + { + int64_t vtorDispOffset; + int64_t staticOffset; + DemangleNumber(vtorDispOffset); + DemangleNumber(staticOffset); + m_varName.back() += "`vtordisp{" + to_string(vtorDispOffset) + ", " + to_string(staticOffset) + "}'"; + } + } + + if (pointerSuffix) + { + suffix = DemanglePointerSuffix(); + DemangleModifiers(_const, _volatile, isMember); + } + if (reader.Peek() == '?') + reader.Consume(); + cc = DemangleCallingConvention(); + bool shouldHaveReturnType = true; + if (reader.Peek() == '@') + { + //No return type + shouldHaveReturnType = false; + reader.Consume(); + m_logger->LogDebug("Function has no return type %s", reader.GetRaw()); + } + else + { + //Demangle function return type + bool return_const = false, return_volatile = false, isMember = false; + set<BNPointerSuffix> return_suffix; + bool hasModifiers = false; + //Check for modifiers before return type + if (reader.Peek() == '?') + { + reader.Consume(1); + return_suffix = DemanglePointerSuffix(); + DemangleModifiers(return_const, return_volatile, isMember); + hasModifiers = true; + } + + QualifiedName name; + m_logger->LogDebug("Demangle function return type %s", reader.GetRaw()); + m_logger->Indent(); + returnType = DemangleVarType(nameBackrefList, true, name); + m_logger->LogDebug("Return type: %s", returnType.GetString().c_str()); + m_logger->Dedent(); + if (hasModifiers) + { + returnType.SetConst(return_const); + returnType.SetVolatile(return_volatile); + returnType.SetPointerSuffix(return_suffix); + } + } + if (reader.Peek() == '@') + reader.Consume(); + + m_logger->LogDebug("\tDemangle Function Parameters %s", reader.GetRaw()); + vector<FunctionParameter> params; + bool needsThisPtr = false; + if (cc == ThisCallCallingConvention) + { + needsThisPtr = true; + } + if (funcClass != NoneFunctionClass) + { + if ((funcClass & VirtualFunctionClass) == VirtualFunctionClass + || (funcClass & StaticThunkFunctionClass) == StaticThunkFunctionClass + || (funcClass & VirtualThunkFunctionClass) == VirtualThunkFunctionClass) + { + needsThisPtr = true; + } + else if ((funcClass & StaticFunctionClass) != StaticFunctionClass + && (funcClass & GlobalFunctionClass) != GlobalFunctionClass) + { + needsThisPtr = true; + } + } + + if (needsThisPtr) + { + // Insert implicit "this" parameter for thiscall + // TODO: Replace this with calling convention / platform callbacks to insert thisptr (ask rss) + QualifiedName thisName = m_varName; + if (thisName.size() > 0) + thisName.erase(thisName.end() - 1); + params.push_back(FunctionParameter("this", Type::PointerType(m_arch, Type::NamedType(thisName, Type::VoidType())), true, {})); + } + + DemangleVariableList(params, m_backrefList); + + if (params.size() >= 1 && params.back().type->GetClass() == VoidTypeClass) + params.pop_back(); + + // TODO: fix calling convention + TypeBuilder newType = TypeBuilder::FunctionType(shouldHaveReturnType ? returnType.Finalize() : Type::VoidType(), nullptr, params); + newType.SetConst(_const); + newType.SetVolatile(_volatile); + newType.SetPointerSuffix(suffix); + newType.SetNameType(classFunctionType); + newType.SetCallingConventionName(cc); + auto convention = GetCallingConventionForType(cc); + if (convention) + newType.SetCallingConvention(convention); + + m_logger->LogDebug("Successfully Created Function Type!\n"); + return newType; +} + + +TypeBuilder Demangle::DemangleData() +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + bool _const = false, _volatile = false, isMember = false; + QualifiedName name; + m_logger->Indent(); + TypeBuilder newType = DemangleVarType(m_backrefList, false, name); + m_logger->Dedent(); + auto suffix = DemanglePointerSuffix(); + DemangleModifiers(_const, _volatile, isMember); + newType.SetConst(_const); + newType.SetVolatile(_volatile); + newType.SetPointerSuffix(suffix); + return newType; +} + + +TypeBuilder Demangle::DemanagleRTTI(BNNameType nameType) +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + bool _const = false, _volatile = false, isMember = false; + if (reader.Length() > 0) + DemangleModifiers(_const, _volatile, isMember); + QualifiedName typeName = m_varName; + m_logger->LogDebug("new struct type\n"); + TypeBuilder newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + StructNamedTypeClass, typeName)); + newType.SetNameType(nameType); + newType.SetConst(_const); + newType.SetVolatile(_volatile); + m_logger->LogDebug("log: %s\n", newType.GetString().c_str()); + return newType; +} + + +TypeBuilder Demangle::DemangleVTable() +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + bool _const = false, _volatile = false, isMember = false; + DemangleModifiers(_const, _volatile, isMember); + TypeBuilder newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + StructNamedTypeClass, m_varName)); + if (reader.Peek() != '@') + { + QualifiedName typeName; + BNNameType classFunctionType = NoNameType; + DemangleName(typeName, classFunctionType, m_backrefList); + string suffix = m_varName.back(); + m_varName.back() += "{for `" + typeName.GetString() + "'}"; + + typeName.push_back(suffix); + newType = TypeBuilder::NamedType(NamedTypeReference::GenerateAutoDemangledTypeReference( + StructNamedTypeClass, typeName)); + } + newType.SetConst(_const); + newType.SetVolatile(_volatile); + newType.SetNameType(VFTableNameType); + return newType; +} + + + +Demangle::DemangleContext Demangle::DemangleSymbol() +{ + m_logger->LogDebug("%s: '%s'\n", __FUNCTION__, reader.GetRaw()); + m_logger->Indent(); + BNNameType classFunctionType = NoNameType; + QualifiedName varName; + + if (reader.Peek() == '.') + { + reader.Consume(); + + return { DemangleTypeInfoName(), NoAccess, NoScope }; + } + + if (reader.Read() != '?') + { + throw DemangleException(); + } + + DemangleName(varName, classFunctionType, m_backrefList); + m_logger->LogDebug("Done demangling Name: '%s' - '%s'", varName.GetString().c_str(), reader.GetRaw()); + m_varName = varName; + + DemangleContext context; + + if (classFunctionType == StringNameType) + { + context = { DemangleString(), NoAccess, NoScope }; + return context; + } + + char funcType = reader.Read(); + switch(funcType) + { + case '0': context = {DemangleData(), PrivateAccess, StaticScope }; break; + case '1': context = {DemangleData(), ProtectedAccess, StaticScope }; break; + case '2': context = {DemangleData(), PublicAccess, StaticScope }; break; + case '3': context = {DemangleData(), NoAccess, NoScope }; break; + case '4': context = {DemangleData(), NoAccess, NoScope }; break; + case '5': context = {DemangleVTable(), NoAccess, NoScope }; break; + case '6': context = {DemangleVTable(), NoAccess, NoScope }; break; + case '7': context = {DemangleVTable(), NoAccess, NoScope }; break; + case '8': context = {DemanagleRTTI(classFunctionType), NoAccess, NoScope }; break; + case '9': context = {DemanagleRTTI(classFunctionType), NoAccess, NoScope }; break; + case 'A': context = {DemangleFunction(classFunctionType, true, m_backrefList, PrivateFunctionClass), PrivateAccess, NoScope }; break; + case 'B': context = {DemangleFunction(classFunctionType, true, m_backrefList, PrivateFunctionClass), PrivateAccess, NoScope }; break; + case 'C': context = {DemangleFunction(classFunctionType, false, m_backrefList, PrivateFunctionClass | StaticFunctionClass), PrivateAccess, StaticScope }; break; + case 'D': context = {DemangleFunction(classFunctionType, false, m_backrefList, PrivateFunctionClass | StaticFunctionClass), PrivateAccess, StaticScope }; break; + case 'E': context = {DemangleFunction(classFunctionType, true, m_backrefList, PrivateFunctionClass | VirtualFunctionClass), PrivateAccess, VirtualScope}; break; + case 'F': context = {DemangleFunction(classFunctionType, true, m_backrefList, PrivateFunctionClass | VirtualFunctionClass), PrivateAccess, VirtualScope}; break; + case 'G': context = {DemangleFunction(classFunctionType, true, m_backrefList, PrivateFunctionClass | StaticThunkFunctionClass), PrivateAccess, ThunkScope }; break; + case 'H': context = {DemangleFunction(classFunctionType, true, m_backrefList, PrivateFunctionClass | StaticThunkFunctionClass), PrivateAccess, ThunkScope }; break; + case 'I': context = {DemangleFunction(classFunctionType, true, m_backrefList, ProtectedFunctionClass), ProtectedAccess, NoScope }; break; + case 'J': context = {DemangleFunction(classFunctionType, true, m_backrefList, ProtectedFunctionClass), ProtectedAccess, NoScope }; break; + case 'K': context = {DemangleFunction(classFunctionType, false, m_backrefList, ProtectedFunctionClass | StaticFunctionClass), ProtectedAccess, StaticScope }; break; + case 'L': context = {DemangleFunction(classFunctionType, false, m_backrefList, ProtectedFunctionClass | StaticFunctionClass), ProtectedAccess, StaticScope }; break; + case 'M': context = {DemangleFunction(classFunctionType, true, m_backrefList, ProtectedFunctionClass | VirtualFunctionClass), ProtectedAccess, VirtualScope}; break; + case 'N': context = {DemangleFunction(classFunctionType, true, m_backrefList, ProtectedFunctionClass | VirtualFunctionClass), ProtectedAccess, VirtualScope}; break; + case 'O': context = {DemangleFunction(classFunctionType, true, m_backrefList, ProtectedFunctionClass | StaticThunkFunctionClass), ProtectedAccess, ThunkScope }; break; + case 'P': context = {DemangleFunction(classFunctionType, true, m_backrefList, ProtectedFunctionClass | StaticThunkFunctionClass), ProtectedAccess, ThunkScope }; break; + case 'Q': context = {DemangleFunction(classFunctionType, true, m_backrefList, PublicFunctionClass), PublicAccess, NoScope }; break; + case 'R': context = {DemangleFunction(classFunctionType, true, m_backrefList, PublicFunctionClass), PublicAccess, NoScope }; break; + case 'S': context = {DemangleFunction(classFunctionType, false, m_backrefList, PublicFunctionClass | StaticFunctionClass), PublicAccess, StaticScope }; break; + case 'T': context = {DemangleFunction(classFunctionType, false, m_backrefList, PublicFunctionClass | StaticFunctionClass), PublicAccess, StaticScope }; break; + case 'U': context = {DemangleFunction(classFunctionType, true, m_backrefList, PublicFunctionClass | VirtualFunctionClass), PublicAccess, VirtualScope}; break; + case 'V': context = {DemangleFunction(classFunctionType, true, m_backrefList, PublicFunctionClass | VirtualFunctionClass), PublicAccess, VirtualScope}; break; + case 'W': context = {DemangleFunction(classFunctionType, true, m_backrefList, PublicFunctionClass | StaticThunkFunctionClass), PublicAccess, ThunkScope }; break; + case 'X': context = {DemangleFunction(classFunctionType, true, m_backrefList, PublicFunctionClass | StaticThunkFunctionClass), PublicAccess, ThunkScope }; break; + case 'Y': context = {DemangleFunction(classFunctionType, false, m_backrefList, GlobalFunctionClass), NoAccess, NoScope }; break; + case 'Z': context = {DemangleFunction(classFunctionType, false, m_backrefList, GlobalFunctionClass), NoAccess, NoScope }; break; + case '$': + { + int funcClass = VirtualThunkFunctionClass; + if (reader.Peek() == 'R') + { + reader.Consume(); + funcClass |= VirtualThunkExFunctionClass; + } + char thunkType = reader.Read(); + switch (thunkType) + { + case '0': context = {DemangleFunction(classFunctionType, true, m_backrefList, funcClass | VirtualFunctionClass | PrivateFunctionClass), PrivateAccess, ThunkScope}; break; + case '1': context = {DemangleFunction(classFunctionType, true, m_backrefList, funcClass | VirtualFunctionClass | PrivateFunctionClass), PrivateAccess, ThunkScope}; break; + case '2': context = {DemangleFunction(classFunctionType, true, m_backrefList, funcClass | VirtualFunctionClass | ProtectedFunctionClass), ProtectedAccess, ThunkScope}; break; + case '3': context = {DemangleFunction(classFunctionType, true, m_backrefList, funcClass | VirtualFunctionClass | ProtectedFunctionClass), ProtectedAccess, ThunkScope}; break; + case '4': context = {DemangleFunction(classFunctionType, true, m_backrefList, funcClass | VirtualFunctionClass | PublicFunctionClass), PublicAccess, ThunkScope}; break; + case '5': context = {DemangleFunction(classFunctionType, true, m_backrefList, funcClass | VirtualFunctionClass | PublicFunctionClass), PublicAccess, ThunkScope}; break; + default: throw DemangleException("Unknown virtual thunk type " + string(1, thunkType)); + } + break; + } + default: throw DemangleException("Unknown function type " + string(1, funcType)); + } + return context; +} + +bool Demangle::DemangleMS(Architecture* arch, const string& mangledName, Ref<Type>& outType, + QualifiedName& outVarName, const Ref<BinaryView>& view) +{ + outType = nullptr; + if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) + return false; + return DemangleMS(arch, mangledName, outType, outVarName); +} + +bool Demangle::DemangleMS(Architecture* arch, const string& mangledName, Ref<Type>& outType, + QualifiedName& outVarName, BinaryView* view) +{ + outType = nullptr; + if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) + return false; + return DemangleMS(arch, mangledName, outType, outVarName); +} + +bool Demangle::DemangleMS(Architecture* arch, const string& mangledName, Ref<Type>& outType, + QualifiedName& outVarName) +{ + outType = nullptr; + if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) + return false; + try + { + Demangle demangle(arch, mangledName); + // For now we're throwing away MemberScope and MemberAccess + outType = demangle.DemangleSymbol().type.Finalize(); + outVarName = demangle.GetVarName(); + + } + catch (DemangleException &e) + { + LogDebug("Demangling Failed '%s' '%s;", mangledName.c_str(), e.what()); + return false; + } + return true; +} + + +bool Demangle::DemangleMS(const string& mangledName, Ref<Type>& outType, + QualifiedName& outVarName, const Ref<BinaryView>& view) +{ + outType = nullptr; + if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) + return false; + try + { + Demangle demangle(view, mangledName); + // For now we're throwing away MemberScope and MemberAccess + outType = demangle.DemangleSymbol().type.Finalize(); + outVarName = demangle.GetVarName(); + + } + catch (DemangleException &e) + { + LogDebug("Demangling Failed '%s' '%s;", mangledName.c_str(), e.what()); + return false; + } + return true; +} + + +class MSDemangler: public Demangler +{ +public: + MSDemangler(): Demangler("MS") + { + } + ~MSDemangler() override {} + + virtual bool IsMangledString(const string& name) override + { + return name[0] == '?'; + } + + virtual bool Demangle(Ref<Architecture> arch, const string& name, Ref<Type>& outType, QualifiedName& outVarName, + Ref<BinaryView> view) override + { + if (view) + return Demangle::DemangleMS(arch, name, outType, outVarName, view); + return Demangle::DemangleMS(arch, name, outType, outVarName); + } +}; + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_EDITION + bool DemangleMSVCPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + static MSDemangler* demangler = new MSDemangler(); + Demangler::Register(demangler); + return true; + } +} diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h new file mode 100644 index 00000000..d52b666a --- /dev/null +++ b/demangler/msvc/demangle_msvc.h @@ -0,0 +1,162 @@ +// 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. + +#pragma once +#include <stdexcept> +#include <exception> +#include <string> +#include <vector> +#include <set> +#include "binaryninjaapi.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(); } +}; + + +class Demangle +{ + enum NameType + { + NameEmpty, + NameString, + NameLookup, + NameBackref, + NameTemplate, + NameConstructor, + NameDestructor, + NameRtti, + NameReturn, + NameDynamicInitializer, + NameDynamicAtExitDestructor, + NameLocalStaticThreadGuard, + NameLocalVftable + }; + + enum FunctionClass + { + NoneFunctionClass = 0, + PrivateFunctionClass = 1 << 0, + ProtectedFunctionClass = 1 << 1, + PublicFunctionClass = 1 << 2, + GlobalFunctionClass = 1 << 3, + StaticFunctionClass = 1 << 4, + VirtualFunctionClass = 1 << 5, + FriendFunctionClass = 1 << 6, + StaticThunkFunctionClass = 1 << 7, + VirtualThunkFunctionClass = 1 << 8, + VirtualThunkExFunctionClass = 1 << 9, + }; + + class Reader + { + public: + Reader(std::string data); + std::string PeekString(size_t count=1); + char Peek(); + const char* GetRaw(); + char Read(); + std::string ReadString(size_t count=1); + std::string ReadUntil(char sentinal); + void Consume(size_t count=1); + size_t Length(); + private: + std::string m_data; + }; + + class BackrefList + { + public: + std::vector<BinaryNinja::TypeBuilder> typeList; + std::vector<std::string> nameList; + const BinaryNinja::TypeBuilder& GetTypeBackref(size_t reference); + std::string GetStringBackref(size_t reference); + void PushTypeBackref(BinaryNinja::TypeBuilder t); + void PushStringBackref(std::string& s); + void PushFrontStringBackref(std::string& s); + }; + + Reader reader; + BackrefList m_backrefList; + BinaryNinja::Architecture* m_arch; + BinaryNinja::Ref<BinaryNinja::Platform> m_platform; + BinaryNinja::Ref<BinaryNinja::BinaryView> m_view; + BinaryNinja::QualifiedName m_varName; + BinaryNinja::Ref<BinaryNinja::Logger> m_logger; + + NameType GetNameType(); + BinaryNinja::TypeBuilder DemangleVarType(BackrefList& varList, bool isReturn, BinaryNinja::QualifiedName& name); + void DemangleNumber(int64_t& num); + void DemangleChar(char& ch); + void DemangleWideChar(uint16_t& wch); + void DemangleModifiers(bool& _const, bool& _volatile, bool& isMember); + std::set<BNPointerSuffix> DemanglePointerSuffix(); + void DemangleVariableList(std::vector<BinaryNinja::FunctionParameter>& paramList, BackrefList& varList); + void DemangleNameTypeRtti(BNNameType& classFunctionType, + BackrefList& nameBackrefList, + std::string& out, + std::string& rttiTypeName); + void DemangleTypeNameLookup(std::string& out, BNNameType& functionType); + void DemangleNameTypeString(std::string& out); + void DemangleNameTypeBackref(std::string& out, const std::vector<std::string>& backrefList); + void DemangleName(BinaryNinja::QualifiedName& nameList, + BNNameType& classFunctionType, + BackrefList& nameBackrefList); + BinaryNinja::Ref<BinaryNinja::CallingConvention> GetCallingConventionForType(BNCallingConventionName ccName); + BNCallingConventionName DemangleCallingConvention(); + BinaryNinja::TypeBuilder DemangleFunction(BNNameType classFunctionType, bool pointerSuffix, BackrefList& varList, int funcClass = NoneFunctionClass); + BinaryNinja::TypeBuilder DemangleData(); + void DemangleNameTypeRtti(BNNameType& classFunctionType, + BackrefList& nameBackrefList, + std::string& out); + BinaryNinja::TypeBuilder DemangleVTable(); + BinaryNinja::TypeBuilder DemanagleRTTI(BNNameType classFunctionType); + std::string DemangleTemplateInstantiationName(BackrefList& nameBackrefList); + std::string DemangleTemplateParams(std::vector<BinaryNinja::FunctionParameter>& params, BackrefList& nameBackrefList, std::string& out); + std::string DemangleUnqualifiedSymbolName(BinaryNinja::QualifiedName& nameList, BackrefList& nameBackrefList, BNNameType& classFunctionType); + BinaryNinja::TypeBuilder DemangleString(); + BinaryNinja::TypeBuilder DemangleTypeInfoName(); + +public: + struct DemangleContext + { + BinaryNinja::TypeBuilder type; + BNMemberAccess access; + BNMemberScope scope; + }; + Demangle(BinaryNinja::Architecture* arch, std::string mangledName); + Demangle(BinaryNinja::Ref<BinaryNinja::BinaryView> view, std::string mangledName); + Demangle(BinaryNinja::Ref<BinaryNinja::Platform> platform, std::string mangledName); + DemangleContext DemangleSymbol(); + BinaryNinja::QualifiedName GetVarName() const { return m_varName; } + + // Be careful not to accidentally implicitly cast a BinaryView* to a bool + static bool DemangleMS(BinaryNinja::Architecture* arch, const std::string& mangledName, BinaryNinja::Ref<BinaryNinja::Type>& outType, + BinaryNinja::QualifiedName& outVarName, const BinaryNinja::Ref<BinaryNinja::BinaryView>& view); + static bool DemangleMS(BinaryNinja::Architecture* arch, const std::string& mangledName, BinaryNinja::Ref<BinaryNinja::Type>& outType, + BinaryNinja::QualifiedName& outVarName, BinaryNinja::BinaryView* view); + static bool DemangleMS(BinaryNinja::Architecture* arch, const std::string& mangledName, BinaryNinja::Ref<BinaryNinja::Type>& outType, + BinaryNinja::QualifiedName& outVarName); + + static bool DemangleMS(const std::string& mangledName, BinaryNinja::Ref<BinaryNinja::Type>& outType, + BinaryNinja::QualifiedName& outVarName, const BinaryNinja::Ref<BinaryNinja::BinaryView>& view); + static bool DemangleMS(const std::string& mangledName, BinaryNinja::Ref<BinaryNinja::Type>& outType, + BinaryNinja::QualifiedName& outVarName, BinaryNinja::BinaryView* view); +}; + diff --git a/python/demangle.py b/python/demangle.py index abc7ebf9..590fb4be 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -19,14 +19,18 @@ # IN THE SOFTWARE. import ctypes +import traceback # Binary Ninja components +import binaryninja from . import _binaryninjacore as core from . import binaryview from . import types -from .architecture import Architecture +from .log import log_error +from .architecture import Architecture, CoreArchitecture from .platform import Platform -from typing import Iterable, List, Optional, Union +from typing import Iterable, List, Optional, Union, Tuple + def get_qualified_name(names: Iterable[str]): """ @@ -46,6 +50,55 @@ def get_qualified_name(names: Iterable[str]): return "::".join(names) +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]]]: + """ + ``demangle_generic`` demangles a mangled symbol 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 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 + :Example: + + >>> demangle_generic(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + (<type: public: static enum Foobar::foo __cdecl (enum Foobar::foo)>, ['Foobar', 'testf']) + >>> demangle_generic(Architecture["x86_64"], "__ZN20ArmCallingConvention27GetIntegerArgumentRegistersEv") + (<type: immutable:FunctionTypeClass 'int64_t()'>, ['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)() + 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): + return None, [mangled_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 + + def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = None) -> Optional[List[str]]: """ ``demangle_llvm`` demangles a mangled name using the LLVM demangler. @@ -225,3 +278,201 @@ def simplify_name_to_qualified_name(input_name: Union[str, types.QualifiedName], return None return result + +class _DemanglerMetaclass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetDemanglerList(count) + try: + for i in range(0, count.value): + yield CoreDemangler(types[i]) + finally: + core.BNFreeDemanglerList(types) + + def __getitem__(self, value): + binaryninja._init_plugins() + handle = core.BNGetDemanglerByName(str(value)) + if handle is None: + raise KeyError(f"'{value}' is not a valid Demangler") + return CoreDemangler(handle) + + +class Demangler(metaclass=_DemanglerMetaclass): + """ + Pluggable name demangling interface. See :py:func:`register` and :py:func:`demangle` + for details on the process of this interface. + + The list of Demanglers can be queried: + + >>> list(Demangler) + [<Demangler: MS>, <Demangler: GNU3>] + """ + + name = None + _registered_demanglers = [] + _cached_name = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNDemangler) + self.__dict__["name"] = core.BNGetDemanglerName(handle) + else: + self.handle = None + + @classmethod + def register(cls): + """ + Register a custom Demangler. Newly registered demanglers will get priority over + previously registered demanglers and built-in demanglers. + """ + demangler = cls() + + assert demangler.__class__.name is not None + assert demangler.handle is None + + demangler._cb = 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.handle = core.BNRegisterDemangler(cls.name, demangler._cb) + cls._registered_demanglers.append(demangler) + + @classmethod + def promote(cls, demangler): + """ + Promote a demangler to the highest-priority position. + + >>> list(Demangler) + [<Demangler: MS>, <Demangler: GNU3>] + >>> Demangler.promote(list(Demangler)[0]) + >>> list(Demangler) + [<Demangler: GNU3>, <Demangler: MS>] + + :param demangler: Demangler to promote + """ + core.BNPromoteDemangler(demangler.handle) + + def __eq__(self, other): + if not isinstance(other, Demangler): + return False + return self.name == other.name + + def __str__(self): + return f'<Demangler: {self.name}>' + + def __repr__(self): + return f'<Demangler: {self.name}>' + + def _is_mangled_string(self, ctxt, name): + try: + return self.is_mangled_string(core.pyNativeStr(name)) + except: + log_error(traceback.format_exc()) + return False + + def _demangle(self, ctxt, arch, name, out_type, out_var_name, view): + 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) + if result is None: + return False + type, var_name = result + + if not isinstance(var_name, types.QualifiedName): + var_name = types.QualifiedName(var_name) + + Demangler._cached_name = var_name._to_core_struct() + if type: + out_type[0] = core.BNNewTypeReference(type.handle) + else: + out_type[0] = None + out_var_name[0] = Demangler._cached_name + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_var_name(self, ctxt, name): + try: + Demangler._cached_name = None + except: + log_error(traceback.format_exc()) + + def is_mangled_string(self, name: str) -> bool: + """ + 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 + :py:func:`demangle` will determine the result of a call to :py:func:`demangle_generic`. + Returning True from this does not require the demangler to succeed the call to + :py:func:`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 + """ + raise NotImplementedError() + + def demangle( + self, + arch: Architecture, + name: str, + view: Optional['binaryview.BinaryView'] = None + ) -> Optional[Tuple['types.Type', 'types.QualifiedName']]: + """ + 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. + + 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 :py:func:`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 :py:func:`is_mangled_string`), and then returns a value from + this function will determine the result of a call to :py:func:`demangle_generic`. + 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, <name>) result, 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 + :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. + """ + raise NotImplementedError() + + +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']]: + 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.BNDemanglerDemangle(self.handle, arch.handle, name, out_type, out_var_name, view_handle): + 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) + return result_type, result_var_name diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3b8c11e2..a3c62e33 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1040,6 +1040,14 @@ dependencies = [ ] [[package]] +name = "test_demangler" +version = "0.1.0" +dependencies = [ + "binaryninja", + "log", +] + +[[package]] name = "thiserror" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 789a4224..7384893e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -36,7 +36,8 @@ members = [ "examples/hlil_lifter", "examples/pdb-ng", "examples/pdb-ng/demo", - "examples/template" + "examples/template", + "examples/test_demangler", ] [profile.release] diff --git a/rust/examples/test_demangler/Cargo.toml b/rust/examples/test_demangler/Cargo.toml new file mode 100644 index 00000000..eb4daa34 --- /dev/null +++ b/rust/examples/test_demangler/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "test_demangler" +version = "0.1.0" +edition = "2021" + +# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core): +[lib] +crate-type = ["cdylib"] + +# You can point at the BinaryNinja dependency in one of two ways, via path: +[dependencies] +binaryninja = {path="../../"} +log = "0.4.21" + +# Or directly at the git repo: +# [dependencies] +# binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"} diff --git a/rust/examples/test_demangler/src/lib.rs b/rust/examples/test_demangler/src/lib.rs new file mode 100644 index 00000000..eaa38dad --- /dev/null +++ b/rust/examples/test_demangler/src/lib.rs @@ -0,0 +1,64 @@ +use log::{info, LevelFilter}; +use binaryninja::architecture::CoreArchitecture; +use binaryninja::binaryview::BinaryView; +use binaryninja::{command, logger}; +use binaryninja::command::Command; +use binaryninja::demangle::{Demangler, CustomDemangler}; +use binaryninja::rc::Ref; +use binaryninja::types::{QualifiedName, Type}; + +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<Ref<BinaryView>>) -> Result<(Option<Ref<Type>>, QualifiedName), ()> { + match name { + "test_name" => Ok((Some(Type::bool()), QualifiedName::from(vec!["test_name"]))), + "test_name2" => Ok((None, QualifiedName::from(vec!["test_name2", "aaa"]))), + _ => Err(()), + } + + } +} + +struct DemangleCommand; + +impl Command for DemangleCommand { + fn action(&self, view: &BinaryView) { + for d in Demangler::list().iter() { + info!("{}", d.name()); + + info!("{}", d.is_mangled_string("__ZN1AC2Ei")); + info!("{:?}", d.demangle( + &CoreArchitecture::by_name("x86_64").expect("x86 exists"), + "__ZN1AC2Ei", + Some(view) + )); + info!("{:?}", d.demangle( + &CoreArchitecture::by_name("x86_64").expect("x86 exists"), + "test_name", + None + )); + info!("{:?}", d.demangle( + &CoreArchitecture::by_name("x86_64").expect("x86 exists"), + "test_name2", + None + )); + } + } + + fn valid(&self, _view: &BinaryView) -> bool { + true + } +} + +#[no_mangle] +pub extern "C" fn CorePluginInit() -> bool { + let _ = logger::init(LevelFilter::Info); + Demangler::register("Test", TestDemangler {}); + command::register("Demangle Test", "Test", DemangleCommand {}); + true +} diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 1b940ff9..8fbc4316 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -17,15 +17,75 @@ use binaryninjacore_sys::*; use std::os::raw::c_char; use std::{ffi::CStr, result}; +use std::ffi::c_void; use crate::architecture::CoreArchitecture; -use crate::string::{BnStrCompatible, BnString}; -use crate::types::Type; +use crate::binaryview::BinaryView; +use crate::string::{BnStrCompatible, BnString, raw_to_string}; +use crate::types::{QualifiedName, Type}; use crate::rc::*; pub type Result<R> = result::Result<R, ()>; +pub fn demangle_generic<S: BnStrCompatible>( + arch: &CoreArchitecture, + mangled_name: S, + view: Option<&BinaryView>, + simplify: bool, +) -> Result<(Option<Ref<Type>>, Vec<String>)> { + let mangled_name_bwn = mangled_name.into_bytes_with_nul(); + let mangled_name_ptr = mangled_name_bwn.as_ref(); + let mut out_type: *mut BNType = std::ptr::null_mut(); + let mut out_name = BNQualifiedName { + name: std::ptr::null_mut(), + join: std::ptr::null_mut(), + nameCount: 0, + }; + let view_ptr = match view { + Some(v) => v.handle, + None => std::ptr::null_mut(), + }; + let res = unsafe { + BNDemangleGeneric( + arch.0, + mangled_name_ptr.as_ptr() as *const c_char, + &mut out_type, + &mut out_name, + view_ptr, + simplify, + ) + }; + + if !res { + let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) { + Ok(cstr) => cstr, + Err(_) => { + log::error!("demangle_generic: failed to parse mangled name"); + return Err(()); + } + }; + return Ok((None, vec![cstr.to_string_lossy().into_owned()])); + } + + let out_type = match out_type.is_null() { + true => { + log::debug!("demangle_generic: out_type is NULL"); + None + } + false => Some(unsafe { Type::ref_from_raw(out_type) }), + }; + + Ok(( + out_type, + QualifiedName(out_name) + .strings() + .iter() + .map(|str| str.to_string()) + .collect::<Vec<_>>() + )) +} + pub fn demangle_llvm<S: BnStrCompatible>( mangled_name: S, simplify: bool, @@ -179,3 +239,197 @@ pub fn demangle_ms<S: BnStrCompatible>( Ok((out_type, names)) } + +#[derive(PartialEq, Eq, Hash)] +pub struct Demangler { + pub(crate) handle: *mut BNDemangler, +} + +impl Demangler { + pub(crate) unsafe fn from_raw(handle: *mut BNDemangler) -> Self { + debug_assert!(!handle.is_null()); + + Self { handle } + } + + pub fn is_mangled_string<S: BnStrCompatible>(&self, name: S) -> bool { + let bytes = name.into_bytes_with_nul(); + unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) } + } + + pub fn demangle<S: BnStrCompatible>( + &self, + arch: &CoreArchitecture, + name: S, + view: Option<&BinaryView>, + ) -> Result<(Option<Ref<Type>>, QualifiedName)> { + let name_bytes = name.into_bytes_with_nul(); + + let mut out_type = std::ptr::null_mut(); + let mut out_var_name = BNQualifiedName { + name: std::ptr::null_mut(), + join: std::ptr::null_mut(), + nameCount: 0, + }; + + let view_ptr = match view { + Some(v) => v.handle, + None => std::ptr::null_mut() + }; + + if !unsafe { BNDemanglerDemangle(self.handle, arch.0, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr) } { + return Err(()); + } + + let var_type = + if out_type.is_null() { + None + } else { + Some(unsafe { Type::ref_from_raw(out_type) }) + }; + let var_name = QualifiedName(out_var_name); + + Ok((var_type, var_name)) + } + + pub fn name(&self) -> BnString { + unsafe { BnString::from_raw(BNGetDemanglerName(self.handle)) } + } + + pub fn from_name<S: BnStrCompatible>(name: S) -> Option<Self> { + let name_bytes = name.into_bytes_with_nul(); + let demangler = unsafe { BNGetDemanglerByName(name_bytes.as_ref().as_ptr() as *const _) }; + if demangler.is_null() { + None + } else { + Some(unsafe { Demangler::from_raw(demangler) }) + } + } + + pub fn list() -> Array<Self> { + let mut count: usize = 0; + let demanglers = unsafe { BNGetDemanglerList(&mut count) }; + unsafe { Array::<Demangler>::new(demanglers, count, ()) } + } + + pub fn register<S, C>(name: S, callbacks: C) -> Self + where + S: BnStrCompatible, + C: CustomDemangler, + { + extern "C" fn cb_is_mangled_string<C>(ctxt: *mut c_void, name: *const c_char) -> bool + where + C: CustomDemangler, + { + ffi_wrap!("CustomDemangler::cb_is_mangled_string", unsafe { + let cmd = &*(ctxt as *const C); + let name = + if let Some(n) = raw_to_string(name) { + n + } else { + return false; + }; + cmd.is_mangled_string(&name) + }) + } + extern "C" fn cb_demangle<C>(ctxt: *mut c_void, arch: *mut BNArchitecture, name: *const c_char, out_type: *mut *mut BNType, out_var_name: *mut BNQualifiedName, view: *mut BNBinaryView) -> bool + where + C: CustomDemangler, + { + ffi_wrap!("CustomDemangler::cb_demangle", unsafe { + let cmd = &*(ctxt as *const C); + let arch = CoreArchitecture::from_raw(arch); + let name = + if let Some(n) = raw_to_string(name) { + n + } else { + return false; + }; + let view = if view.is_null() { + None + } else { + Some(BinaryView::from_raw(BNNewViewReference(view))) + }; + + match cmd.demangle(&arch, &name, view) { + Ok((type_, name)) => { + *out_type = match type_ { + Some(t) => RefCountable::inc_ref(t.as_ref()).handle, + None => std::ptr::null_mut() + }; + // TODO: Need to have a better way for api-owned QNames + (*out_var_name).nameCount = name.0.nameCount; + (*out_var_name).join = BNAllocString(name.0.join); + (*out_var_name).name = BNAllocStringList(name.0.name as *mut *const _, name.0.nameCount); + true + }, + Err(_) => { + false + } + } + }) + } + extern "C" fn cb_free_var_name<C>(_ctxt: *mut c_void, name: *mut BNQualifiedName) + where + C: CustomDemangler, + { + ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe { + BNFreeString((*name).join); + BNFreeStringList((*name).name, (*name).nameCount); + }) + } + + let name = name.into_bytes_with_nul(); + let name_ptr = name.as_ref().as_ptr() as *mut _; + let ctxt = Box::into_raw(Box::new(callbacks)); + + let callbacks = BNDemanglerCallbacks { + context: ctxt as *mut c_void, + isMangledString: Some(cb_is_mangled_string::<C>), + demangle: Some(cb_demangle::<C>), + freeVarName: Some(cb_free_var_name::<C>), + }; + + unsafe { + Demangler::from_raw(BNRegisterDemangler( + name_ptr, + Box::leak(Box::new(callbacks)), + )) + } + } + + pub fn promote(demangler: &Demangler) { + unsafe { BNPromoteDemangler(demangler.handle); } + } +} + +unsafe impl Sync for Demangler {} + +unsafe impl Send for Demangler {} + +impl CoreArrayProvider for Demangler { + type Raw = *mut BNDemangler; + type Context = (); + type Wrapped<'a> = Demangler; +} + +unsafe impl CoreArrayProviderInner for Demangler { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeDemanglerList(raw); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Demangler::from_raw(*raw) + } +} + +pub trait CustomDemangler: 'static + Sync { + fn is_mangled_string(&self, name: &str) -> bool; + + fn demangle( + &self, + arch: &CoreArchitecture, + name: &str, + view: Option<Ref<BinaryView>>, + ) -> Result<(Option<Ref<Type>>, QualifiedName)>; +} diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index b90b18a7..e788b06d 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -2443,28 +2443,20 @@ void ElfView::DefineElfSymbol(BNSymbolType type, const string& incomingName, uin string shortName = rawName; string fullName = rawName; Ref<Type> typeRef = symbolTypeRef; - if (m_arch && IsGNU3MangledString(rawName)) + if (m_arch) { - QualifiedName varName; + QualifiedName demangledName; Ref<Type> demangledType; - if (DemangleGNU3(m_arch, rawName, demangledType, varName, m_simplifyTemplates)) + bool simplify = Settings::Instance()->Get<bool>("analysis.types.templateSimplifier", this); + if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, simplify)) { - shortName = varName.GetString(); + shortName = demangledName.GetString(); fullName = shortName; if (demangledType) fullName += demangledType->GetStringAfterName(); if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) typeRef = demangledType; } - else if (!m_extractMangledTypes && DemangleLLVM(rawName, varName, m_simplifyTemplates)) - { - shortName = varName.GetString(); - fullName = shortName; - } - else - { - m_logger->LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); - } } if (!typeRef && (size > 0 && size <= 8)) diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index d8a8a57f..5a118256 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -2428,30 +2428,23 @@ Ref<Symbol> MachoView::DefineMachoSymbol( string fullName = rawName; Ref<Type> typeRef = symbolTypeRef; - QualifiedName varName; if (m_arch) { - if (IsGNU3MangledString(rawName)) + QualifiedName demangledName; + Ref<Type> demangledType; + bool simplify = Settings::Instance()->Get<bool>("analysis.types.templateSimplifier", this); + if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, simplify)) { - Ref<Type> demangledType; - if (DemangleGNU3(m_arch, rawName, demangledType, varName, m_simplifyTemplates)) - { - shortName = varName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } - else if (!m_extractMangledTypes && DemangleLLVM(rawName, varName, m_simplifyTemplates)) - { - shortName = varName.GetString(); - fullName = shortName; - } - else - { - m_logger->LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); - } + shortName = demangledName.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) + typeRef = demangledType; + } + else + { + m_logger->LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); } } diff --git a/view/pe/coffview.cpp b/view/pe/coffview.cpp index be8cef8e..28103a4a 100644 --- a/view/pe/coffview.cpp +++ b/view/pe/coffview.cpp @@ -1518,22 +1518,23 @@ void COFFView::AddCOFFSymbol(BNSymbolType type, const string& dll, const string& string shortName = rawName; string fullName = rawName; - if (m_arch && name.size() > 0 && name[0] == '?') + if (m_arch && name.size() > 0) { - QualifiedName demangleName; + QualifiedName demangledName; Ref<Type> demangledType; - if (DemangleMS(m_arch, name, demangledType, demangleName, m_simplifyTemplates)) + bool simplify = Settings::Instance()->Get<bool>("analysis.types.templateSimplifier", this); + if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, simplify)) { - shortName = demangleName.GetString(); - fullName = shortName + demangledType->GetStringAfterName(); + shortName = demangledName.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); if (!symbolTypeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) symbolTypeRef = demangledType; } else { - // TODO: This is happening a lot, so figure out why the demangler can't handle symbols like "??_C@_0M@LLLPOAKG@hasChildren@" - // For now, disable the message because it's very noisy for some binaries - DEBUG_COFF(m_logger->LogDebug("COFF: Failed to demangle: '%s'\n", name.c_str())); + m_logger->LogDebug("Failed to demangle: '%s'\n", name.c_str()); } } diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp index 2980d667..e299b0e5 100644 --- a/view/pe/peview.cpp +++ b/view/pe/peview.cpp @@ -2965,49 +2965,22 @@ void PEView::AddPESymbol(BNSymbolType type, const string& dll, const string& nam if (m_arch && name.size() > 0) { - QualifiedName demangleName; + QualifiedName demangledName; Ref<Type> demangledType; - if (name[0] == '?') + bool simplify = Settings::Instance()->Get<bool>("analysis.types.templateSimplifier", this); + if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, simplify)) { - if (DemangleMS(m_arch, name, demangledType, demangleName, m_simplifyTemplates)) - { - shortName = demangleName.GetString(); - fullName = shortName + demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } - else if (!m_extractMangledTypes && DemangleLLVM(rawName, demangleName, m_simplifyTemplates)) - { - shortName = demangleName.GetString(); - fullName = shortName; - } - else - { - m_logger->LogDebug("Failed to demangle: '%s'\n", name.c_str()); - } + shortName = demangledName.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) + typeRef = demangledType; } - else if (IsGNU3MangledString(rawName)) + else { - if (DemangleGNU3(m_arch, rawName, demangledType, demangleName, m_simplifyTemplates)) - { - shortName = demangleName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } - else if (!m_extractMangledTypes && DemangleLLVM(rawName, demangleName, m_simplifyTemplates)) - { - shortName = demangleName.GetString(); - fullName = shortName; - } - else - { - m_logger->LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); - } + m_logger->LogDebug("Failed to demangle: '%s'\n", name.c_str()); } - // Not a mangled string } NameSpace ns(dll); |
