summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2024-09-23 17:15:17 -0400
committerGlenn Smith <glenn@vector35.com>2024-10-17 17:33:52 -0400
commit8149774201203326096099394434871992996628 (patch)
treed968976df167dc74090f61051c41ca30e5cc7c8e
parent5353484086517832418955474757290903351986 (diff)
Demangler plugin API
Closes #467
-rw-r--r--binaryninjaapi.h115
-rw-r--r--binaryninjacore.h45
-rw-r--r--demangle.cpp135
-rw-r--r--demangler/gnu3/CMakeLists.txt40
-rw-r--r--demangler/gnu3/demangle_gnu3.cpp2373
-rw-r--r--demangler/gnu3/demangle_gnu3.h122
-rw-r--r--demangler/msvc/CMakeLists.txt40
-rw-r--r--demangler/msvc/demangle_msvc.cpp1887
-rw-r--r--demangler/msvc/demangle_msvc.h162
-rw-r--r--python/demangle.py255
-rw-r--r--rust/Cargo.lock8
-rw-r--r--rust/Cargo.toml3
-rw-r--r--rust/examples/test_demangler/Cargo.toml17
-rw-r--r--rust/examples/test_demangler/src/lib.rs64
-rw-r--r--rust/src/demangle.rs258
-rw-r--r--view/elf/elfview.cpp18
-rw-r--r--view/macho/machoview.cpp35
-rw-r--r--view/pe/coffview.cpp17
-rw-r--r--view/pe/peview.cpp49
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);