diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 260bfc25a0..2ee7b016c7 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2433,48 +2433,20 @@ namespace BinaryNinja { */ Ref Load(Ref rawData, bool updateAnalysis, ProgressFunction progress, Ref options = new Metadata(MetadataType::KeyValueDataType), bool isDatabase = false); - /*! Attempt to demangle a mangled name, trying all relevant demanglers and using whichever one accepts it - - \see Demangler::Demangle for a discussion on which demangler will be used. - - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. - \param[in] mangledName a mangled Microsoft Visual Studio C++ name - \param[out] outType Pointer to Type to output - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view (Optional) view of the binary containing the mangled name - \param[in] simplify (Optional) Whether to simplify demangled names. - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleGeneric(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - Ref view = nullptr, const bool simplify = false); - /*! Demangles using LLVM's demangler - \param[in] mangledName a mangled (msvc/itanium/rust/dlang) name + \param[in] mangledName a mangled (msvc/gnu3/rust/dlang) name \param[out] outVarName QualifiedName reference to write the output name to. \param[in] simplify Whether to simplify demangled names. \return True if the name was demangled and written to the out* parameters \ingroup demangle */ - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, const bool simplify = false); - - /*! 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] view View to check the analysis.types.templateSimplifier for - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, BinaryView* view); + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, bool simplify = true); /*! Demangles a Microsoft Visual Studio C++ name - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. + \param[in] platform Platform for the symbol. Required for pointer/integer sizes and calling conventions. \param[in] mangledName a mangled Microsoft Visual Studio C++ name \param[out] outType Reference to Type to output \param[out] outVarName QualifiedName reference to write the output name to. @@ -2483,29 +2455,20 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - const bool simplify = false); - - /*! Demangles a Microsoft Visual Studio C++ name + bool DemangleMS(const Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify = true); - This overload will use the view's "analysis.types.templateSimplifier" setting - to determine whether to simplify the mangled name. + /*! Determines if a symbol name is a mangled Microsoft Visual Studio C++ name - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. - \param[in] mangledName a mangled Microsoft Visual Studio C++ name - \param[out] outType Reference to Type to output - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view View to check the analysis.types.templateSimplifier for - \return True if the name was demangled and written to the out* parameters + \param[in] mangledName a potentially mangled name \ingroup demangle */ - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - BinaryView* view); + bool IsMSVCMangledString(const std::string& mangledName); /*! Demangles a GNU3 name - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. + \param[in] platform Platform for the symbol. Required for pointer/integer sizes and calling conventions. \param[in] mangledName a mangled GNU3 name \param[out] outType Reference to Type to output \param[out] outVarName QualifiedName reference to write the output name to. @@ -2514,25 +2477,8 @@ namespace BinaryNinja { \ingroup demangle */ - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, - QualifiedName& outVarName, const bool simplify = false); - - /*! Demangles a GNU3 name - - This overload will use the view's "analysis.types.templateSimplifier" setting - to determine whether to simplify the mangled name. - - \param[in] arch Architecture for the symbol. Required for pointer and integer sizes. - \param[in] mangledName a mangled GNU3 name - \param[out] outType Reference to Type to output - \param[out] outVarName QualifiedName reference to write the output name to. - \param[in] view View to check the analysis.types.templateSimplifier for - \return True if the name was demangled and written to the out* parameters - - \ingroup demangle - */ - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view); + bool DemangleGNU3(const Platform* platform, const std::string& mangledName, Ref& outType, + QualifiedName& outVarName, bool simplify = true); /*! Determines if a symbol name is a mangled GNU3 name @@ -2542,25 +2488,7 @@ namespace BinaryNinja { */ bool IsGNU3MangledString(const std::string& mangledName); - /*! - \ingroup demangle - */ - std::string SimplifyToString(const std::string& input); - - /*! - \ingroup demangle - */ - std::string SimplifyToString(const QualifiedName& input); - - /*! - \ingroup demangle - */ - QualifiedName SimplifyToQualifiedName(const std::string& input, bool simplify); - - /*! - \ingroup demangle - */ - QualifiedName SimplifyToQualifiedName(const QualifiedName& input); + QualifiedName SimplifyDemangledTemplateName(const QualifiedName& name); /*! \ingroup mainthread @@ -4831,6 +4759,31 @@ namespace BinaryNinja { static QualifiedName FromAPIObject(const BNQualifiedName* name); }; + struct DemanglerConfig + { + Ref platform; + Ref view; + bool simplifyTemplates = false; + + static DemanglerConfig Default(); + static DemanglerConfig ForPlatform(Platform* platform, bool simplifyTemplates = false); + static DemanglerConfig ForBinaryView(BinaryView* view); + static DemanglerConfig FromAPIObject(const BNDemanglerConfig* config); + + Platform& GetPlatform() const; + BNDemanglerConfig ToAPIObject() const; + }; + + struct DemanglerResult + { + QualifiedName name; + Ref type; + + static DemanglerResult FromAPIObject(const BNDemanglerResult* result); + static DemanglerResult FromAPIObjectAndFree(BNDemanglerResult* result); + BNDemanglerResult ToAPIObject() const; + }; + /*! \ingroup namelist @@ -20700,38 +20653,6 @@ namespace BinaryNinja { static int Compare(LinearViewCursor* a, LinearViewCursor* b); }; - /*! - - \ingroup simplifyname - */ - class SimplifyName - { - public: - // Use these functions to interface with the simplifier - static std::string to_string(const std::string& input); - static std::string to_string(const QualifiedName& input); - static QualifiedName to_qualified_name(const std::string& input, bool simplify); - static QualifiedName to_qualified_name(const QualifiedName& input); - - // Below is everything for the above APIs to work - enum SimplifierDest - { - str, - fqn - }; - - SimplifyName(const std::string&, const SimplifierDest, const bool); - ~SimplifyName(); - - operator std::string() const; - operator QualifiedName(); - - private: - const char* m_rust_string; - const char** m_rust_array; - uint64_t m_length; - }; - struct FindParameters { BNFindType type; @@ -22544,36 +22465,54 @@ namespace BinaryNinja { */ class Demangler: public StaticCoreRefCountObject { + public: + using Config = DemanglerConfig; + using Result = DemanglerResult; + + private: std::string m_nameForRegister; protected: - explicit Demangler(const std::string& name); + explicit Demangler(std::string demanglerName); Demangler(BNDemangler* demangler); virtual ~Demangler() = default; - static bool IsMangledStringCallback(void* ctxt, const char* name); - static bool DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view); - static void FreeVarNameCallback(void* ctxt, BNQualifiedName* name); + static bool IsMangledStringCallback(void* ctxt, const char* mangledName); + static bool DemangleCallback(void* ctxt, const char* mangledName, const BNDemanglerConfig* config, + BNDemanglerResult* result); + static void FreeResultCallback(void* ctxt, BNDemanglerResult* result); public: - /*! Register a custom Demangler. Newly registered demanglers will get priority over + /*! Register a custom Demangler. Newly registered demanglers get priority over previously registered demanglers and built-in demanglers. + + \return True if registration succeeded; false if the demangler was invalid. */ - static void Register(Demangler* demangler); + static bool Register(Demangler* demangler); /*! Get the list of currently registered demanglers, sorted by lowest to highest priority. \return List of demanglers */ static std::vector> GetList(); - static Ref GetByName(const std::string& name); + static Ref GetByName(const std::string& demanglerName); /*! Promote a demangler to the highest-priority position. \param demangler Demangler to promote + \return True if promotion succeeded; false if the demangler was invalid or not registered. */ - static void Promote(Ref demangler); + static bool Promote(const Ref& demangler); + + /*! + Attempt to demangle a mangled name, trying all relevant demanglers and using whichever one accepts it. + + \param[in] mangledName Raw mangled name + \param[in] config Platform/view/options used while demangling + \return Demangled type/name if successful + */ + static std::optional DemangleAny( + const std::string& mangledName, const Config& config = DemanglerConfig::Default()); std::string GetName() const; @@ -22581,40 +22520,30 @@ namespace BinaryNinja { The most recently registered demangler that claims a name is a mangled string (returns true from this function), and then returns a value from Demangle will - determine the result of a call to DemangleGeneric. Returning True from this + determine the result of a call to DemangleAny. Returning True from this does not require the demangler to succeed the call to Demangle, but simply implies that it may succeed. - \param name Raw mangled name string + \param mangledName Raw mangled name string \return True if the demangler thinks it can handle the name */ - virtual bool IsMangledString(const std::string& name) = 0; + virtual bool IsMangledString(const std::string& mangledName) = 0; /*! Demangle a raw name into a Type and QualifiedName. - Any unresolved named types referenced by the resulting Type will be created as - empty structures or void typedefs in the view, if the result is used on - a data structure in the view. Given this, the call to Demangle should NOT - cause any side-effects creating types in the view trying to resolve this - and instead just return a type with unresolved named type references. - The most recently registered demangler that claims a name is a mangled string (returns true from IsMangledString), and then returns a value from - this function will determine the result of a call to DemangleGeneric. + this function will determine the result of a call to DemangleAny. If this call returns None, the next most recently used demangler(s) will be tried instead. If the mangled name has no type information, but a name is still possible to extract, this function may return a successful result with outType=nullptr, which will be accepted. - \param arch Architecture for context in which the name exists, eg for pointer sizes - \param name Raw mangled name - \param outType Resulting type, if one can be deduced, will be written here. Otherwise nullptr will be written - \param outVarName Resulting variable name - \param view (Optional) BinaryView context in which the name exists, eg for type lookup - \return True if demangling was successful and results were stored into out-parameters + \param mangledName Raw mangled name + \param config Platform/view/options used while demangling + \return Demangled type/name if successful */ - virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view = nullptr) = 0; + virtual std::optional Demangle(const std::string& mangledName, const Config& config) = 0; }; /*! @@ -22627,8 +22556,7 @@ namespace BinaryNinja { virtual ~CoreDemangler() = default; virtual bool IsMangledString(const std::string& name); - virtual bool Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view); + virtual std::optional Demangle(const std::string& name, const Config& config); }; namespace Unicode diff --git a/binaryninjacore.h b/binaryninjacore.h index 225cb437db..035df4b719 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 176 +#define BN_CURRENT_CORE_ABI_VERSION 177 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 176 +#define BN_MINIMUM_CORE_ABI_VERSION 177 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -3898,13 +3898,27 @@ extern "C" void (*freeConflictList)(void* context, BNAnalysisMergeConflict** conflictList, size_t count); } BNAnalysisMergeConflictSplitterCallbacks; + typedef struct BNDemanglerConfig + { + BNPlatform* platform; + BNBinaryView* view; + bool simplifyTemplates; + } BNDemanglerConfig; + + typedef struct BNDemanglerResult + { + BNQualifiedName name; + BNType* type; + } BNDemanglerResult; + typedef struct BNDemanglerCallbacks { + size_t size; void* context; bool (*isMangledString)(void* ctxt, const char* name); - bool (*demangle)(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view); - void (*freeVarName)(void* ctxt, BNQualifiedName* name); + bool (*demangle)(void* ctxt, const char* name, const BNDemanglerConfig* config, + BNDemanglerResult* result); + void (*freeResult)(void* ctxt, BNDemanglerResult* result); } BNDemanglerCallbacks; BN_ENUM(uint8_t, BNScopeType) @@ -8246,37 +8260,32 @@ extern "C" BINARYNINJACOREAPI void BNUpdateReportFlowGraph(BNReportCollection* reports, size_t i, BNFlowGraph* graph); // Demangler - BINARYNINJACOREAPI bool BNDemangleMS(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - BINARYNINJACOREAPI bool BNDemangleMSWithOptions(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); - BINARYNINJACOREAPI bool BNDemangleMSPlatform(BNPlatform* platform, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - - BINARYNINJACOREAPI bool BNIsGNU3MangledString(const char* mangledName); - BINARYNINJACOREAPI bool BNDemangleGNU3(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - BINARYNINJACOREAPI bool BNDemangleGNU3WithOptions(BNArchitecture* arch, const char* mangledName, BNType** outType, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); - BINARYNINJACOREAPI void BNFreeDemangledName(char*** name, size_t nameElements); - - BINARYNINJACOREAPI bool BNDemangleLLVM(const char* mangledName, - char*** outVarName, size_t* outVarNameElements, const bool simplify); - BINARYNINJACOREAPI bool BNDemangleLLVMWithOptions(const char* mangledName, - char*** outVarName, size_t* outVarNameElements, const BNBinaryView* const view); - - BINARYNINJACOREAPI BNDemangler* BNRegisterDemangler(const char* name, BNDemanglerCallbacks* callbacks); +#define BN_DEMANGLER_MSVC "msvc" +#define BN_DEMANGLER_GNU3 "gnu3" +#define BN_DEMANGLER_LLVM "llvm" + + BINARYNINJACOREAPI BNDemanglerConfig BNGetDefaultDemanglerConfig(void); + BINARYNINJACOREAPI BNDemanglerConfig BNGetDemanglerConfigForPlatform(BNPlatform* platform, + bool simplifyTemplates); + BINARYNINJACOREAPI BNDemanglerConfig BNGetDemanglerConfigForBinaryView(BNBinaryView* view); + BINARYNINJACOREAPI bool BNDemangle(const char* name, const BNDemanglerConfig* config, + BNDemanglerResult* result); + BINARYNINJACOREAPI bool BNDemangleWithDemangler(const BNDemangler* demangler, const char* name, + const BNDemanglerConfig* config, BNDemanglerResult* result); + BINARYNINJACOREAPI void BNFreeDemanglerResult(BNDemanglerResult* result); + BINARYNINJACOREAPI bool BNSimplifyDemangledTemplateName( + const BNQualifiedName* name, BNQualifiedName* result); + + BINARYNINJACOREAPI BNDemangler* BNRegisterDemangler(const char* name, const BNDemanglerCallbacks* callbacks); BINARYNINJACOREAPI BNDemangler** BNGetDemanglerList(size_t* count); 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); + BINARYNINJACOREAPI BNDemangler* BNGetMSVCDemangler(void); + BINARYNINJACOREAPI BNDemangler* BNGetGNU3Demangler(void); + BINARYNINJACOREAPI BNDemangler* BNGetLLVMDemangler(void); + BINARYNINJACOREAPI char* BNGetDemanglerName(const BNDemangler* demangler); + BINARYNINJACOREAPI bool BNPromoteDemangler(const BNDemangler* demangler); + BINARYNINJACOREAPI bool BNIsDemanglerMangledName(const BNDemangler* demangler, const char* name); // Plugin repository APIs BINARYNINJACOREAPI char** BNPluginGetApis(BNPlugin* p, size_t* count); @@ -8637,9 +8646,6 @@ extern "C" BINARYNINJACOREAPI uint32_t BNGetAddressRenderedWidth(uint64_t addr); - BINARYNINJACOREAPI BNQualifiedName BNRustSimplifyStrToFQN(const char* const, bool); - BINARYNINJACOREAPI char* BNRustSimplifyStrToStr(const char* const); - BINARYNINJACOREAPI BNDebugInfoParser* BNRegisterDebugInfoParser(const char* name, bool (*isValid)(void*, BNBinaryView*), bool (*parseInfo)(void*, BNDebugInfo*, BNBinaryView*, BNBinaryView*, BNProgressFunction, void*), diff --git a/demangle.cpp b/demangle.cpp index faf27ac21e..ac07c4c85d 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -1,145 +1,182 @@ #include "binaryninjaapi.h" #include +#include using namespace std; using namespace BinaryNinja; -namespace BinaryNinja { - bool DemangleGeneric(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view, bool simplify) +namespace { + std::optional DemangleWithDemangler( + const BNDemangler* demangler, const Platform* platform, const std::string& mangledName, bool simplify) { - BNType* apiType = nullptr; - BNQualifiedName apiVarName; - bool success = BNDemangleGeneric( - arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr, simplify); + if (!demangler) + return std::nullopt; - if (!success) - return false; + BNDemanglerConfig apiConfig(platform ? platform->GetObject() : nullptr, nullptr, simplify); + BNDemanglerResult apiResult = {}; + if (!BNDemangleWithDemangler(demangler, mangledName.c_str(), &apiConfig, &apiResult)) + return std::nullopt; - outType = apiType ? new Type(apiType) : nullptr; - outVarName = QualifiedName::FromAPIObject(&apiVarName); - BNFreeQualifiedName(&apiVarName); + return DemanglerResult::FromAPIObjectAndFree(&apiResult); + } + + bool StoreDemangleResult(std::optional&& result, Ref& outType, QualifiedName& outVarName) + { + if (!result) + return false; + outType = std::move(result->type); + outVarName = result->name; return true; } +} - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, - BinaryView* view) +namespace BinaryNinja +{ + DemanglerConfig DemanglerConfig::FromAPIObject(const BNDemanglerConfig* config) { - const bool simplify = Settings::Instance()->Get("analysis.types.templateSimplifier", view); - return DemangleLLVM(mangledName, outVarName, simplify); + DemanglerConfig result; + if (!config) + return Default(); + + result.platform = config->platform ? new CorePlatform(BNNewPlatformReference(config->platform)) : Default().platform; + result.view = config->view ? new BinaryView(BNNewViewReference(config->view)) : nullptr; + result.simplifyTemplates = config->simplifyTemplates; + return result; } - bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, - const bool simplify) + + DemanglerConfig DemanglerConfig::Default() { - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleLLVM(mangledName.c_str(), &localVarName, &localSize, simplify)) - return false; - for (size_t i = 0; i < localSize; i++) - { - outVarName.push_back(localVarName[i]); - } - BNFreeDemangledName(&localVarName, localSize); - return true; + BNDemanglerConfig config = BNGetDefaultDemanglerConfig(); + static auto cfg = FromAPIObject(&config); + return cfg; } - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - BinaryView* view) + DemanglerConfig DemanglerConfig::ForPlatform(Platform* platform, bool simplifyTemplates) { - BNType* localType = nullptr; - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleMSWithOptions(arch->GetObject(), mangledName.c_str(), &localType, &localVarName, &localSize, - view ? view->GetObject() : nullptr)) - return false; - outType = localType ? new Type(localType) : nullptr; - for (size_t i = 0; i < localSize; i++) - { - outVarName.push_back(localVarName[i]); - } - BNFreeDemangledName(&localVarName, localSize); - return true; + return {platform ? platform : Default().platform.GetPtr(), nullptr, simplifyTemplates}; } - bool DemangleMS(Architecture* arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - const bool simplify) + DemanglerConfig DemanglerConfig::ForBinaryView(BinaryView* view) { - BNType* localType = nullptr; - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleMS(arch->GetObject(), mangledName.c_str(), &localType, &localVarName, &localSize, simplify)) - return false; - outType = localType ? new Type(localType) : nullptr; - for (size_t i = 0; i < localSize; i++) + if (!view) + return Default(); + + Ref platform = view->GetDefaultPlatform(); + if (!platform) { - outVarName.push_back(localVarName[i]); + if (auto arch = view->GetDefaultArchitecture()) + platform = arch->GetStandalonePlatform(); } - BNFreeDemangledName(&localVarName, localSize); - return true; + if (!platform) + platform = Default().platform; + + return {platform, view, + Settings::Instance()->Get("analysis.types.templateSimplifier", view)}; } - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - BinaryView* view) + Platform& DemanglerConfig::GetPlatform() const { - const bool simplify = Settings::Instance()->Get("analysis.types.templateSimplifier", view); - return DemangleGNU3(arch, mangledName, outType, outVarName, simplify); + if (platform) + return *platform; + return *Default().platform; } - bool DemangleGNU3(Ref arch, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, - const bool simplify) + BNDemanglerConfig DemanglerConfig::ToAPIObject() const { - BNType* localType = nullptr; - char** localVarName = nullptr; - size_t localSize = 0; - if (!BNDemangleGNU3(arch->GetObject(), mangledName.c_str(), &localType, &localVarName, &localSize, simplify)) - return false; - outType = localType ? new Type(localType) : nullptr; - outVarName.clear(); - for (size_t i = 0; i < localSize; i++) - { - outVarName.push_back(localVarName[i]); - } - BNFreeDemangledName(&localVarName, localSize); - return true; + return { + GetPlatform().GetObject(), + view ? view->GetObject() : nullptr, + simplifyTemplates, + }; } + DemanglerResult DemanglerResult::FromAPIObject(const BNDemanglerResult* apiResult) + { + DemanglerResult result; + if (!apiResult) + return result; - bool IsGNU3MangledString(const std::string& mangledName) + result.name = QualifiedName::FromAPIObject(&apiResult->name); + if (apiResult->type) + result.type = new Type(BNNewTypeReference(apiResult->type)); + else + result.type = nullptr; + return result; + } + + DemanglerResult DemanglerResult::FromAPIObjectAndFree(BNDemanglerResult* apiResult) + { + DemanglerResult result = FromAPIObject(apiResult); + BNFreeDemanglerResult(apiResult); + return result; + } + + BNDemanglerResult DemanglerResult::ToAPIObject() const { - return BNIsGNU3MangledString(mangledName.c_str()); + return { + name.GetAPIObject(), + type ? BNNewTypeReference(type->m_object) : nullptr, + }; + } + + bool DemangleLLVM(const std::string& mangledName, QualifiedName& outVarName, + bool simplify) + { + Ref outType; + return StoreDemangleResult( + DemangleWithDemangler(BNGetLLVMDemangler(), nullptr, mangledName, simplify), outType, outVarName); } - string SimplifyToString(const string& input) + bool DemangleMS(const Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify) { - return BNRustSimplifyStrToStr(input.c_str()); + return StoreDemangleResult( + DemangleWithDemangler(BNGetMSVCDemangler(), platform, mangledName, simplify), outType, + outVarName); } - string SimplifyToString(const QualifiedName& input) + bool DemangleGNU3(const Platform* platform, const std::string& mangledName, Ref& outType, QualifiedName& outVarName, + bool simplify) { - return BNRustSimplifyStrToStr(input.GetString().c_str()); + outVarName.clear(); + return StoreDemangleResult( + DemangleWithDemangler(BNGetGNU3Demangler(), platform, mangledName, simplify), outType, + outVarName); } - QualifiedName SimplifyToQualifiedName(const string& input, bool simplify) + bool IsMSVCMangledString(const std::string& mangledName) { - BNQualifiedName name = BNRustSimplifyStrToFQN(input.c_str(), simplify); - QualifiedName result = QualifiedName::FromAPIObject(&name); - BNFreeQualifiedName(&name); - return result; + BNDemangler* demangler = BNGetMSVCDemangler(); + return demangler && BNIsDemanglerMangledName(demangler, mangledName.c_str()); } - QualifiedName SimplifyToQualifiedName(const QualifiedName& input) + bool IsGNU3MangledString(const std::string& mangledName) { - BNQualifiedName name = BNRustSimplifyStrToFQN(input.GetString().c_str(), true); - QualifiedName result = QualifiedName::FromAPIObject(&name); - BNFreeQualifiedName(&name); + BNDemangler* demangler = BNGetGNU3Demangler(); + return demangler && BNIsDemanglerMangledName(demangler, mangledName.c_str()); + } + + QualifiedName SimplifyDemangledTemplateName(const QualifiedName& name) + { + BNQualifiedName apiName = name.GetAPIObject(); + BNQualifiedName apiResult = {}; + if (!BNSimplifyDemangledTemplateName(&apiName, &apiResult)) + { + QualifiedName::FreeAPIObject(&apiName); + return name; + } + + QualifiedName result = QualifiedName::FromAPIObject(&apiResult); + QualifiedName::FreeAPIObject(&apiName); + BNFreeQualifiedName(&apiResult); return result; } - Demangler::Demangler(const std::string& name): m_nameForRegister(name) + Demangler::Demangler(std::string demanglerName): m_nameForRegister(std::move(demanglerName)) { } @@ -148,52 +185,57 @@ namespace BinaryNinja { m_object = demangler; } - bool Demangler::IsMangledStringCallback(void* ctxt, const char* name) + bool Demangler::IsMangledStringCallback(void* ctxt, const char* mangledName) { - Demangler* demangler = (Demangler*)ctxt; - return demangler->IsMangledString(name); + auto demangler = static_cast(ctxt); + return demangler->IsMangledString(mangledName); } - bool Demangler::DemangleCallback(void* ctxt, BNArchitecture* arch, const char* name, BNType** outType, - BNQualifiedName* outVarName, BNBinaryView* view) + bool Demangler::DemangleCallback(void* ctxt, const char* mangledName, const BNDemanglerConfig* config, + BNDemanglerResult* result) { - Demangler* demangler = (Demangler*)ctxt; + auto demangler = static_cast(ctxt); - Ref apiArch = new CoreArchitecture(arch); - Ref apiView = view ? new BinaryView(BNNewViewReference(view)) : nullptr; - - Ref apiType; - QualifiedName apiVarName; - bool success = demangler->Demangle(apiArch, name, apiType, apiVarName, apiView); - if (!success) + if (!mangledName || !result) return false; - if (apiType) - { - *outType = BNNewTypeReference(apiType->m_object); - } - else - { - *outType = nullptr; - } - *outVarName = apiVarName.GetAPIObject(); + auto demangleResult = demangler->Demangle(mangledName, DemanglerConfig::FromAPIObject(config)); + if (!demangleResult) + return false; + *result = demangleResult->ToAPIObject(); return true; } - void Demangler::FreeVarNameCallback(void* ctxt, BNQualifiedName* name) + void Demangler::FreeResultCallback(void* ctxt, BNDemanglerResult* result) { - QualifiedName::FreeAPIObject(name); + BNFreeDemanglerResult(result); } - void Demangler::Register(Demangler* demangler) + bool Demangler::Register(Demangler* demangler) { - BNDemanglerCallbacks cb; - cb.context = (void*)demangler; + if (!demangler) + return false; + + BNDemanglerCallbacks cb = {}; + cb.size = sizeof(cb); + cb.context = reinterpret_cast(demangler); cb.isMangledString = IsMangledStringCallback; cb.demangle = DemangleCallback; - cb.freeVarName = FreeVarNameCallback; - demangler->m_object = BNRegisterDemangler(demangler->m_nameForRegister.c_str(), &cb); + cb.freeResult = FreeResultCallback; + BNDemangler* object = BNRegisterDemangler(demangler->m_nameForRegister.c_str(), &cb); + if (!object) + return false; + + demangler->m_object = object; + return true; + } + + bool Demangler::Promote(const Ref& demangler) + { + if (!demangler || !demangler->m_object) + return false; + return BNPromoteDemangler(demangler->m_object); } std::vector> Demangler::GetList() @@ -208,17 +250,22 @@ namespace BinaryNinja { return result; } - Ref Demangler::GetByName(const std::string& name) + Ref Demangler::GetByName(const std::string& demanglerName) { - BNDemangler* result = BNGetDemanglerByName(name.c_str()); + BNDemangler* result = BNGetDemanglerByName(demanglerName.c_str()); if (!result) return nullptr; return new CoreDemangler(result); } - void Demangler::Promote(Ref demangler) + std::optional Demangler::DemangleAny(const std::string& mangledName, const Config& config) { - BNPromoteDemangler(demangler->m_object); + BNDemanglerConfig apiConfig = config.ToAPIObject(); + BNDemanglerResult apiResult = {}; + if (!BNDemangle(mangledName.c_str(), &apiConfig, &apiResult)) + return std::nullopt; + + return Result::FromAPIObjectAndFree(&apiResult); } std::string Demangler::GetName() const @@ -238,20 +285,15 @@ namespace BinaryNinja { return BNIsDemanglerMangledName(m_object, name.c_str()); } - bool CoreDemangler::Demangle(Ref arch, const std::string& name, Ref& outType, - QualifiedName& outVarName, Ref view) + std::optional CoreDemangler::Demangle(const std::string& name, const Config& config) { - BNType* apiType = nullptr; - BNQualifiedName apiVarName; - bool success = BNDemanglerDemangle( - m_object, arch->m_object, name.c_str(), &apiType, &apiVarName, view ? view->m_object : nullptr); + BNDemanglerConfig apiConfig = config.ToAPIObject(); + BNDemanglerResult apiResult = {}; + bool success = BNDemangleWithDemangler(m_object, name.c_str(), &apiConfig, &apiResult); if (!success) - return false; + return std::nullopt; - outType = apiType ? new Type(apiType) : nullptr; - outVarName = QualifiedName::FromAPIObject(&apiVarName); - BNFreeQualifiedName(&apiVarName); - return true; + return Result::FromAPIObjectAndFree(&apiResult); } } // namespace BinaryNinja diff --git a/demangler/demangled_log.h b/demangler/demangled_log.h new file mode 100644 index 0000000000..bdc0e69263 --- /dev/null +++ b/demangler/demangled_log.h @@ -0,0 +1,77 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "demangler/demangled_type_node.h" + + +class DemangleLogIndentationScope; + +class DemangleLogIndentation +{ +public: + [[nodiscard]] static _STD_STRING Prefix() + { + return _STD_STRING(Depth(), ' '); + } + + [[nodiscard]] static _STD_STRING Apply(const char* format) + { + _STD_STRING indentedFormat = Prefix(); + indentedFormat += format; + return indentedFormat; + } + +private: + friend class DemangleLogIndentationScope; + + static size_t& Depth() + { + static thread_local size_t depth = 0; + return depth; + } + + static void Push() + { + Depth()++; + } + + static void Pop() + { + size_t& depth = Depth(); + if (depth > 0) + depth--; + } +}; + + +class DemangleLogIndentationScope +{ +public: + DemangleLogIndentationScope() + { + DemangleLogIndentation::Push(); + } + + ~DemangleLogIndentationScope() + { + DemangleLogIndentation::Pop(); + } + + DemangleLogIndentationScope(const DemangleLogIndentationScope&) = delete; + DemangleLogIndentationScope(DemangleLogIndentationScope&&) = delete; + DemangleLogIndentationScope& operator=(const DemangleLogIndentationScope&) = delete; + DemangleLogIndentationScope& operator=(DemangleLogIndentationScope&&) = delete; +}; diff --git a/demangler/demangled_reader.h b/demangler/demangled_reader.h new file mode 100644 index 0000000000..4b669e0e10 --- /dev/null +++ b/demangler/demangled_reader.h @@ -0,0 +1,243 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "demangler/demangled_type_node.h" + +#include +#include +#include +#include +#include +#include + + +class DemangleException: public std::exception +{ + _STD_STRING m_message; + +public: + DemangleException(_STD_STRING msg = "Attempt to read beyond bounds or missing expected character"): + m_message(std::move(msg)) + {} + + [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } +}; + + +template +class DemangleNestingGuard +{ + size_t& m_depth; + size_t m_maxDepth; + +public: + explicit DemangleNestingGuard(size_t& depth, size_t maxDepth = MaxDepth): + m_depth(depth), + m_maxDepth(maxDepth) + { + m_depth++; + if (m_depth > m_maxDepth) + { + m_depth--; + throw DemangleException("Detected adversarial mangled string"); + } + } + + ~DemangleNestingGuard() + { + m_depth--; + } + + DemangleNestingGuard(const DemangleNestingGuard&) = delete; + DemangleNestingGuard(DemangleNestingGuard&&) = delete; + DemangleNestingGuard& operator=(const DemangleNestingGuard&) = delete; + DemangleNestingGuard& operator=(DemangleNestingGuard&&) = delete; +}; + + +class DemangleReader +{ + const char* m_begin = nullptr; + const char* m_ptr = nullptr; + const char* m_end = nullptr; + size_t m_maxReadStringLength = std::numeric_limits::max(); + bool m_throwOnPeekPastEnd = true; + + void ValidatePrintableAscii() const + { + for (const char* p = m_begin; p < m_end; p++) + if (*p < 0x20 || *p > 0x7e) + throw DemangleException(); + } + +public: + DemangleReader() = default; + + explicit DemangleReader(const _STD_STRING& data, + size_t maxReadStringLength = std::numeric_limits::max(), + bool throwOnPeekPastEnd = true): + m_maxReadStringLength(maxReadStringLength), + m_throwOnPeekPastEnd(throwOnPeekPastEnd) + { + Reset(data); + } + + void Reset(const _STD_STRING& data) + { + m_begin = data.c_str(); + m_ptr = m_begin; + m_end = m_begin + data.size(); + ValidatePrintableAscii(); + } + + [[nodiscard]] size_t Length() const { return static_cast(m_end - m_ptr); } + [[nodiscard]] size_t GetOffset() const { return static_cast(m_ptr - m_begin); } + + void SetOffset(size_t offset) + { + size_t length = static_cast(m_end - m_begin); + m_ptr = m_begin + std::min(offset, length); + } + + void UnRead(size_t count = 1) + { + if (count <= GetOffset()) + m_ptr -= count; + } + + [[nodiscard]] bool PeekMatch(const char* str, size_t len) const + { + if (len > Length()) + return false; + return memcmp(m_ptr, str, len) == 0; + } + + template + [[nodiscard]] bool PeekMatch(const char (&str)[N]) const + { + return PeekMatch(str, N - 1); + } + + [[nodiscard]] _STD_STRING PeekString(size_t count = 1) const + { + if (count > Length()) + return "\0"; + return _STD_STRING(m_ptr, count); + } + + [[nodiscard]] char PeekAt(size_t offset) const + { + if (offset >= Length()) + throw DemangleException(); + return m_ptr[offset]; + } + + [[nodiscard]] char Peek() const + { + if (m_ptr >= m_end) + { + if (m_throwOnPeekPastEnd) + throw DemangleException(); + return '\0'; + } + return *m_ptr; + } + + [[nodiscard]] char PeekOr(char fallback = '\0') const + { + if (Length() == 0) + return fallback; + return *m_ptr; + } + + [[nodiscard]] const char* GetRaw() const { return m_ptr; } + + void SetRaw(const char* p) + { + if (p < m_begin || p > m_end) + throw DemangleException(); + m_ptr = p; + } + + [[nodiscard]] char Read() + { + if (m_ptr >= m_end) + throw DemangleException(); + return *m_ptr++; + } + + _STD_STRING ReadString(size_t count = 1) + { + if (count > Length()) + throw DemangleException(); + if (count > m_maxReadStringLength) + throw DemangleException("Demangled node exceeds maximum length"); + _STD_STRING out(m_ptr, count); + m_ptr += count; + return out; + } + + std::string_view ReadStringView(size_t count = 1) + { + if (count > Length()) + throw DemangleException(); + if (count > m_maxReadStringLength) + throw DemangleException("Demangled node exceeds maximum length"); + std::string_view out(m_ptr, count); + m_ptr += count; + return out; + } + + _STD_STRING ReadUntil(char sentinel) + { + const char* found = static_cast(memchr(m_ptr, sentinel, m_end - m_ptr)); + if (!found) + throw DemangleException(); + size_t count = static_cast(found - m_ptr); + _STD_STRING out = ReadString(count); + Consume(); // sentinel + return out; + } + + bool ConsumeIf(char ch) + { + if (PeekOr() != ch) + return false; + Consume(); + return true; + } + + bool ConsumeIf(const char* str, size_t len) + { + if (!PeekMatch(str, len)) + return false; + Consume(len); + return true; + } + + template + bool ConsumeIf(const char (&str)[N]) + { + return ConsumeIf(str, N - 1); + } + + void Consume(size_t count = 1) + { + if (count > Length()) + throw DemangleException(); + m_ptr += count; + } +}; diff --git a/demangler/demangled_template_simplifier.cpp b/demangler/demangled_template_simplifier.cpp new file mode 100644 index 0000000000..2f2d7f13bb --- /dev/null +++ b/demangler/demangled_template_simplifier.cpp @@ -0,0 +1,2442 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "demangled_template_simplifier.h" + +#include +#include +#include +#include + +#ifdef BINARYNINJACORE_LIBRARY +using namespace BinaryNinjaCore; +#else +using namespace BinaryNinja; +using namespace std; +#endif + +namespace +{ + using Param = DemangledTypeNode::Param; + using ParamList = vector; + + std::string_view TrimSpaces(std::string_view s) + { + size_t start = 0; + while (start < s.size() && s[start] == ' ') + start++; + size_t end = s.size(); + while (end > start && s[end - 1] == ' ') + end--; + return s.substr(start, end - start); + } + + bool StartsWith(std::string_view s, const char* prefix) + { + size_t n = strlen(prefix); + return s.compare(0, n, prefix) == 0; + } + + std::string_view StripLeadingTypeKeyword(std::string_view s) + { + s = TrimSpaces(s); + if (StartsWith(s, "class ")) + return s.substr(6); + if (StartsWith(s, "struct ")) + return s.substr(7); + if (StartsWith(s, "union ")) + return s.substr(6); + if (StartsWith(s, "enum ")) + return s.substr(5); + return s; + } + + string RemoveSpaces(std::string_view s) + { + string out; + out.reserve(s.size()); + for (char c : s) + { + if (c != ' ') + out += c; + } + return out; + } + + bool StringEquals(const string& s, std::string_view value) + { + return s.size() == value.size() && + (value.empty() || memcmp(s.data(), value.data(), value.size()) == 0); + } + + bool IsUnqualifiedNonPointerType(const DemangledTypeNode& type) + { + return !type.IsConst() && !type.IsVolatile() && type.GetPointerSuffixBits() == 0; + } + + size_t ResolvedIntegerWidth(size_t width, DemangledTypeNode::WidthKind widthKind) + { + switch (widthKind) + { + case DemangledTypeNode::AddressWidth: + return 8; + case DemangledTypeNode::DefaultIntegerWidth: + return 4; + case DemangledTypeNode::FixedWidth: + default: + return width; + } + } + + bool GetUnqualifiedIntegerTypeInfo(const DemangledTypeNode& type, size_t& width, + DemangledTypeNode::WidthKind& widthKind, bool& isSigned, std::string_view& altName) + { + return IsUnqualifiedNonPointerType(type) && type.GetIntegerTypeInfo(width, widthKind, isSigned, altName); + } + + bool GetUnqualifiedWideCharTypeInfo(const DemangledTypeNode& type, size_t& width, + std::string_view& altName) + { + return IsUnqualifiedNonPointerType(type) && type.GetWideCharTypeInfo(width, altName); + } + + string CanonicalTypeNoSpaces(const DemangledTypeNode& type) + { + string s = type.GetString(); + return RemoveSpaces(StripLeadingTypeKeyword(std::string_view(s.data(), s.size()))); + } + + string CanonicalTypeNoSpaces(std::string_view type) + { + return RemoveSpaces(StripLeadingTypeKeyword(type)); + } + + bool SameType(const DemangledTypeNode& a, const DemangledTypeNode& b) + { + if (a.IsStructurallyEqual(b)) + return true; + return CanonicalTypeNoSpaces(a) == CanonicalTypeNoSpaces(b); + } + + bool SameType(const DemangledTypeNode& a, std::string_view b) + { + if (!a.IsConst() && !a.IsVolatile() && a.GetPointerSuffixBits() == 0 && a.GetClass() == NamedTypeReferenceClass) + { + const DemangledQualifiedName& name = a.GetName(); + if (name.size() == 1 && !name[0].HasTemplateArguments()) + { + const string& base = name[0].GetBase(); + if (StringEquals(base, b)) + return true; + } + } + return CanonicalTypeNoSpaces(a) == CanonicalTypeNoSpaces(b); + } + + bool IsSimpleNamedType(const DemangledTypeNode& type, std::string_view name, bool cnst = false) + { + if (type.IsConst() != cnst || type.IsVolatile() || type.GetPointerSuffixBits() != 0 || + type.GetClass() != NamedTypeReferenceClass) + return false; + + const DemangledQualifiedName& typeName = type.GetName(); + if (typeName.size() != 1 || typeName[0].HasTemplateArguments()) + return false; + const string& base = typeName[0].GetBase(); + return StringEquals(base, name); + } + + bool IsQualifiedName(const DemangledTypeNode& type, std::initializer_list expected) + { + if (!IsUnqualifiedNonPointerType(type) || type.GetClass() != NamedTypeReferenceClass) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (name.size() != expected.size()) + return false; + + size_t i = 0; + for (std::string_view expectedSegment : expected) + { + if (name[i].HasTemplateArguments() || !StringEquals(name[i].GetBase(), expectedSegment)) + return false; + i++; + } + return true; + } + + const char* CharFamilyAliasName(const DemangledTypeNode& element, const char* charAlias, + const char* wcharAlias, const char* char8Alias = nullptr, const char* char16Alias = nullptr, + const char* char32Alias = nullptr) + { + size_t width = 0; + DemangledTypeNode::WidthKind widthKind = DemangledTypeNode::FixedWidth; + bool isSigned = false; + std::string_view altName; + if (GetUnqualifiedIntegerTypeInfo(element, width, widthKind, isSigned, altName)) + { + if (char8Alias && altName == "char8_t") + return char8Alias; + if (isSigned && (altName.empty() || altName == "char") && + ResolvedIntegerWidth(width, widthKind) == 1) + return charAlias; + } + + size_t wideWidth = 0; + std::string_view wideAltName; + if (GetUnqualifiedWideCharTypeInfo(element, wideWidth, wideAltName)) + { + if (char16Alias && wideAltName == "char16_t") + return char16Alias; + if (char32Alias && wideAltName == "char32_t") + return char32Alias; + if (wideAltName.empty() || wideAltName == "wchar_t") + return wcharAlias; + } + + if (IsSimpleNamedType(element, "char")) + return charAlias; + if (IsSimpleNamedType(element, "wchar_t")) + return wcharAlias; + if (char8Alias && IsSimpleNamedType(element, "char8_t")) + return char8Alias; + if (char16Alias && IsSimpleNamedType(element, "char16_t")) + return char16Alias; + if (char32Alias && IsSimpleNamedType(element, "char32_t")) + return char32Alias; + + const string type = CanonicalTypeNoSpaces(element); + if (type == "char") + return charAlias; + if (type == "wchar_t") + return wcharAlias; + if (char8Alias && type == "char8_t") + return char8Alias; + if (char16Alias && type == "char16_t") + return char16Alias; + if (char32Alias && type == "char32_t") + return char32Alias; + return nullptr; + } + + bool HasOnlyOmittedPointerSuffixes(const DemangledTypeNode& type) + { + return (type.GetPointerSuffixBits() & ~(1u << Ptr64Suffix)) == 0; + } + + bool IsConstTypeNamed(const DemangledTypeNode& type, std::string_view name) + { + if (IsSimpleNamedType(type, name, true)) + return true; + if (!type.IsConst() || type.IsVolatile()) + return false; + + DemangledTypeNode unqualified = type; + unqualified.SetConst(false); + return SameType(unqualified, name); + } + + bool IsInlineStdNamespace(const DemangledNamePart& component) + { + if (component.HasTemplateArguments()) + return false; + const string& name = component.GetBase(); + return name == "__1" || name == "__cxx11" || name == "__detail"; + } + + bool IsAbslInlineNamespace(const DemangledNamePart& component) + { + if (component.HasTemplateArguments()) + return false; + const string& name = component.GetBase(); + if (!StartsWith(name, "lts_")) + return false; + for (size_t i = 4; i < name.size(); i++) + { + if (!isdigit(static_cast(name[i])) && name[i] != '_') + return false; + } + return name.size() > 4; + } + + bool IsStdScope(const DemangledQualifiedName& name, size_t* templateIndex) + { + if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "std") + return false; + + size_t i = 1; + while (i < name.size() && IsInlineStdNamespace(name[i])) + i++; + if (i >= name.size()) + return false; + *templateIndex = i; + return true; + } + + bool IsStdScope(const DemangledTypeNode& type, size_t* templateIndex) + { + const DemangledQualifiedName& name = type.GetName(); + return IsStdScope(name, templateIndex); + } + + bool IsAbslScope(const DemangledQualifiedName& name, size_t* templateIndex) + { + if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "absl") + return false; + + size_t i = 1; + while (i < name.size() && IsAbslInlineNamespace(name[i])) + i++; + if (i >= name.size()) + return false; + *templateIndex = i; + return true; + } + + bool IsAbslScope(const DemangledTypeNode& type, size_t* templateIndex) + { + const DemangledQualifiedName& name = type.GetName(); + return IsAbslScope(name, templateIndex); + } + + bool IsStdTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + size_t templateIndex = 0; + if (!IsStdScope(type, &templateIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (name.empty() || templateIndex + 1 != name.size()) + return false; + if (name[templateIndex].GetBase() != tmpl || !name[templateIndex].HasTemplateArguments()) + return false; + if (args) + *args = &name[templateIndex].GetTemplateArguments(); + return true; + } + + bool IsAbslTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + size_t templateIndex = 0; + if (!IsAbslScope(type, &templateIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (name.empty() || templateIndex + 1 != name.size()) + return false; + if (name[templateIndex].GetBase() != tmpl || !name[templateIndex].HasTemplateArguments()) + return false; + if (args) + *args = &name[templateIndex].GetTemplateArguments(); + return true; + } + + bool IsAbslNestedTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, + const ParamList** args = nullptr) + { + size_t scopeIndex = 0; + if (!IsAbslScope(type, &scopeIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (scopeIndex + 2 != name.size()) + return false; + if (name[scopeIndex].HasTemplateArguments() || name[scopeIndex].GetBase() != scope) + return false; + if (name[scopeIndex + 1].GetBase() != tmpl || !name[scopeIndex + 1].HasTemplateArguments()) + return false; + if (args) + *args = &name[scopeIndex + 1].GetTemplateArguments(); + return true; + } + + bool IsAbslNestedName(const DemangledTypeNode& type, std::string_view scope, std::string_view base) + { + size_t scopeIndex = 0; + if (!IsAbslScope(type, &scopeIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + return scopeIndex + 2 == name.size() && !name[scopeIndex].HasTemplateArguments() && + name[scopeIndex].GetBase() == scope && !name[scopeIndex + 1].HasTemplateArguments() && + name[scopeIndex + 1].GetBase() == base; + } + + bool IsStdPmrTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + const DemangledQualifiedName& name = type.GetName(); + if (name.size() < 3 || name[0].HasTemplateArguments() || name[0].GetBase() != "std") + return false; + + size_t i = 1; + while (i < name.size() && IsInlineStdNamespace(name[i])) + i++; + if (i >= name.size() || name[i].HasTemplateArguments() || name[i].GetBase() != "pmr") + return false; + i++; + if (i + 1 != name.size()) + return false; + if (name[i].GetBase() != tmpl || !name[i].HasTemplateArguments()) + return false; + if (args) + *args = &name[i].GetTemplateArguments(); + return true; + } + + bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsStdTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + bool IsStdOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, std::string_view arg) + { + const ParamList* args = nullptr; + if (!IsStdTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + bool IsStdPmrOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsStdPmrTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + bool IsPmrPolymorphicAllocator(const DemangledTypeNode& type, const DemangledTypeNode& valueType) + { + return IsStdPmrOneArgTemplate(type, "polymorphic_allocator", valueType); + } + + bool IsConstTypeOf(const DemangledTypeNode& type, const DemangledTypeNode& base) + { + if (type.IsConst() && !type.IsVolatile()) + { + DemangledTypeNode unqualified = type; + unqualified.SetConst(false); + if (SameType(unqualified, base)) + return true; + } + + const string baseString = base.GetString(); + string suffixConst = baseString; + suffixConst += " const"; + string prefixConst = "const "; + prefixConst += baseString; + const string actual = CanonicalTypeNoSpaces(type); + return actual == CanonicalTypeNoSpaces(std::string_view(suffixConst.data(), suffixConst.size())) || + actual == CanonicalTypeNoSpaces(std::string_view(prefixConst.data(), prefixConst.size())); + } + + bool IsStdPairOfConstKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* pairArgs = nullptr; + if (!IsStdTemplate(type, "pair", &pairArgs) || pairArgs->size() != 2 || !(*pairArgs)[0].type || + !(*pairArgs)[1].type) + return false; + return IsConstTypeOf(*(*pairArgs)[0].type, key) && SameType(*(*pairArgs)[1].type, value); + } + + bool IsStdPairOfKeyValue(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* pairArgs = nullptr; + if (!IsStdTemplate(type, "pair", &pairArgs) || pairArgs->size() != 2 || !(*pairArgs)[0].type || + !(*pairArgs)[1].type) + return false; + return SameType(*(*pairArgs)[0].type, key) && SameType(*(*pairArgs)[1].type, value); + } + + bool IsDefaultPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* allocArgs = nullptr; + if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) + return false; + return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value); + } + + bool IsDefaultPairAllocatorWithMutableKey(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* allocArgs = nullptr; + if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) + return false; + return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value) || + IsStdPairOfKeyValue(*(*allocArgs)[0].type, key, value); + } + + bool IsPmrPairAllocator(const DemangledTypeNode& type, const DemangledTypeNode& key, + const DemangledTypeNode& value) + { + const ParamList* allocArgs = nullptr; + if (!IsStdPmrTemplate(type, "polymorphic_allocator", &allocArgs) || allocArgs->size() != 1 || + !(*allocArgs)[0].type) + return false; + return IsStdPairOfConstKeyValue(*(*allocArgs)[0].type, key, value); + } + + bool IsAbslOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsAbslTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + std::string_view AbiBaseName(std::string_view name); + + bool IsStdStringLike(const DemangledTypeNode& type) + { + const DemangledQualifiedName& name = type.GetName(); + size_t templateIndex = 0; + if (!IsStdScope(name, &templateIndex)) + return false; + if (templateIndex + 1 != name.size()) + return false; + + const std::string_view base = AbiBaseName(name[templateIndex].GetBase()); + if (base == "basic_string" || base == "basic_string_view") + return true; + return !name[templateIndex].HasTemplateArguments() && + (base == "string" || base == "wstring" || base == "u8string" || base == "u16string" || + base == "u32string" || base == "string_view" || base == "wstring_view" || base == "u8string_view" || + base == "u16string_view" || base == "u32string_view"); + } + + bool IsAbslStringViewLike(const DemangledTypeNode& type) + { + size_t templateIndex = 0; + if (!IsAbslScope(type, &templateIndex)) + return false; + const DemangledQualifiedName& name = type.GetName(); + return templateIndex + 1 == name.size() && !name[templateIndex].HasTemplateArguments() && + name[templateIndex].GetBase() == "string_view"; + } + + bool IsAbslDefaultStringLikeKey(const DemangledTypeNode& key) + { + return IsStdStringLike(key) || IsAbslStringViewLike(key); + } + + bool IsAbslNestedOneArgTemplate(const DemangledTypeNode& type, std::string_view scope, std::string_view tmpl, + const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsAbslNestedTemplate(type, scope, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + bool IsAbslHashDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) + { + if (IsAbslNestedOneArgTemplate(type, "container_internal", "hash_default_hash", key) || + IsAbslNestedOneArgTemplate(type, "hash_internal", "Hash", key) || IsAbslOneArgTemplate(type, "Hash", key)) + return true; + + if (IsAbslNestedName(type, "container_internal", "StringHash")) + return IsAbslDefaultStringLikeKey(key); + + const ParamList* args = nullptr; + if (IsAbslNestedTemplate(type, "container_internal", "BasicStringHash", &args) && args->size() == 1 && + (*args)[0].type) + { + const DemangledTypeNode& charType = *(*args)[0].type; + return CharFamilyAliasName(charType, "", "", nullptr, "", "") || IsAbslDefaultStringLikeKey(key); + } + return false; + } + + bool IsAbslEqDefault(const DemangledTypeNode& type, const DemangledTypeNode& key) + { + if (IsAbslNestedOneArgTemplate(type, "container_internal", "hash_default_eq", key) || + IsStdOneArgTemplate(type, "equal_to", key)) + return true; + + const ParamList* args = nullptr; + if (IsStdTemplate(type, "equal_to", &args) && args->size() == 1 && (*args)[0].type && + SameType(*(*args)[0].type, "void")) + return true; + + if (IsAbslNestedName(type, "container_internal", "StringEq")) + return IsAbslDefaultStringLikeKey(key); + + if (IsAbslNestedTemplate(type, "container_internal", "BasicStringEq", &args) && args->size() == 1 && + (*args)[0].type) + { + const DemangledTypeNode& charType = *(*args)[0].type; + return CharFamilyAliasName(charType, "", "", nullptr, "", "") || IsAbslDefaultStringLikeKey(key); + } + return false; + } + + bool IsBoostScope(const DemangledQualifiedName& name, size_t* templateIndex) + { + if (name.empty() || name[0].HasTemplateArguments() || name[0].GetBase() != "boost") + return false; + if (name.size() < 2) + return false; + *templateIndex = 1; + return true; + } + + bool IsBoostScope(const DemangledTypeNode& type, size_t* templateIndex) + { + const DemangledQualifiedName& name = type.GetName(); + return IsBoostScope(name, templateIndex); + } + + bool IsBoostTemplate(const DemangledTypeNode& type, std::string_view tmpl, const ParamList** args = nullptr) + { + size_t templateIndex = 0; + if (!IsBoostScope(type, &templateIndex)) + return false; + + const DemangledQualifiedName& name = type.GetName(); + if (templateIndex + 1 != name.size()) + return false; + if (name[templateIndex].GetBase() != tmpl || !name[templateIndex].HasTemplateArguments()) + return false; + if (args) + *args = &name[templateIndex].GetTemplateArguments(); + return true; + } + + bool IsBoostOneArgTemplate(const DemangledTypeNode& type, std::string_view tmpl, const DemangledTypeNode& arg) + { + const ParamList* args = nullptr; + if (!IsBoostTemplate(type, tmpl, &args) || args->size() != 1 || !(*args)[0].type) + return false; + return SameType(*(*args)[0].type, arg); + } + + bool GetSimpleLiteralName(const DemangledTypeNode& arg, std::string_view& literal) + { + if (arg.IsConst() || arg.IsVolatile() || arg.GetPointerSuffixBits() != 0 || + arg.GetClass() != NamedTypeReferenceClass) + return false; + + const DemangledQualifiedName& name = arg.GetName(); + if (name.size() != 1 || name[0].HasTemplateArguments()) + return false; + literal = name[0].GetBase(); + return true; + } + + bool IsIntegerSuffix(char c) + { + return c == 'u' || c == 'U' || c == 'l' || c == 'L'; + } + + bool MatchesNonTypeValue(std::string_view raw, std::string_view value) + { + raw = TrimSpaces(raw); + size_t castClose = raw.rfind(')'); + if (!raw.empty() && raw[0] == '(' && castClose != std::string_view::npos && castClose + 1 < raw.size()) + raw = TrimSpaces(raw.substr(castClose + 1)); + + size_t rawIndex = 0; + for (char expected: value) + { + while (rawIndex < raw.size() && raw[rawIndex] == ' ') + rawIndex++; + if (rawIndex >= raw.size() || raw[rawIndex] != expected) + return false; + rawIndex++; + } + + while (rawIndex < raw.size() && raw[rawIndex] == ' ') + rawIndex++; + while (rawIndex < raw.size() && IsIntegerSuffix(raw[rawIndex])) + rawIndex++; + while (rawIndex < raw.size() && raw[rawIndex] == ' ') + rawIndex++; + return rawIndex == raw.size(); + } + + bool IsNonTypeValue(const DemangledTypeNode& arg, std::string_view value) + { + std::string_view literal; + return GetSimpleLiteralName(arg, literal) && MatchesNonTypeValue(literal, value); + } + + bool CanonicalIntegerLiteral(std::string_view raw, string& out) + { + raw = TrimSpaces(raw); + bool hadCast = false; + size_t castClose = raw.rfind(')'); + if (!raw.empty() && raw[0] == '(' && castClose != std::string_view::npos && castClose + 1 < raw.size()) + { + raw = TrimSpaces(raw.substr(castClose + 1)); + hadCast = true; + } + + string s = RemoveSpaces(raw); + size_t i = 0; + if (i < s.size() && (s[i] == '-' || s[i] == '+')) + i++; + const size_t digitsStart = i; + while (i < s.size() && isdigit(static_cast(s[i]))) + i++; + if (i == digitsStart) + return false; + + const size_t suffixStart = i; + while (i < s.size()) + { + char c = s[i]; + if (c != 'u' && c != 'U' && c != 'l' && c != 'L') + return false; + i++; + } + if (suffixStart == s.size() && !hadCast) + return false; + + out = s.substr(0, suffixStart); + return !out.empty() && out != s; + } + + bool CanonicalizeNonTypeIntegerLiteral(DemangledTypeNode& type) + { + std::string_view literal; + if (!GetSimpleLiteralName(type, literal)) + return false; + + string canonical; + if (!CanonicalIntegerLiteral(literal, canonical)) + return false; + type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{canonical}); + return true; + } + + bool IsVoidType(const DemangledTypeNode& arg) + { + return SameType(arg, "void"); + } + + bool IsPtrdiffType(const DemangledTypeNode& arg) + { + if (IsSimpleNamedType(arg, "long") || IsSimpleNamedType(arg, "long int") || + IsSimpleNamedType(arg, "long long") || IsSimpleNamedType(arg, "long long int") || + IsSimpleNamedType(arg, "__int64") || IsSimpleNamedType(arg, "int64_t") || + IsQualifiedName(arg, {"std", "ptrdiff_t"})) + return true; + + size_t width = 0; + DemangledTypeNode::WidthKind widthKind = DemangledTypeNode::FixedWidth; + bool isSigned = false; + std::string_view altName; + if (GetUnqualifiedIntegerTypeInfo(arg, width, widthKind, isSigned, altName)) + { + if (altName == "long" || altName == "long int" || altName == "long long" || + altName == "long long int" || altName == "__int64" || altName == "int64_t") + return true; + return isSigned && altName.empty() && ResolvedIntegerWidth(width, widthKind) == 8; + } + + string t = CanonicalTypeNoSpaces(arg); + return t == "long" || t == "longint" || t == "longlong" || t == "longlongint" || + t == "__int64" || t == "int64_t" || t == "std::ptrdiff_t"; + } + + bool IsPointerTo(const DemangledTypeNode& type, const DemangledTypeNode& pointee) + { + const DemangledTypeNode* child = nullptr; + BNReferenceType referenceType = PointerReferenceType; + return !type.IsConst() && !type.IsVolatile() && HasOnlyOmittedPointerSuffixes(type) && + type.GetPointerChildType(child, referenceType) && referenceType == PointerReferenceType && child && + SameType(*child, pointee); + } + + bool IsReferenceTo(const DemangledTypeNode& type, const DemangledTypeNode& referenced) + { + const DemangledTypeNode* child = nullptr; + BNReferenceType referenceType = PointerReferenceType; + return !type.IsConst() && !type.IsVolatile() && HasOnlyOmittedPointerSuffixes(type) && + type.GetPointerChildType(child, referenceType) && referenceType == ReferenceReferenceType && child && + SameType(*child, referenced); + } + + bool IsConstPointerTo(const DemangledTypeNode& type, std::string_view pointee) + { + const DemangledTypeNode* child = nullptr; + BNReferenceType referenceType = PointerReferenceType; + return !type.IsConst() && !type.IsVolatile() && HasOnlyOmittedPointerSuffixes(type) && + type.GetPointerChildType(child, referenceType) && referenceType == PointerReferenceType && child && + IsConstTypeNamed(*child, pointee); + } + + bool IsStringConstIterator(const DemangledTypeNode& type, bool wide, bool pmr) + { + const ParamList* wrapIterArgs = nullptr; + if (IsStdTemplate(type, "__wrap_iter", &wrapIterArgs) && wrapIterArgs->size() == 1 && + (*wrapIterArgs)[0].type) + { + return IsConstPointerTo(*(*wrapIterArgs)[0].type, wide ? "wchar_t" : "char"); + } + + const char* stringName = wide ? "wstring" : "string"; + if (pmr) + { + if (IsQualifiedName(type, {"std", "pmr", stringName, "const_iterator"})) + return true; + } + return IsQualifiedName(type, {"std", stringName, "const_iterator"}); + } + + const char* RegexIteratorAliasName(const DemangledTypeNode& iterator, const char* charPointerAlias, + const char* wcharPointerAlias, const char* stringAlias, const char* wstringAlias, bool pmr = false) + { + if (IsConstPointerTo(iterator, "char")) + return charPointerAlias; + if (IsConstPointerTo(iterator, "wchar_t")) + return wcharPointerAlias; + if (IsStringConstIterator(iterator, false, pmr)) + return stringAlias; + if (IsStringConstIterator(iterator, true, pmr)) + return wstringAlias; + return nullptr; + } + + bool IsSubMatchOfIterator(const DemangledTypeNode& type, const DemangledTypeNode& iterator, bool pmr = false) + { + const ParamList* subMatchArgs = nullptr; + if (IsStdTemplate(type, "sub_match", &subMatchArgs) && subMatchArgs->size() == 1 && (*subMatchArgs)[0].type) + return SameType(*(*subMatchArgs)[0].type, iterator); + + size_t templateIndex = 0; + if (!IsStdScope(type, &templateIndex)) + return false; + const DemangledQualifiedName& name = type.GetName(); + if (name.empty() || templateIndex + 1 != name.size() || name[templateIndex].HasTemplateArguments()) + return false; + + const char* expected = RegexIteratorAliasName(iterator, "csub_match", "wcsub_match", "ssub_match", + "wssub_match", pmr); + return expected && name[templateIndex].GetBase() == expected; + } + + bool IsDefaultSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) + { + const ParamList* allocArgs = nullptr; + if (!IsStdTemplate(type, "allocator", &allocArgs) || allocArgs->size() != 1 || !(*allocArgs)[0].type) + return false; + return IsSubMatchOfIterator(*(*allocArgs)[0].type, iterator); + } + + bool IsPmrSubMatchAllocator(const DemangledTypeNode& type, const DemangledTypeNode& iterator) + { + const ParamList* allocArgs = nullptr; + if (!IsStdPmrTemplate(type, "polymorphic_allocator", &allocArgs) || allocArgs->size() != 1 || + !(*allocArgs)[0].type) + return false; + return IsSubMatchOfIterator(*(*allocArgs)[0].type, iterator, true); + } + + const char* WideStringName(std::string_view name) + { + if (name == "basic_string") return "wstring"; + if (name == "basic_stringbuf") return "wstringbuf"; + if (name == "basic_istringstream") return "wistringstream"; + if (name == "basic_ostringstream") return "wostringstream"; + if (name == "basic_stringstream") return "wstringstream"; + if (name == "basic_ios") return "wios"; + if (name == "basic_streambuf") return "wstreambuf"; + if (name == "basic_istream") return "wistream"; + if (name == "basic_ostream") return "wostream"; + if (name == "basic_iostream") return "wiostream"; + if (name == "basic_filebuf") return "wfilebuf"; + if (name == "basic_ifstream") return "wifstream"; + if (name == "basic_ofstream") return "wofstream"; + if (name == "basic_fstream") return "wfstream"; + return nullptr; + } + + const char* AtomicAliasForName(std::string_view t) + { + if (t == "bool") return "atomic_bool"; + if (t == "char") return "atomic_char"; + if (t == "signed char") return "atomic_schar"; + if (t == "unsigned char") return "atomic_uchar"; + if (t == "short" || t == "short int") return "atomic_short"; + if (t == "unsigned short" || t == "unsigned short int") return "atomic_ushort"; + if (t == "int") return "atomic_int"; + if (t == "unsigned" || t == "unsigned int") return "atomic_uint"; + if (t == "long" || t == "long int") return "atomic_long"; + if (t == "unsigned long" || t == "unsigned long int") return "atomic_ulong"; + if (t == "long long" || t == "long long int") return "atomic_llong"; + if (t == "unsigned long long" || t == "unsigned long long int") return "atomic_ullong"; + if (t == "char8_t") return "atomic_char8_t"; + if (t == "char16_t") return "atomic_char16_t"; + if (t == "char32_t") return "atomic_char32_t"; + if (t == "wchar_t") return "atomic_wchar_t"; + if (t == "int8_t" || t == "__int8") return "atomic_int8_t"; + if (t == "uint8_t" || t == "unsigned __int8") return "atomic_uint8_t"; + if (t == "int16_t" || t == "__int16") return "atomic_int16_t"; + if (t == "uint16_t" || t == "unsigned __int16") return "atomic_uint16_t"; + if (t == "int32_t" || t == "__int32") return "atomic_int32_t"; + if (t == "uint32_t" || t == "unsigned __int32") return "atomic_uint32_t"; + if (t == "int64_t" || t == "__int64") return "atomic_int64_t"; + if (t == "uint64_t" || t == "unsigned __int64") return "atomic_uint64_t"; + if (t == "int_least8_t") return "atomic_int_least8_t"; + if (t == "uint_least8_t") return "atomic_uint_least8_t"; + if (t == "int_least16_t") return "atomic_int_least16_t"; + if (t == "uint_least16_t") return "atomic_uint_least16_t"; + if (t == "int_least32_t") return "atomic_int_least32_t"; + if (t == "uint_least32_t") return "atomic_uint_least32_t"; + if (t == "int_least64_t") return "atomic_int_least64_t"; + if (t == "uint_least64_t") return "atomic_uint_least64_t"; + if (t == "int_fast8_t") return "atomic_int_fast8_t"; + if (t == "uint_fast8_t") return "atomic_uint_fast8_t"; + if (t == "int_fast16_t") return "atomic_int_fast16_t"; + if (t == "uint_fast16_t") return "atomic_uint_fast16_t"; + if (t == "int_fast32_t") return "atomic_int_fast32_t"; + if (t == "uint_fast32_t") return "atomic_uint_fast32_t"; + if (t == "int_fast64_t") return "atomic_int_fast64_t"; + if (t == "uint_fast64_t") return "atomic_uint_fast64_t"; + if (t == "intptr_t") return "atomic_intptr_t"; + if (t == "uintptr_t") return "atomic_uintptr_t"; + if (t == "size_t") return "atomic_size_t"; + if (t == "ptrdiff_t") return "atomic_ptrdiff_t"; + if (t == "intmax_t") return "atomic_intmax_t"; + if (t == "uintmax_t") return "atomic_uintmax_t"; + return nullptr; + } + + const char* AtomicAliasForPrimitive(const DemangledTypeNode& type) + { + if (!IsUnqualifiedNonPointerType(type)) + return nullptr; + + if (type.GetClass() == BoolTypeClass) + return "atomic_bool"; + + size_t width = 0; + DemangledTypeNode::WidthKind widthKind = DemangledTypeNode::FixedWidth; + bool isSigned = false; + std::string_view altName; + if (GetUnqualifiedIntegerTypeInfo(type, width, widthKind, isSigned, altName)) + { + if (!altName.empty()) + { + if (const char* alias = AtomicAliasForName(altName)) + return alias; + } + + switch (ResolvedIntegerWidth(width, widthKind)) + { + case 1: + return isSigned ? "atomic_char" : "atomic_uint8_t"; + case 2: + return isSigned ? "atomic_int16_t" : "atomic_uint16_t"; + case 4: + return isSigned ? "atomic_int32_t" : "atomic_uint32_t"; + case 8: + return isSigned ? "atomic_int64_t" : "atomic_uint64_t"; + default: + return nullptr; + } + } + + size_t wideWidth = 0; + std::string_view wideAltName; + if (GetUnqualifiedWideCharTypeInfo(type, wideWidth, wideAltName)) + { + if (!wideAltName.empty()) + { + if (const char* alias = AtomicAliasForName(wideAltName)) + return alias; + } + return "atomic_wchar_t"; + } + + return nullptr; + } + + const char* AtomicAliasForNamedType(const DemangledTypeNode& type) + { + if (!IsUnqualifiedNonPointerType(type) || type.GetClass() != NamedTypeReferenceClass) + return nullptr; + + const DemangledQualifiedName& name = type.GetName(); + if (name.size() == 1 && !name[0].HasTemplateArguments()) + return AtomicAliasForName(name[0].GetBase()); + + if (name.size() == 2 && !name[0].HasTemplateArguments() && !name[1].HasTemplateArguments() && + StringEquals(name[0].GetBase(), "std")) + return AtomicAliasForName(name[1].GetBase()); + + return nullptr; + } + + const char* AtomicAliasName(const DemangledTypeNode& type) + { + if (const char* alias = AtomicAliasForPrimitive(type)) + return alias; + if (const char* alias = AtomicAliasForNamedType(type)) + return alias; + + const string t = CanonicalTypeNoSpaces(type); + if (t == "bool") return "atomic_bool"; + if (t == "char") return "atomic_char"; + if (t == "signedchar") return "atomic_schar"; + if (t == "unsignedchar") return "atomic_uchar"; + if (t == "short" || t == "shortint") return "atomic_short"; + if (t == "unsignedshort" || t == "unsignedshortint") return "atomic_ushort"; + if (t == "int") return "atomic_int"; + if (t == "unsigned" || t == "unsignedint") return "atomic_uint"; + if (t == "long" || t == "longint") return "atomic_long"; + if (t == "unsignedlong" || t == "unsignedlongint") return "atomic_ulong"; + if (t == "longlong" || t == "longlongint") return "atomic_llong"; + if (t == "unsignedlonglong" || t == "unsignedlonglongint") return "atomic_ullong"; + if (t == "char8_t") return "atomic_char8_t"; + if (t == "char16_t") return "atomic_char16_t"; + if (t == "char32_t") return "atomic_char32_t"; + if (t == "wchar_t") return "atomic_wchar_t"; + if (t == "int8_t" || t == "std::int8_t" || t == "__int8") return "atomic_int8_t"; + if (t == "uint8_t" || t == "std::uint8_t" || t == "unsigned__int8") return "atomic_uint8_t"; + if (t == "int16_t" || t == "std::int16_t" || t == "__int16") return "atomic_int16_t"; + if (t == "uint16_t" || t == "std::uint16_t" || t == "unsigned__int16") return "atomic_uint16_t"; + if (t == "int32_t" || t == "std::int32_t" || t == "__int32") return "atomic_int32_t"; + if (t == "uint32_t" || t == "std::uint32_t" || t == "unsigned__int32") return "atomic_uint32_t"; + if (t == "int64_t" || t == "std::int64_t" || t == "__int64") return "atomic_int64_t"; + if (t == "uint64_t" || t == "std::uint64_t" || t == "unsigned__int64") return "atomic_uint64_t"; + if (t == "int_least8_t" || t == "std::int_least8_t") return "atomic_int_least8_t"; + if (t == "uint_least8_t" || t == "std::uint_least8_t") return "atomic_uint_least8_t"; + if (t == "int_least16_t" || t == "std::int_least16_t") return "atomic_int_least16_t"; + if (t == "uint_least16_t" || t == "std::uint_least16_t") return "atomic_uint_least16_t"; + if (t == "int_least32_t" || t == "std::int_least32_t") return "atomic_int_least32_t"; + if (t == "uint_least32_t" || t == "std::uint_least32_t") return "atomic_uint_least32_t"; + if (t == "int_least64_t" || t == "std::int_least64_t") return "atomic_int_least64_t"; + if (t == "uint_least64_t" || t == "std::uint_least64_t") return "atomic_uint_least64_t"; + if (t == "int_fast8_t" || t == "std::int_fast8_t") return "atomic_int_fast8_t"; + if (t == "uint_fast8_t" || t == "std::uint_fast8_t") return "atomic_uint_fast8_t"; + if (t == "int_fast16_t" || t == "std::int_fast16_t") return "atomic_int_fast16_t"; + if (t == "uint_fast16_t" || t == "std::uint_fast16_t") return "atomic_uint_fast16_t"; + if (t == "int_fast32_t" || t == "std::int_fast32_t") return "atomic_int_fast32_t"; + if (t == "uint_fast32_t" || t == "std::uint_fast32_t") return "atomic_uint_fast32_t"; + if (t == "int_fast64_t" || t == "std::int_fast64_t") return "atomic_int_fast64_t"; + if (t == "uint_fast64_t" || t == "std::uint_fast64_t") return "atomic_uint_fast64_t"; + if (t == "intptr_t" || t == "std::intptr_t") return "atomic_intptr_t"; + if (t == "uintptr_t" || t == "std::uintptr_t") return "atomic_uintptr_t"; + if (t == "size_t" || t == "std::size_t") return "atomic_size_t"; + if (t == "ptrdiff_t" || t == "std::ptrdiff_t") return "atomic_ptrdiff_t"; + if (t == "intmax_t" || t == "std::intmax_t") return "atomic_intmax_t"; + if (t == "uintmax_t" || t == "std::uintmax_t") return "atomic_uintmax_t"; + return nullptr; + } + + void ResizeTemplateArgs(DemangledNamePart& c, size_t n) + { + c.GetMutableTemplateArguments().resize(n); + } + + std::string_view AbiBaseName(std::string_view name) + { + size_t suffixStart = name.size(); + while (suffixStart > 0 && name[suffixStart - 1] == ']') + { + std::string_view prefix = name.substr(0, suffixStart); + size_t tagStart = prefix.rfind("[abi:"); + if (tagStart == std::string_view::npos) + break; + suffixStart = tagStart; + } + return name.substr(0, suffixStart); + } + + std::string_view AbiTagSuffix(std::string_view name) + { + size_t suffixStart = name.size(); + while (suffixStart > 0 && name[suffixStart - 1] == ']') + { + std::string_view prefix = name.substr(0, suffixStart); + size_t tagStart = prefix.rfind("[abi:"); + if (tagStart == std::string_view::npos) + break; + suffixStart = tagStart; + } + return name.substr(suffixStart); + } + + void ReplaceWithAlias(DemangledNamePart& c, std::string_view alias) + { + string replacement(alias.data(), alias.size()); + const std::string_view abiSuffix = AbiTagSuffix(c.GetBase()); + replacement.append(abiSuffix.data(), abiSuffix.size()); + c.SetBase(std::move(replacement)); + c.ClearTemplateArguments(); + } + + bool ApplyStringRule(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.size() != 3 || (name != "basic_string" && name != "basic_stringbuf" && + name != "basic_istringstream" && name != "basic_ostringstream" && name != "basic_stringstream")) + return false; + if (!args[0].type || !args[1].type || !args[2].type) + return false; + + const DemangledTypeNode& element = *args[0].type; + if (!IsStdOneArgTemplate(*args[1].type, "char_traits", element) || + !IsStdOneArgTemplate(*args[2].type, "allocator", element)) + return false; + + if (SameType(element, "char")) + { + if (name == "basic_string") + ReplaceWithAlias(c, "string"); + else + ReplaceWithAlias(c, name.substr(6)); + return true; + } + if (SameType(element, "wchar_t")) + { + if (const char* wide = WideStringName(name)) + ReplaceWithAlias(c, wide); + else + return false; + return true; + } + if (name == "basic_string") + { + if (const char* alias = CharFamilyAliasName(element, "string", "wstring", "u8string", "u16string", + "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + + if (const char* alias = CharFamilyAliasName(element, nullptr, nullptr, name == "basic_string" ? "u8string" : nullptr, + name == "basic_string" ? "u16string" : nullptr, name == "basic_string" ? "u32string" : nullptr)) + { + ReplaceWithAlias(c, alias); + return true; + } + + ResizeTemplateArgs(c, 1); + return true; + } + + bool ApplyIostreamRule(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.size() != 2 || !StartsWith(name, "basic_")) + return false; + if (name != "basic_ios" && name != "basic_streambuf" && name != "basic_istream" && + name != "basic_ostream" && name != "basic_iostream" && name != "basic_filebuf" && + name != "basic_ifstream" && name != "basic_ofstream" && name != "basic_fstream") + return false; + if (!args[0].type || !args[1].type) + return false; + + const DemangledTypeNode& element = *args[0].type; + if (!IsStdOneArgTemplate(*args[1].type, "char_traits", element)) + return false; + + if (SameType(element, "char")) + { + ReplaceWithAlias(c, name.substr(6)); + return true; + } + if (SameType(element, "wchar_t")) + { + if (const char* wide = WideStringName(name)) + { + ReplaceWithAlias(c, wide); + return true; + } + } + + ResizeTemplateArgs(c, 1); + return true; + } + + bool ApplyContainerRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "vector" && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if (name == "vector" && args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "lessthan", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "deque" || name == "forward_list" || name == "list") && args.size() == 2 && + args[0].type && args[1].type && IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "stack" || name == "queue") && args.size() == 2 && args[0].type && args[1].type) + { + const ParamList* dequeArgs = nullptr; + if (IsStdTemplate(*args[1].type, "deque", &dequeArgs) && dequeArgs->size() == 1 && + (*dequeArgs)[0].type && SameType(*(*dequeArgs)[0].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + } + + if (name == "priority_queue" && args.size() == 3 && args[0].type && args[1].type && args[2].type) + { + const ParamList* vectorArgs = nullptr; + if (IsStdTemplate(*args[1].type, "vector", &vectorArgs) && vectorArgs->size() == 1 && + (*vectorArgs)[0].type && SameType(*(*vectorArgs)[0].type, *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + } + + if ((name == "set" || name == "multiset") && args.size() == 3 && args[0].type && args[1].type && + args[2].type && IsStdOneArgTemplate(*args[1].type, "less", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "map" || name == "multimap") && args.size() == 4 && args[0].type && args[1].type && + args[2].type && args[3].type && IsStdOneArgTemplate(*args[2].type, "less", *args[0].type) && + IsDefaultPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "unordered_set" || name == "unordered_multiset") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[1].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "unordered_map" || name == "unordered_multimap") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsStdOneArgTemplate(*args[2].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type) && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + return false; + } + + bool ApplyDiscoveredStdRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "basic_regex" && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "regex_traits", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "unique_ptr" && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "default_delete", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "match_results" && args.size() == 2 && args[0].type && args[1].type && + IsDefaultSubMatchAllocator(*args[1].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "ratio" && args.size() == 2 && args[1].type && IsNonTypeValue(*args[1].type, "1")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "extent" && args.size() == 2 && args[1].type && IsNonTypeValue(*args[1].type, "0")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "enable_if" && args.size() == 2 && args[1].type && IsVoidType(*args[1].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "compare_three_way_result" && args.size() == 2 && args[0].type && args[1].type && + SameType(*args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "formatter" && args.size() == 2 && args[1].type && SameType(*args[1].type, "char")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if ((name == "moneypunct" || name == "moneypunct_byname") && args.size() == 2 && args[1].type && + (IsNonTypeValue(*args[1].type, "false") || IsNonTypeValue(*args[1].type, "0"))) + { + ResizeTemplateArgs(c, 1); + return true; + } + if ((name == "codecvt_utf8" || name == "codecvt_utf16" || name == "codecvt_utf8_utf16") && + args.size() == 3 && args[1].type && args[2].type && IsNonTypeValue(*args[1].type, "1114111") && + IsNonTypeValue(*args[2].type, "0")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "istream_iterator" && args.size() == 4 && args[1].type && args[2].type && args[3].type && + SameType(*args[1].type, "char") && IsStdOneArgTemplate(*args[2].type, "char_traits", "char") && + IsPtrdiffType(*args[3].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "ostream_iterator" && args.size() == 3 && args[1].type && args[2].type && + SameType(*args[1].type, "char") && IsStdOneArgTemplate(*args[2].type, "char_traits", "char")) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (name == "iterator" && args.size() == 5 && args[1].type && args[2].type && args[3].type && + args[4].type && IsPtrdiffType(*args[2].type) && IsPointerTo(*args[3].type, *args[1].type) && + IsReferenceTo(*args[4].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + if ((name == "default_searcher" || name == "boyer_moore_searcher" || + name == "boyer_moore_horspool_searcher") && args.size() == 2 && args[1].type) + { + const ParamList* equalArgs = nullptr; + if (IsStdTemplate(*args[1].type, "equal_to", &equalArgs) && equalArgs->size() == 1 && + (*equalArgs)[0].type && IsVoidType(*(*equalArgs)[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + } + if ((name == "boyer_moore_searcher" || name == "boyer_moore_horspool_searcher") && args.size() == 3 && + args[2].type && IsStdOneArgTemplate(*args[2].type, "equal_to", "void")) + { + ResizeTemplateArgs(c, 2); + return true; + } + return false; + } + + bool ApplyChronoRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + if (c.GetBase() != "duration" || args.size() != 2 || !args[1].type) + return false; + + const ParamList* ratioArgs = nullptr; + if (IsStdTemplate(*args[1].type, "ratio", &ratioArgs) && ratioArgs->size() == 1 && + (*ratioArgs)[0].type && IsNonTypeValue(*(*ratioArgs)[0].type, "1")) + { + ResizeTemplateArgs(c, 1); + return true; + } + return false; + } + + bool ApplyGenericStdDefaultRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + if (args.size() == 2 && args[0].type && args[1].type && + (IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) || + IsStdOneArgTemplate(*args[1].type, "istreambuf_iterator", *args[0].type) || + IsStdOneArgTemplate(*args[1].type, "ostreambuf_iterator", *args[0].type))) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if (args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + return false; + } + + bool ApplyStdFunctionTemplateRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if ((name == "operator<<" || name == "operator+") && args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + if (name == "operator<<") + c.ClearTemplateArguments(); + else + ResizeTemplateArgs(c, 1); + return true; + } + + if (name != "operator==" && name != "operator!=" && name != "operator<" && name != "operator<=" && + name != "operator>" && name != "operator>=" && name != "operator<=>") + return false; + + if (args.size() == 5 && args[0].type && args[1].type && args[2].type && args[3].type && args[4].type && + IsStdOneArgTemplate(*args[2].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type) && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + if (args.size() == 4 && args[0].type && args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[1].type, "hash", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type) && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (args.size() == 4 && args[0].type && args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[2].type, "less", *args[0].type) && + IsDefaultPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + if (args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "less", *args[0].type) && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + if (args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + return false; + } + + bool ApplyStdTemplateRules(DemangledNamePart& c) + { + if (ApplyStringRule(c)) + return true; + if (ApplyIostreamRule(c)) + return true; + if (ApplyContainerRules(c)) + return true; + if (ApplyDiscoveredStdRules(c)) + return true; + if (ApplyStdFunctionTemplateRules(c)) + return true; + return ApplyGenericStdDefaultRules(c); + } + + bool ApplyAbslTemplateRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if ((name == "flat_hash_set" || name == "node_hash_set") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + const bool defaultHash = IsAbslHashDefault(*args[1].type, *args[0].type); + const bool defaultEq = IsAbslEqDefault(*args[2].type, *args[0].type); + if (defaultHash && defaultEq) + ResizeTemplateArgs(c, 1); + else if (defaultEq) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "flat_hash_map" || name == "node_hash_map") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + const bool defaultHash = IsAbslHashDefault(*args[2].type, *args[0].type); + const bool defaultEq = IsAbslEqDefault(*args[3].type, *args[0].type); + if (defaultHash && defaultEq) + ResizeTemplateArgs(c, 2); + else if (defaultEq) + ResizeTemplateArgs(c, 3); + else + ResizeTemplateArgs(c, 4); + return true; + } + + if ((name == "btree_set" || name == "btree_multiset") && args.size() == 3 && args[0].type && + args[1].type && args[2].type && IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + if (IsStdOneArgTemplate(*args[1].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 1); + else + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "btree_map" || name == "btree_multimap") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsDefaultPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + if (IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if (name == "InlinedVector" && args.size() == 3 && args[0].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + if (name == "FixedArray" && args.size() == 3 && args[0].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + return false; + } + + bool ApplyBoostContainerStringRule(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (name != "basic_string" || args.size() != 3 || !args[0].type || !args[1].type || !args[2].type) + return false; + + const DemangledTypeNode& element = *args[0].type; + if (!IsStdOneArgTemplate(*args[1].type, "char_traits", element) || + !IsStdOneArgTemplate(*args[2].type, "allocator", element)) + return false; + + if (const char* alias = CharFamilyAliasName(element, "string", "wstring", "u8string", "u16string", + "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + + ResizeTemplateArgs(c, 1); + return true; + } + + bool ApplyBoostContainerTemplateRules(DemangledNamePart& c) + { + if (ApplyBoostContainerStringRule(c)) + return true; + + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if ((name == "vector" || name == "deque" || name == "list" || name == "slist" || + name == "stable_vector") && args.size() == 2 && args[0].type && args[1].type && + IsStdOneArgTemplate(*args[1].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if (name == "small_vector" && args.size() == 3 && args[0].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "set" || name == "multiset" || name == "flat_set" || name == "flat_multiset") && + args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[2].type, "allocator", *args[0].type)) + { + if (IsStdOneArgTemplate(*args[1].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 1); + else + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "map" || name == "multimap" || name == "flat_map" || name == "flat_multimap") && + args.size() == 4 && args[0].type && args[1].type && args[2].type && args[3].type && + IsDefaultPairAllocatorWithMutableKey(*args[3].type, *args[0].type, *args[1].type)) + { + if (IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + return false; + } + + bool ApplyBoostUnorderedTemplateRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if ((name == "unordered_set" || name == "unordered_multiset") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsStdOneArgTemplate(*args[3].type, "allocator", *args[0].type)) + { + const bool defaultHash = IsBoostOneArgTemplate(*args[1].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 1); + else if (defaultEqual) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "unordered_map" || name == "unordered_multimap") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsDefaultPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + const bool defaultHash = IsBoostOneArgTemplate(*args[2].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 2); + else if (defaultEqual) + ResizeTemplateArgs(c, 3); + else + ResizeTemplateArgs(c, 4); + return true; + } + + return false; + } + + bool ApplyPmrAliasToStdTemplate(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "basic_string" && args.size() == 3 && args[0].type && args[1].type && args[2].type && + IsStdOneArgTemplate(*args[1].type, "char_traits", *args[0].type) && + IsPmrPolymorphicAllocator(*args[2].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "vector" || name == "deque" || name == "forward_list" || name == "list") && + args.size() == 2 && args[0].type && args[1].type && + IsPmrPolymorphicAllocator(*args[1].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + if ((name == "set" || name == "multiset") && args.size() == 3 && args[0].type && args[1].type && + args[2].type && IsPmrPolymorphicAllocator(*args[2].type, *args[0].type)) + { + if (IsStdOneArgTemplate(*args[1].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 1); + else + ResizeTemplateArgs(c, 2); + return true; + } + + if ((name == "map" || name == "multimap") && args.size() == 4 && args[0].type && args[1].type && + args[2].type && args[3].type && IsPmrPairAllocator(*args[3].type, *args[0].type, *args[1].type)) + { + if (IsStdOneArgTemplate(*args[2].type, "less", *args[0].type)) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "unordered_set" || name == "unordered_multiset") && args.size() == 4 && args[0].type && + args[1].type && args[2].type && args[3].type && + IsPmrPolymorphicAllocator(*args[3].type, *args[0].type)) + { + const bool defaultHash = IsStdOneArgTemplate(*args[1].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[2].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 1); + else if (defaultEqual) + ResizeTemplateArgs(c, 2); + else + ResizeTemplateArgs(c, 3); + return true; + } + + if ((name == "unordered_map" || name == "unordered_multimap") && args.size() == 5 && args[0].type && + args[1].type && args[2].type && args[3].type && args[4].type && + IsPmrPairAllocator(*args[4].type, *args[0].type, *args[1].type)) + { + const bool defaultHash = IsStdOneArgTemplate(*args[2].type, "hash", *args[0].type); + const bool defaultEqual = IsStdOneArgTemplate(*args[3].type, "equal_to", *args[0].type); + if (defaultHash && defaultEqual) + ResizeTemplateArgs(c, 2); + else if (defaultEqual) + ResizeTemplateArgs(c, 3); + else + ResizeTemplateArgs(c, 4); + return true; + } + + if (name == "match_results" && args.size() == 2 && args[0].type && args[1].type && + IsPmrSubMatchAllocator(*args[1].type, *args[0].type)) + { + ResizeTemplateArgs(c, 1); + return true; + } + + return false; + } + + bool ApplyConcreteStdAliasRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (args.empty()) + return false; + + if (name == "atomic" && args.size() == 1 && args[0].type) + { + if (const char* alias = AtomicAliasName(*args[0].type)) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "basic_string" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "string", "wstring", "u8string", + "u16string", "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "basic_string_view" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "string_view", "wstring_view", + "u8string_view", "u16string_view", "u32string_view")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "basic_regex" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "regex", "wregex")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "sub_match" && args.size() == 1 && args[0].type) + { + if (const char* alias = RegexIteratorAliasName(*args[0].type, "csub_match", "wcsub_match", + "ssub_match", "wssub_match")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "match_results" && args.size() == 1 && args[0].type) + { + if (const char* alias = RegexIteratorAliasName(*args[0].type, "cmatch", "wcmatch", "smatch", + "wsmatch")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if ((name == "regex_iterator" || name == "regex_token_iterator") && (args.size() == 1 || args.size() == 3) && + args[0].type) + { + if (args.size() == 3) + { + if (!args[1].type || !args[2].type) + return false; + const bool charIterator = IsConstPointerTo(*args[0].type, "char") || + IsStringConstIterator(*args[0].type, false, false); + const bool wcharIterator = IsConstPointerTo(*args[0].type, "wchar_t") || + IsStringConstIterator(*args[0].type, true, false); + if (!((charIterator && SameType(*args[1].type, "char") && + IsStdOneArgTemplate(*args[2].type, "regex_traits", "char")) || + (wcharIterator && SameType(*args[1].type, "wchar_t") && + IsStdOneArgTemplate(*args[2].type, "regex_traits", "wchar_t")))) + return false; + } + + const bool token = name == "regex_token_iterator"; + const char* alias = RegexIteratorAliasName(*args[0].type, + token ? "cregex_token_iterator" : "cregex_iterator", + token ? "wcregex_token_iterator" : "wcregex_iterator", + token ? "sregex_token_iterator" : "sregex_iterator", + token ? "wsregex_token_iterator" : "wsregex_iterator"); + if (alias) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if ((name == "basic_syncbuf" || name == "basic_osyncstream" || name == "basic_spanbuf" || + name == "basic_ispanstream" || name == "basic_ospanstream" || name == "basic_spanstream") && + args.size() == 1 && args[0].type) + { + if (name == "basic_syncbuf") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "syncbuf", "wsyncbuf")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_osyncstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "osyncstream", "wosyncstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_spanbuf") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "spanbuf", "wspanbuf")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_ispanstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "ispanstream", "wispanstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_ospanstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "ospanstream", "wospanstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + else if (name == "basic_spanstream") + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "spanstream", "wspanstream")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + } + return false; + } + + bool ApplyPmrConcreteAliasRules(DemangledNamePart& c) + { + ParamList& args = c.GetMutableTemplateArguments(); + const std::string_view name = AbiBaseName(c.GetBase()); + if (name == "basic_string" && args.size() == 1 && args[0].type) + { + if (const char* alias = CharFamilyAliasName(*args[0].type, "string", "wstring", "u8string", + "u16string", "u32string")) + { + ReplaceWithAlias(c, alias); + return true; + } + } + if (name == "match_results" && args.size() == 1 && args[0].type) + { + if (const char* alias = RegexIteratorAliasName(*args[0].type, "cmatch", "wcmatch", "smatch", + "wsmatch", true)) + { + ReplaceWithAlias(c, alias); + return true; + } + } + return false; + } + + void SimplifyTemplateArguments(DemangledNamePart& c, bool& changed) + { + for (auto& arg: c.GetMutableTemplateArguments()) + { + if (!arg.type) + continue; + DemangledTypeNode mutableType = *arg.type; + bool argChanged = CanonicalizeNonTypeIntegerLiteral(mutableType); + auto nested = DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(mutableType); + if (argChanged || nested.changed) + { + arg.type = DemangledTypeNode::CreateShared(std::move(mutableType)); + changed = true; + } + } + } + + bool ApplyNamespaceRules(DemangledQualifiedName& name) + { + bool changed = false; + if (!name.empty() && !name[0].HasTemplateArguments() && name[0].GetBase() == "__gnu_cxx") + { + name.erase(name.begin()); + changed = true; + } + + for (auto i = 1; i < name.size();) + { + if (!name[i - 1].HasTemplateArguments() && name[i - 1].GetBase() == "std" && + IsInlineStdNamespace(name[i])) + { + name.erase(name.begin() + i); + changed = true; + continue; + } + i++; + } + for (auto i = 1; i < name.size();) + { + if (!name[i - 1].HasTemplateArguments() && name[i - 1].GetBase() == "absl" && + IsAbslInlineNamespace(name[i])) + { + name.erase(name.begin() + i); + changed = true; + continue; + } + i++; + } + return changed; + } + + bool ApplyStdRules(DemangledQualifiedName& name) + { + bool changed = false; + for (auto i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") + continue; + if (ApplyStdTemplateRules(name[i])) + changed = true; + } + + for (auto i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") + continue; + if (ApplyPmrAliasToStdTemplate(name[i])) + { + name.insert(name.begin() + i, DemangledNamePart("pmr")); + changed = true; + i++; + } + } + + for (auto i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "std") + continue; + if (ApplyConcreteStdAliasRules(name[i])) + changed = true; + } + + for (auto i = 2; i < name.size(); i++) + { + if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "std" || + name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "pmr") + continue; + if (ApplyPmrConcreteAliasRules(name[i])) + changed = true; + } + + for (auto i = 2; i < name.size(); i++) + { + if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "std" || + name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "chrono") + continue; + if (ApplyChronoRules(name[i])) + changed = true; + } + return changed; + } + + bool ApplyAbslRules(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "absl") + continue; + if (ApplyAbslTemplateRules(name[i])) + changed = true; + } + return changed; + } + + bool ApplyBoostRules(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + if (name[i - 1].HasTemplateArguments() || name[i - 1].GetBase() != "boost") + continue; + if (ApplyBoostUnorderedTemplateRules(name[i])) + changed = true; + } + + for (size_t i = 2; i < name.size(); i++) + { + if (name[i - 2].HasTemplateArguments() || name[i - 2].GetBase() != "boost" || + name[i - 1].HasTemplateArguments()) + continue; + + if (name[i - 1].GetBase() == "container") + { + if (ApplyBoostContainerTemplateRules(name[i])) + changed = true; + } + else if (name[i - 1].GetBase() == "unordered") + { + if (ApplyBoostUnorderedTemplateRules(name[i])) + changed = true; + } + } + return changed; + } + + struct AbiTaggedName + { + string base; + string tags; + }; + + AbiTaggedName SplitAbiTagSuffix(std::string_view name) + { + size_t suffixStart = name.size(); + while (suffixStart > 0 && name[suffixStart - 1] == ']') + { + std::string_view prefix = name.substr(0, suffixStart); + size_t tagStart = prefix.rfind("[abi:"); + if (tagStart == std::string_view::npos) + break; + suffixStart = tagStart; + } + return { + string(name.substr(0, suffixStart)), + string(name.substr(suffixStart)) + }; + } + + string AddAbiTags(const string& base, const string& tags) + { + if (tags.empty() || (base.size() >= tags.size() && base.compare(base.size() - tags.size(), tags.size(), tags) == 0)) + return base; + return base + tags; + } + + bool ConstructorNameMatches(const string& previousBase, const string& currentBase) + { + if (previousBase == currentBase) + return true; + if (currentBase.size() > previousBase.size() + 1 && + currentBase.compare(0, previousBase.size(), previousBase) == 0 && + currentBase[previousBase.size()] == '<') + return true; + if (StartsWith(std::string_view(currentBase.data(), currentBase.size()), "basic_") && + previousBase == currentBase.substr(6)) + return true; + if (const char* wideName = WideStringName(currentBase)) + return previousBase == wideName; + return false; + } + + bool RewriteSimplifiedConstructorSegments(DemangledQualifiedName& name) + { + bool changed = false; + for (size_t i = 1; i < name.size(); i++) + { + std::string_view currentName = name[i].GetBase(); + bool destructor = false; + if (!currentName.empty() && currentName[0] == '~') + { + destructor = true; + currentName.remove_prefix(1); + } + + AbiTaggedName current = SplitAbiTagSuffix(currentName); + AbiTaggedName previous = SplitAbiTagSuffix(name[i - 1].GetBase()); + if (current.base.empty() || previous.base.empty() || !ConstructorNameMatches(previous.base, current.base)) + continue; + + string replacement = AddAbiTags(previous.base, current.tags); + if (destructor) + replacement.insert(replacement.begin(), '~'); + + bool argsChanged = false; + DemangledNamePart simplifiedCurrent = name[i]; + if (name[i].HasTemplateArguments() && !destructor) + { + simplifiedCurrent.SetBase(current.base); + if (ApplyStdTemplateRules(simplifiedCurrent) || ApplyConcreteStdAliasRules(simplifiedCurrent) || + ApplyAbslTemplateRules(simplifiedCurrent) || + ApplyBoostContainerTemplateRules(simplifiedCurrent) || + ApplyBoostUnorderedTemplateRules(simplifiedCurrent)) + { + const std::string_view simplifiedBase = AbiBaseName(simplifiedCurrent.GetBase()); + if (simplifiedBase == previous.base || simplifiedBase == replacement) + { + argsChanged = true; + } + } + } + + if (replacement != name[i].GetBase()) + { + name[i].SetBase(replacement); + changed = true; + } + if (argsChanged) + { + name[i].SetTemplateArguments(simplifiedCurrent.GetTemplateArguments(), true); + if (!simplifiedCurrent.HasTemplateArguments()) + name[i].ClearTemplateArguments(); + changed = true; + } + } + return changed; + } + + bool IsAliasName(std::string_view alias, const unordered_set& aliases) + { + return aliases.find(alias) != aliases.end(); + } + + bool IsTopLevelSimplifiedAlias(const DemangledQualifiedName& name) + { + if (name.empty() || name[0].HasTemplateArguments()) + return false; + + if (name[0].GetBase() == "std") + { + static const unordered_set stdAliases = { + "string", "wstring", "u8string", "u16string", "u32string", + "string_view", "wstring_view", "u8string_view", "u16string_view", "u32string_view", + "stringbuf", "wstringbuf", "istringstream", "wistringstream", + "ostringstream", "wostringstream", "stringstream", "wstringstream", + "ios", "wios", "streambuf", "wstreambuf", "istream", "wistream", + "ostream", "wostream", "iostream", "wiostream", "filebuf", "wfilebuf", + "ifstream", "wifstream", "ofstream", "wofstream", "fstream", "wfstream", + "syncbuf", "wsyncbuf", "osyncstream", "wosyncstream", "spanbuf", "wspanbuf", + "ispanstream", "wispanstream", "ospanstream", "wospanstream", "spanstream", "wspanstream", + "regex", "wregex", "csub_match", "wcsub_match", "ssub_match", "wssub_match", + "cmatch", "wcmatch", "smatch", "wsmatch", "cregex_iterator", "wcregex_iterator", + "sregex_iterator", "wsregex_iterator", "cregex_token_iterator", "wcregex_token_iterator", + "sregex_token_iterator", "wsregex_token_iterator", + "atomic_bool", "atomic_char", "atomic_schar", "atomic_uchar", + "atomic_short", "atomic_ushort", "atomic_int", "atomic_uint", + "atomic_long", "atomic_ulong", "atomic_llong", "atomic_ullong", + "atomic_char8_t", "atomic_char16_t", "atomic_char32_t", "atomic_wchar_t", + "atomic_int8_t", "atomic_uint8_t", "atomic_int16_t", "atomic_uint16_t", + "atomic_int32_t", "atomic_uint32_t", "atomic_int64_t", "atomic_uint64_t", + "atomic_int_least8_t", "atomic_uint_least8_t", "atomic_int_least16_t", + "atomic_uint_least16_t", "atomic_int_least32_t", "atomic_uint_least32_t", + "atomic_int_least64_t", "atomic_uint_least64_t", "atomic_int_fast8_t", + "atomic_uint_fast8_t", "atomic_int_fast16_t", "atomic_uint_fast16_t", + "atomic_int_fast32_t", "atomic_uint_fast32_t", "atomic_int_fast64_t", + "atomic_uint_fast64_t", "atomic_intptr_t", "atomic_uintptr_t", + "atomic_size_t", "atomic_ptrdiff_t", "atomic_intmax_t", "atomic_uintmax_t", + "atomic_signed_lock_free", "atomic_unsigned_lock_free", "streampos" + }; + + if (name.size() == 2 && !name[1].HasTemplateArguments()) + return IsAliasName(name[1].GetBase(), stdAliases); + + if (name.size() != 3 || name[1].HasTemplateArguments() || name[1].GetBase() != "pmr" || + name[2].HasTemplateArguments()) + return false; + + static const unordered_set pmrAliases = { + "string", "wstring", "u8string", "u16string", "u32string", + "vector", "deque", "list", "forward_list", + "map", "multimap", "set", "multiset", + "unordered_map", "unordered_multimap", "unordered_set", "unordered_multiset", + "match_results", "cmatch", "wcmatch", "smatch", "wsmatch" + }; + return IsAliasName(name[2].GetBase(), pmrAliases); + } + + if (name[0].GetBase() == "boost" && name.size() == 3 && !name[1].HasTemplateArguments() && + name[1].GetBase() == "container" && !name[2].HasTemplateArguments()) + { + static const unordered_set boostContainerAliases = { + "string", "wstring", "u8string", "u16string", "u32string" + }; + return IsAliasName(name[2].GetBase(), boostContainerAliases); + } + + return false; + } + + bool HasTemplateArgs(const DemangledQualifiedName& name) + { + return std::ranges::any_of(name, [](const auto& component) { return component.HasTemplateArguments(); }); + } + + // Compatibility parser for qualified names that do not come from a demangler AST. + // Native GNU3/MSVC paths use the structured DemangledTypeNode overloads. + size_t FindTemplateOpen(std::string_view component) + { + component = TrimSpaces(component); + if (StartsWith(component, "operator")) + { + static constexpr std::string_view operators[] = { + "operator<=>", "operator<<=", "operator>>=", "operator->*", "operator++", "operator--", + "operator<<", "operator>>", "operator<=", "operator>=", "operator==", "operator!=", + "operator+=", "operator-=", "operator*=", "operator/=", "operator%=", "operator&=", + "operator|=", "operator^=", "operator&&", "operator||", "operator()", "operator[]", + "operator->", "operator<", "operator>", "operator+", "operator-", "operator*", + "operator/", "operator%", "operator&", "operator|", "operator^", "operator~", + "operator!", "operator=", "operator," + }; + for (std::string_view op: operators) + { + if (component.size() > op.size() && component.compare(0, op.size(), op) == 0 && + component[op.size()] == '<') + return op.size(); + } + return std::string_view::npos; + } + int depth = 0; + for (size_t i = 0; i < component.size(); i++) + { + if (component[i] == '<') + { + if (depth == 0) + return i; + depth++; + } + else if (component[i] == '>' && depth > 0) + { + depth--; + } + } + return std::string_view::npos; + } + + vector SplitTopLevel(std::string_view s, char delimiter) + { + vector out; + int angleDepth = 0, parenDepth = 0, bracketDepth = 0; + size_t start = 0; + for (size_t i = 0; i < s.size(); i++) + { + char c = s[i]; + if (delimiter == ':' && i + 1 < s.size() && c == ':' && s[i + 1] == ':' && + angleDepth == 0 && parenDepth == 0 && bracketDepth == 0) + { + out.push_back(TrimSpaces(s.substr(start, i - start))); + i++; + start = i + 1; + continue; + } + if (delimiter != ':' && c == delimiter && angleDepth == 0 && parenDepth == 0 && bracketDepth == 0) + { + out.push_back(TrimSpaces(s.substr(start, i - start))); + start = i + 1; + continue; + } + switch (c) + { + case '<': angleDepth++; break; + case '>': if (angleDepth > 0) angleDepth--; break; + case '(': parenDepth++; break; + case ')': if (parenDepth > 0) parenDepth--; break; + case '[': bracketDepth++; break; + case ']': if (bracketDepth > 0) bracketDepth--; break; + default: break; + } + } + out.push_back(TrimSpaces(s.substr(start))); + return out; + } + + DemangledTypeNode ParseCompatibilityType(std::string_view s); + + ParamList ParseCompatibilityArgs(std::string_view s) + { + ParamList args; + for (std::string_view arg: SplitTopLevel(s, ',')) + { + DemangledTypeNode node = ParseCompatibilityType(arg); + args.push_back({"", DemangledTypeNode::CreateShared(std::move(node))}); + } + return args; + } + + DemangledQualifiedName ParseCompatibilityName(std::string_view s) + { + DemangledQualifiedName out; + for (std::string_view part: SplitTopLevel(s, ':')) + { + part = TrimSpaces(part); + size_t open = FindTemplateOpen(part); + if (open == std::string_view::npos) + { + out.emplace_back(part); + continue; + } + size_t close = part.rfind('>'); + if (close == std::string_view::npos || close < open) + { + out.emplace_back(part); + continue; + } + + std::string_view base = TrimSpaces(part.substr(0, open)); + DemangledNamePart segment{base}; + segment.SetTemplateArguments(ParseCompatibilityArgs(part.substr(open + 1, close - open - 1)), true); + out.push_back(std::move(segment)); + } + return out; + } + + DemangledTypeNode ParseCompatibilityType(std::string_view s) + { + s = StripLeadingTypeKeyword(s); + if (s.empty()) + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{""}); + if (s.find("::") == std::string_view::npos && FindTemplateOpen(s) == std::string_view::npos) + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{string(s)}); + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, ParseCompatibilityName(s)); + } + + StringList RenderSegments(const DemangledQualifiedName& name) + { + return DemangledTypeNode::NamedType(UnknownNamedTypeClass, name).RenderTypeNameSegments(); + } +} + +DemangledTemplateSimplifier::SimplifyNameResult DemangledTemplateSimplifier::SimplifyNameSegmentsInPlace( + DemangledQualifiedName& name) +{ + SimplifyNameResult result; + if (name.empty()) + return result; + + bool changed = false; + for (auto& component: name) + SimplifyTemplateArguments(component, changed); + changed |= ApplyNamespaceRules(name); + changed |= ApplyStdRules(name); + changed |= ApplyAbslRules(name); + changed |= ApplyBoostRules(name); + changed |= RewriteSimplifiedConstructorSegments(name); + + result.changed = changed; + result.topLevelIsAlias = IsTopLevelSimplifiedAlias(name); + return result; +} + +DemangledTemplateSimplifier::SimplifyNameResult DemangledTemplateSimplifier::SimplifyTypeNodeInPlace( + DemangledTypeNode& type) +{ + SimplifyNameResult result; + bool topLevelNameIsAlias = false; + result.changed = type.MutateQualifiedNames([&](DemangledQualifiedName& name) { + auto nameResult = SimplifyNameSegmentsInPlace(name); + if (nameResult.topLevelIsAlias) + { + topLevelNameIsAlias = true; + result.topLevelIsAlias = true; + } + return nameResult.changed; + }); + if (topLevelNameIsAlias && type.GetClass() == NamedTypeReferenceClass && type.GetNTRClass() != UnknownNamedTypeClass) + { + type.SetNTRType(UnknownNamedTypeClass); + result.changed = true; + } + if (type.MutateChildTypes([&](DemangledTypeNode& child) { + auto childResult = SimplifyTypeNodeInPlace(child); + if (childResult.topLevelIsAlias) + result.topLevelIsAlias = true; + return childResult.changed; + })) + result.changed = true; + return result; +} + +BN::QualifiedName DemangledTemplateSimplifier::SimplifyQualifiedName(const BN::QualifiedName& name) +{ + auto renderedName = name.GetString(); + auto stripped = StripLeadingTypeKeyword(std::string_view(renderedName.data(), renderedName.size())); + DemangledQualifiedName parsed = ParseCompatibilityName(stripped); + SimplifyNameSegmentsInPlace(parsed); + return BN::QualifiedName(RenderSegments(parsed)); +} + +bool DemangledTemplateSimplifier::NameSegmentsHaveTemplateArguments(const DemangledQualifiedName& name) +{ + return HasTemplateArgs(name); +} diff --git a/demangler/demangled_template_simplifier.h b/demangler/demangled_template_simplifier.h new file mode 100644 index 0000000000..b57c4f2814 --- /dev/null +++ b/demangler/demangled_template_simplifier.h @@ -0,0 +1,31 @@ +// Copyright 2016-2026 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "demangled_type_node.h" + +namespace DemangledTemplateSimplifier +{ + struct SimplifyNameResult + { + bool changed = false; + bool topLevelIsAlias = false; + }; + + BN::QualifiedName SimplifyQualifiedName(const BN::QualifiedName& name); + SimplifyNameResult SimplifyNameSegmentsInPlace(DemangledQualifiedName& name); + SimplifyNameResult SimplifyTypeNodeInPlace(DemangledTypeNode& type); + bool NameSegmentsHaveTemplateArguments(const DemangledQualifiedName& name); +} diff --git a/demangler/demangled_type_node.cpp b/demangler/demangled_type_node.cpp index afbebf457a..2b32bc17f2 100644 --- a/demangler/demangled_type_node.cpp +++ b/demangler/demangled_type_node.cpp @@ -16,10 +16,10 @@ #ifdef BINARYNINJACORE_LIBRARY #include "binaryview.h" #endif +#include "base/assertions.h" #include -#include -#include -#include +#include +#include #ifdef BINARYNINJACORE_LIBRARY using namespace BinaryNinjaCore; @@ -31,11 +31,73 @@ using namespace std; namespace { - static constexpr uint8_t DemangledPtr64Bit = 1u << 0; - static constexpr uint8_t DemangledUnalignedBit = 1u << 1; - static constexpr uint8_t DemangledRestrictBit = 1u << 2; - static constexpr uint8_t DemangledReferenceBit = 1u << 3; - static constexpr uint8_t DemangledLvalueBit = 1u << 4; + constexpr uint8_t DemangledPtr64Bit = 1u << 0; + constexpr uint8_t DemangledUnalignedBit = 1u << 1; + constexpr uint8_t DemangledRestrictBit = 1u << 2; + constexpr uint8_t DemangledReferenceBit = 1u << 3; + constexpr uint8_t DemangledLvalueBit = 1u << 4; + + class DemanglerFallbackArchitecture : public Architecture + { + public: + DemanglerFallbackArchitecture() : Architecture("demangler_fallback") {} + + BNEndianness GetEndianness() const override { return LittleEndian; } + size_t GetAddressSize() const override { return 8; } + size_t GetDefaultIntegerSize() const override { return 4; } + size_t GetInstructionAlignment() const override { return 1; } + size_t GetMaxInstructionLength() const override { return 1; } + size_t GetOpcodeDisplayLength() const override { return 1; } + +#ifdef BINARYNINJACORE_LIBRARY + bool GetInstructionInfo(const uint8_t*, uint64_t, size_t, InstructionInfo&) override { return false; } + bool GetInstructionText(const uint8_t*, uint64_t, size_t&, vector&) override { return false; } + bool GetInstructionTextWithContext(const uint8_t*, uint64_t, size_t&, void*, vector&) override { return false; } + bool GetInstructionLowLevelIL(const uint8_t*, uint64_t, size_t&, LowLevelILFunction&) override { return false; } + void AnalyzeBasicBlocks(Function&, BNBasicBlockAnalysisContext*) override {} + bool LiftFunction(LowLevelILFunction&, BNFunctionLifterContext*) override { return false; } + void FreeFunctionArchContext(void*) override {} + string GetRegisterName(uint32_t) override { return {}; } + string GetFlagName(uint32_t) override { return {}; } + string GetFlagWriteTypeName(uint32_t) override { return {}; } + string GetSemanticFlagClassName(uint32_t) override { return {}; } + string GetSemanticFlagGroupName(uint32_t) override { return {}; } + vector GetFullWidthRegisters() override { return {}; } + vector GetAllRegisters() override { return {}; } + vector GetAllFlags() override { return {}; } + vector GetAllFlagWriteTypes() override { return {}; } + vector GetAllSemanticFlagClasses() override { return {}; } + vector GetAllSemanticFlagGroups() override { return {}; } + BNFlagRole GetFlagRole(uint32_t, uint32_t = 0) override { return SpecialFlagRole; } + vector GetFlagsRequiredForFlagCondition(BNLowLevelILFlagCondition, uint32_t = 0) override { return {}; } + vector GetFlagsRequiredForSemanticFlagGroup(uint32_t) override { return {}; } + map GetFlagConditionsForSemanticFlagGroup(uint32_t) override { return {}; } + uint32_t GetSemanticClassForFlagWriteType(uint32_t) override { return 0; } + BNRegisterInfo GetRegisterInfo(uint32_t) override { return {}; } + uint32_t GetStackPointerRegister() override { return 0; } + BNIntrinsicClass GetIntrinsicClass(uint32_t) override { return GeneralIntrinsicClass; } + string GetIntrinsicName(uint32_t) override { return {}; } + vector GetAllIntrinsics() override { return {}; } + vector GetIntrinsicInputs(uint32_t) override { return {}; } + vector>> GetIntrinsicOutputs(uint32_t) override { return {}; } + bool CanAssemble() override { return false; } + bool Assemble(const string&, uint64_t, DataBuffer&, string&) override { return false; } + bool IsNeverBranchPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsAlwaysBranchPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsInvertBranchPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsSkipAndReturnZeroPatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool IsSkipAndReturnValuePatchAvailable(const uint8_t*, uint64_t, size_t) override { return false; } + bool ConvertToNop(uint8_t*, uint64_t, size_t) override { return false; } + bool AlwaysBranch(uint8_t*, uint64_t, size_t) override { return false; } + bool InvertBranch(uint8_t*, uint64_t, size_t) override { return false; } + bool SkipAndReturnValue(uint8_t*, uint64_t, size_t, uint64_t) override { return false; } + Architecture* RegisterArchitectureHook(BNCustomArchitecture*) override { return nullptr; } +#else + bool GetInstructionInfo(const uint8_t*, uint64_t, size_t, InstructionInfo&) override { return false; } + bool GetInstructionText(const uint8_t*, uint64_t, size_t&, vector&) override { return false; } + bool GetInstructionLowLevelIL(const uint8_t*, uint64_t, size_t&, LowLevelILFunction&) override { return false; } +#endif + }; static void AppendPointerSuffixToken(string& out, const char* token) { @@ -67,7 +129,7 @@ namespace } static void AppendTemplateArgumentList(string& out, const vector& args, - bool spaceAfterComma, Platform* platform) + bool spaceAfterComma, Platform& platform) { if (args.empty()) return; @@ -109,92 +171,44 @@ namespace return empty; } - static size_t ResolveAddressWidth(Platform* platform) + static size_t ResolveAddressWidth(const Platform& platform) { - if (platform) - return platform->GetAddressSize(); - return 8; + return platform.GetAddressSize(); } - static size_t ResolveDefaultIntegerWidth(Platform* platform) + static size_t ResolveDefaultIntegerWidth(const Platform& platform) { - if (platform) - { -#ifdef BINARYNINJACORE_LIBRARY - Architecture* platformArch = platform->GetArchitecture(); -#else - Ref platformArch = platform->GetArchitecture(); -#endif - if (platformArch) - return platformArch->GetDefaultIntegerSize(); - } - return 4; + auto platformArch = platform.GetArchitecture(); + return platformArch->GetDefaultIntegerSize(); } - static Ref ResolveCallingConvention(BNCallingConventionName cc, Platform* platform) + static Ref ResolveCallingConvention(BNCallingConventionName cc, const Platform& platform) { -#ifndef BINARYNINJACORE_LIBRARY - Ref platformArch; -#endif - Architecture* arch = nullptr; - if (platform) - { -#ifdef BINARYNINJACORE_LIBRARY - arch = platform->GetArchitecture(); -#else - platformArch = platform->GetArchitecture(); - arch = platformArch.GetPtr(); -#endif - } + auto platformArch = platform.GetArchitecture(); + Architecture* arch = platformArch; switch (cc) { case CdeclCallingConvention: - if (platform) - { - auto platformCC = platform->GetCdeclCallingConvention(); - if (platformCC) - return platformCC; - } - if (arch) - { - auto archCC = arch->GetCdeclCallingConvention(); - if (archCC) - return archCC; - } - return arch ? arch->GetCallingConventionByName("cdecl") : nullptr; + if (auto platformCC = platform.GetCdeclCallingConvention()) + return platformCC; + if (auto archCC = arch->GetCdeclCallingConvention()) + return archCC; + return arch->GetCallingConventionByName("cdecl"); case STDCallCallingConvention: - if (platform) - { - auto platformCC = platform->GetStdcallCallingConvention(); - if (platformCC) - return platformCC; - } - if (arch) - { - auto archCC = arch->GetStdcallCallingConvention(); - if (archCC) - return archCC; - } - return arch ? arch->GetCallingConventionByName("stdcall") : nullptr; + if (auto platformCC = platform.GetStdcallCallingConvention()) + return platformCC; + if (auto archCC = arch->GetStdcallCallingConvention()) + return archCC; + return arch->GetCallingConventionByName("stdcall"); case FastcallCallingConvention: - if (platform) - { - auto platformCC = platform->GetFastcallCallingConvention(); - if (platformCC) - return platformCC; - } - if (arch) - { - auto archCC = arch->GetFastcallCallingConvention(); - if (archCC) - return archCC; - } - return arch ? arch->GetCallingConventionByName("fastcall") : nullptr; + if (auto platformCC = platform.GetFastcallCallingConvention()) + return platformCC; + if (auto archCC = arch->GetFastcallCallingConvention()) + return archCC; + return arch->GetCallingConventionByName("fastcall"); case ThisCallCallingConvention: - if (arch) - return arch->GetCallingConventionByName("thiscall"); - return nullptr; + return arch->GetCallingConventionByName("thiscall"); default: return nullptr; } @@ -202,6 +216,13 @@ namespace } +Platform& GetDemanglerFallbackPlatform() +{ + static DemanglerFallbackArchitecture arch; + static auto platform = arch.GetStandalonePlatform(); + return *platform; +} + #define HAS_POINTER_SUFFIX(bit) ((m_pointerSuffixBits & (bit)) != 0) static const char* CallingConventionString[] = @@ -231,6 +252,18 @@ DemangledNamePart::DemangledNamePart(string base): } +DemangledNamePart::DemangledNamePart(const char* base): + m_base(base), m_hasTemplateArgs(false), m_spaceAfterTemplateComma(false) +{ +} + + +DemangledNamePart::DemangledNamePart(std::string_view base): + m_base(base), m_hasTemplateArgs(false), m_spaceAfterTemplateComma(false) +{ +} + + DemangledNamePart::DemangledNamePart(string base, std::shared_ptr baseTypeSuffix): m_base(std::move(base)), m_baseTypeSuffix(std::move(baseTypeSuffix)), m_hasTemplateArgs(false), m_spaceAfterTemplateComma(false) @@ -254,7 +287,15 @@ void DemangledNamePart::SetTemplateArguments(vector args } -void DemangledNamePart::AppendString(string& out, Platform* platform) const +void DemangledNamePart::ClearTemplateArguments() +{ + m_templateArgs.clear(); + m_hasTemplateArgs = false; + m_spaceAfterTemplateComma = false; +} + + +void DemangledNamePart::AppendString(string& out, Platform& platform) const { out += m_base; if (m_baseTypeSuffix) @@ -268,7 +309,7 @@ void DemangledNamePart::AppendString(string& out, Platform* platform) const } -string DemangledNamePart::GetString(Platform* platform) const +string DemangledNamePart::GetString(Platform& platform) const { string out; AppendString(out, platform); @@ -491,7 +532,7 @@ void DemangledTypeNode::SetImplicitThisParameter(DemangledTypeNode type) payload->implicitThisParameterType = CreateShared(std::move(type)); return; } - assert(false && "SetImplicitThisParameter called for non-function demangled type"); + BN_ASSERT(false && "SetImplicitThisParameter called for non-function demangled type"); } @@ -503,6 +544,14 @@ DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, return n; } +DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, + std::string_view nameSegment, size_t width, bool isSigned) +{ + DemangledQualifiedName nameSegments; + nameSegments.emplace_back(nameSegment); + return NamedType(cls, std::move(nameSegments), width, isSigned); +} + DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, DemangledQualifiedName nameSegments, size_t width, bool isSigned) { @@ -511,15 +560,20 @@ DemangledTypeNode DemangledTypeNode::NamedType(BNNamedTypeReferenceClass cls, return n; } -DemangledTypeNode DemangledTypeNode::NamedTypeWithDefaultIntegerWidth(BNNamedTypeReferenceClass cls, - StringList nameSegments, bool isSigned) +DemangledTypeNode DemangledTypeNode::NamedType(StringList nameSegments, size_t width, bool isSigned) { - DemangledTypeNode n = NamedType(cls, std::move(nameSegments), 0, isSigned); - if (auto payload = std::get_if(&n.m_payload)) - payload->widthKind = DefaultIntegerWidth; - return n; + return NamedType(UnknownNamedTypeClass, std::move(nameSegments), width, isSigned); } +DemangledTypeNode DemangledTypeNode::NamedType(std::string_view nameSegment, size_t width, bool isSigned) +{ + return NamedType(UnknownNamedTypeClass, nameSegment, width, isSigned); +} + +DemangledTypeNode DemangledTypeNode::NamedType(DemangledQualifiedName nameSegments, size_t width, bool isSigned) +{ + return NamedType(UnknownNamedTypeClass, std::move(nameSegments), width, isSigned); +} DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string suffix) { @@ -531,13 +585,29 @@ DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string suffix) DemangledTypeNode DemangledTypeNode::PostfixType(NodeRef child, string separator, NodeRef suffixType) { - DemangledTypeNode n = PostfixType(child, std::move(separator)); + DemangledTypeNode n = PostfixType(std::move(child), std::move(separator)); if (auto payload = std::get_if(&n.m_payload)) payload->suffixType = std::move(suffixType); return n; } +DemangledTypeNode DemangledTypeNode::UnaryExpression(string op, NodeRef child) +{ + DemangledTypeNode n; + n.m_payload = UnaryExpressionPayload{std::move(op), std::move(child)}; + return n; +} + + +DemangledTypeNode DemangledTypeNode::BinaryExpression(NodeRef left, string op, NodeRef right) +{ + DemangledTypeNode n; + n.m_payload = BinaryExpressionPayload{std::move(left), std::move(op), std::move(right)}; + return n; +} + + uint8_t DemangledTypeNode::PointerSuffixBit(BNPointerSuffix ps) { switch (ps) @@ -558,7 +628,7 @@ uint8_t DemangledTypeNode::PointerSuffixBit(BNPointerSuffix ps) } -size_t DemangledTypeNode::ResolveWidth(size_t width, WidthKind widthKind, Platform* platform) +size_t DemangledTypeNode::ResolveWidth(size_t width, WidthKind widthKind, const Platform& platform) { switch (widthKind) { @@ -591,7 +661,9 @@ BNTypeClass DemangledTypeNode::GetPayloadClass() const case 9: return FunctionTypeClass; case 10: case 11: - // PostfixPayload is an internal named-type rendering form, so it reports as a named type. + case 12: + case 13: + // Internal expression rendering forms report as named types so they can be carried as template args. return NamedTypeReferenceClass; default: return VoidTypeClass; @@ -611,10 +683,69 @@ DemangledTypeNode::NodeRef DemangledTypeNode::GetPrimaryChild() const return payload->returnType; if (auto payload = std::get_if(&m_payload)) return payload->childType; + if (auto payload = std::get_if(&m_payload)) + return payload->childType; + if (auto payload = std::get_if(&m_payload)) + return payload->leftType; return nullptr; } +bool DemangledTypeNode::MutateChildTypes(const std::function& mutator) +{ + bool changed = false; + auto mutateRef = [&](NodeRef& typeRef) { + if (!typeRef) + return; + DemangledTypeNode mutableType = *typeRef; + if (mutator(mutableType)) + { + typeRef = CreateShared(std::move(mutableType)); + changed = true; + } + }; + + if (auto payload = std::get_if(&m_payload)) + mutateRef(payload->childType); + else if (auto payload = std::get_if(&m_payload)) + mutateRef(payload->childType); + else if (auto payload = std::get_if(&m_payload)) + mutateRef(payload->childType); + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->returnType); + mutateRef(payload->implicitThisParameterType); + for (auto& param: payload->params) + mutateRef(param.type); + } + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->childType); + mutateRef(payload->suffixType); + } + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->childType); + } + else if (auto payload = std::get_if(&m_payload)) + { + mutateRef(payload->leftType); + mutateRef(payload->rightType); + } + return changed; +} + + +bool DemangledTypeNode::MutateQualifiedNames(const std::function& mutator) +{ + if (auto payload = std::get_if(&m_payload)) + return mutator(payload->ownerName); + else if (auto payload = std::get_if(&m_payload)) + return mutator(payload->name); + return false; +} + + bool DemangledTypeNode::AddQualifiersToPointerChild(bool cnst, bool vltl) { NodeRef* childType = nullptr; @@ -627,7 +758,7 @@ bool DemangledTypeNode::AddQualifiersToPointerChild(bool cnst, bool vltl) if (!*childType) return true; - if ((*childType).use_count() > 1) + if (childType->use_count() > 1) *childType = CreateSharedCopy(**childType); if (cnst) (*childType)->SetConst(true); @@ -649,7 +780,7 @@ DemangledQualifiedName& DemangledTypeNode::GetMutableName() { if (auto payload = std::get_if(&m_payload)) return payload->name; - assert(false && "GetMutableName called for non-named demangled type"); + BN_ASSERT(false && "GetMutableName called for non-named demangled type"); static thread_local DemangledQualifiedName empty; empty.clear(); return empty; @@ -663,7 +794,7 @@ void DemangledTypeNode::SetName(DemangledQualifiedName name) payload->name = std::move(name); return; } - assert(false && "SetName called for non-named demangled type"); + BN_ASSERT(false && "SetName called for non-named demangled type"); } @@ -675,6 +806,53 @@ BNNamedTypeReferenceClass DemangledTypeNode::GetNTRClass() const } +bool DemangledTypeNode::GetIntegerTypeInfo(size_t& width, WidthKind& widthKind, bool& isSigned, + std::string_view& altName) const +{ + width = 0; + widthKind = FixedWidth; + isSigned = false; + altName = {}; + if (auto payload = std::get_if(&m_payload)) + { + width = payload->width; + widthKind = payload->widthKind; + isSigned = payload->isSigned; + altName = std::string_view(payload->altName.data(), payload->altName.size()); + return true; + } + return false; +} + + +bool DemangledTypeNode::GetWideCharTypeInfo(size_t& width, std::string_view& altName) const +{ + width = 0; + altName = {}; + if (auto payload = std::get_if(&m_payload)) + { + width = payload->width; + altName = std::string_view(payload->altName.data(), payload->altName.size()); + return true; + } + return false; +} + + +bool DemangledTypeNode::GetPointerChildType(const DemangledTypeNode*& childType, BNReferenceType& referenceType) const +{ + childType = nullptr; + referenceType = PointerReferenceType; + if (auto payload = std::get_if(&m_payload)) + { + childType = payload->childType.get(); + referenceType = payload->referenceType; + return true; + } + return false; +} + + void DemangledTypeNode::SetNTRType(BNNamedTypeReferenceClass cls) { if (auto payload = std::get_if(&m_payload)) @@ -682,7 +860,7 @@ void DemangledTypeNode::SetNTRType(BNNamedTypeReferenceClass cls) payload->ntrClass = cls; return; } - assert(false && "SetNTRType called for non-named demangled type"); + BN_ASSERT(false && "SetNTRType called for non-named demangled type"); } @@ -693,7 +871,7 @@ void DemangledTypeNode::SetParenthesizedMemberPointer(bool parenthesized) payload->parenthesized = parenthesized; return; } - assert(false && "SetParenthesizedMemberPointer called for non-member-pointer demangled type"); + BN_ASSERT(false && "SetParenthesizedMemberPointer called for non-member-pointer demangled type"); } @@ -704,19 +882,94 @@ void DemangledTypeNode::SetCallingConventionName(BNCallingConventionName cc) payload->callingConventionName = cc; return; } - assert(false && "SetCallingConventionName called for non-function demangled type"); + BN_ASSERT(false && "SetCallingConventionName called for non-function demangled type"); } bool DemangledTypeNode::HasTemplateArguments() const { - const auto* payload = std::get_if(&m_payload); - if (!payload) + if (const auto* payload = std::get_if(&m_payload)) + return std::ranges::any_of(payload->name, &DemangledNamePart::HasTemplateArguments); + if (const auto* payload = std::get_if(&m_payload)) + return payload->childType && payload->childType->HasTemplateArguments(); + if (const auto* payload = std::get_if(&m_payload)) + return (payload->leftType && payload->leftType->HasTemplateArguments()) || + (payload->rightType && payload->rightType->HasTemplateArguments()); + return false; +} + + +bool DemangledTypeNode::ContainsNodeRef(const NodeRef& target) const +{ + if (!target) + return false; + + std::unordered_set visited; + std::function containsNode; + std::function containsRef; + + containsRef = [&](const NodeRef& ref) { + if (!ref) + return false; + if (ref == target) + return true; + return containsNode(ref.get()); + }; + + auto containsName = [&](const DemangledQualifiedName& name) { + for (const auto& part : name) + { + if (containsRef(part.m_baseTypeSuffix)) + return true; + for (const auto& arg : part.m_templateArgs) + { + if (containsRef(arg.type)) + return true; + } + } + return false; + }; + + auto containsParams = [&](const vector& params) { + for (const auto& param : params) + { + if (containsRef(param.type)) + return true; + } return false; - for (const auto& segment: payload->name) - if (segment.HasTemplateArguments()) + }; + + containsNode = [&](const DemangledTypeNode* node) { + if (!node) + return false; + if (node == target.get()) return true; - return false; + if (!visited.insert(node).second) + return false; + + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType) || containsName(payload->ownerName); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType); + if (auto payload = std::get_if(&node->m_payload)) + { + return containsRef(payload->returnType) || containsRef(payload->implicitThisParameterType) || + containsParams(payload->params); + } + if (auto payload = std::get_if(&node->m_payload)) + return containsName(payload->name); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType) || containsRef(payload->suffixType); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->childType); + if (auto payload = std::get_if(&node->m_payload)) + return containsRef(payload->leftType) || containsRef(payload->rightType); + return false; + }; + + return containsNode(this); } @@ -758,12 +1011,12 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons return true; }; - if (auto payload = std::get_if(&m_payload)) - return payload && std::get_if(&other.m_payload); - if (auto payload = std::get_if(&m_payload)) - return payload && std::get_if(&other.m_payload); - if (auto payload = std::get_if(&m_payload)) - return payload && std::get_if(&other.m_payload); + if (std::get_if(&m_payload)) + return std::get_if(&other.m_payload); + if (std::get_if(&m_payload)) + return std::get_if(&other.m_payload); + if (std::get_if(&m_payload)) + return std::get_if(&other.m_payload); if (auto payload = std::get_if(&m_payload)) { auto otherPayload = std::get_if(&other.m_payload); @@ -823,25 +1076,48 @@ bool DemangledTypeNode::IsStructurallyEqual(const DemangledTypeNode& other) cons typePtrsEqual(payload->childType, otherPayload->childType) && typePtrsEqual(payload->suffixType, otherPayload->suffixType); } + if (auto payload = std::get_if(&m_payload)) + { + auto otherPayload = std::get_if(&other.m_payload); + return otherPayload && payload->op == otherPayload->op && + typePtrsEqual(payload->childType, otherPayload->childType); + } + if (auto payload = std::get_if(&m_payload)) + { + auto otherPayload = std::get_if(&other.m_payload); + return otherPayload && payload->op == otherPayload->op && + typePtrsEqual(payload->leftType, otherPayload->leftType) && + typePtrsEqual(payload->rightType, otherPayload->rightType); + } return false; } -StringList DemangledTypeNode::RenderTypeNameSegments(Platform* platform) const +StringList DemangledTypeNode::RenderTypeNameSegments(Platform& platform) const { StringList result; - if (auto payload = std::get_if(&m_payload)) + if (std::get_if(&m_payload)) { result.push_back(GetString(platform)); return result; } - auto payload = std::get_if(&m_payload); - if (!payload) + if (std::get_if(&m_payload)) + { + result.push_back(GetString(platform)); + return result; + } + if (std::get_if(&m_payload)) + { + result.push_back(GetString(platform)); return result; - result.reserve(payload->name.size()); - for (const auto& segment: payload->name) - result.push_back(segment.GetString(platform)); + } + if (auto payload = std::get_if(&m_payload)) + { + result.reserve(payload->name.size()); + for (const auto& segment: payload->name) + result.push_back(segment.GetString(platform)); + } return result; } @@ -867,7 +1143,7 @@ bool DemangledTypeNode::HasPostfixType() const } -void DemangledTypeNode::AppendPostfixType(string& out, Platform* platform) const +void DemangledTypeNode::AppendPostfixType(string& out, Platform& platform) const { const auto* payload = std::get_if(&m_payload); if (!payload) @@ -880,13 +1156,41 @@ void DemangledTypeNode::AppendPostfixType(string& out, Platform* platform) const } +void DemangledTypeNode::AppendUnaryExpression(string& out, Platform& platform) const +{ + const auto* payload = std::get_if(&m_payload); + if (!payload) + return; + out += payload->op; + out += '('; + if (payload->childType) + payload->childType->AppendString(out, platform); + out += ')'; +} + + +void DemangledTypeNode::AppendBinaryExpression(string& out, Platform& platform) const +{ + const auto* payload = std::get_if(&m_payload); + if (!payload) + return; + out += '('; + if (payload->leftType) + payload->leftType->AppendString(out, platform); + out += ") "; + out += payload->op; + out += " ("; + if (payload->rightType) + payload->rightType->AppendString(out, platform); + out += ')'; +} + + void DemangledTypeNode::AppendModifiers(string& out) const { - if (m_const && m_volatile) - out += " const volatile"; - else if (m_const) + if (m_const) out += " const"; - else if (m_volatile) + if (m_volatile) out += " volatile"; } @@ -905,7 +1209,7 @@ void DemangledTypeNode::AppendPointerSuffix(string& out) const void DemangledTypeNode::AppendNamePartList( - string& out, const DemangledQualifiedName& name, Platform* platform) + string& out, const DemangledQualifiedName& name, Platform& platform) { if (name.empty()) return; @@ -918,14 +1222,14 @@ void DemangledTypeNode::AppendNamePartList( } -void DemangledTypeNode::AppendTypeName(string& out, Platform* platform) const +void DemangledTypeNode::AppendTypeName(string& out, Platform& platform) const { if (auto payload = std::get_if(&m_payload)) AppendNamePartList(out, payload->name, platform); } -string DemangledTypeNode::GetStringBeforeName(Platform* platform) const +string DemangledTypeNode::GetStringBeforeName(Platform& platform) const { string out; AppendBeforeName(out, nullptr, platform); @@ -933,7 +1237,7 @@ string DemangledTypeNode::GetStringBeforeName(Platform* platform) const } -string DemangledTypeNode::GetStringAfterName(Platform* platform) const +string DemangledTypeNode::GetStringAfterName(Platform& platform) const { string out; AppendAfterName(out, nullptr, platform); @@ -941,7 +1245,7 @@ string DemangledTypeNode::GetStringAfterName(Platform* platform) const } -void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* parentType, Platform* platform) const +void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* parentType, Platform& platform) const { switch (GetPayloadClass()) { @@ -1092,6 +1396,18 @@ void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* p AppendModifiers(out); break; } + if (std::get_if(&m_payload)) + { + AppendUnaryExpression(out, platform); + AppendModifiers(out); + break; + } + if (std::get_if(&m_payload)) + { + AppendBinaryExpression(out, platform); + AppendModifiers(out); + break; + } { const auto& payload = std::get(m_payload); switch (payload.ntrClass) @@ -1124,13 +1440,7 @@ void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* p } -static string FormatArrayCount(uint64_t elements) -{ - return string(fmt::format("{:#x}", elements)); -} - - -void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* parentType, Platform* platform) const +void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* parentType, Platform& platform) const { switch (GetPayloadClass()) { @@ -1180,7 +1490,7 @@ void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* pa const auto& payload = std::get(m_payload); if (parentType && parentType->GetPayloadClass() == PointerTypeClass) out += ")"; - out += "[" + FormatArrayCount(payload.elements) + "]"; + out += fmt::format("[{:#x}]", payload.elements); if (payload.childType) payload.childType->AppendAfterName(out, this, platform); break; @@ -1191,11 +1501,10 @@ void DemangledTypeNode::AppendAfterName(string& out, const DemangledTypeNode* pa } -void DemangledTypeNode::AppendString(string& out, Platform* platform) const +void DemangledTypeNode::AppendString(string& out, Platform& platform) const { - size_t beforeEnd = out.size(); AppendBeforeName(out, nullptr, platform); - beforeEnd = out.size(); // track where "before" ends + size_t beforeEnd = out.size(); // track where "before" ends string after; AppendAfterName(after, nullptr, platform); @@ -1215,13 +1524,7 @@ void DemangledTypeNode::AppendString(string& out, Platform* platform) const } -string DemangledTypeNode::GetString() const -{ - return GetString(nullptr); -} - - -string DemangledTypeNode::GetString(Platform* platform) const +string DemangledTypeNode::GetString(Platform& platform) const { string out; AppendString(out, platform); @@ -1229,13 +1532,7 @@ string DemangledTypeNode::GetString(Platform* platform) const } -string DemangledTypeNode::GetTypeAndName(const StringList& name) const -{ - return GetTypeAndName(name, nullptr); -} - - -string DemangledTypeNode::GetTypeAndName(const StringList& name, Platform* platform) const +string DemangledTypeNode::GetTypeAndName(const StringList& name, Platform& platform) const { const string before = GetStringBeforeName(platform); const string qName = JoinNameList(name); @@ -1253,6 +1550,10 @@ bool DemangledTypeNode::HasUndeterminedTopLevelSize() const return payload->widthKind == FixedWidth && payload->width == 0; if (std::holds_alternative(m_payload)) return true; + if (std::holds_alternative(m_payload)) + return true; + if (std::holds_alternative(m_payload)) + return true; if (auto payload = std::get_if(&m_payload)) return payload->childType && payload->childType->HasUndeterminedTopLevelSize(); return false; @@ -1265,7 +1566,7 @@ uint8_t DemangledTypeNode::GetValueConfidence() const } -Ref DemangledTypeNode::Finalize(Platform* platform) const +Ref DemangledTypeNode::Finalize(Platform& platform) const { switch (GetPayloadClass()) { @@ -1371,14 +1672,14 @@ Ref DemangledTypeNode::Finalize(Platform* platform) const if (payload.implicitThisParameterType) { Ref thisType = payload.implicitThisParameterType->Finalize(platform); - finalParams.push_back({"this", thisType->WithConfidence(payload.implicitThisParameterType->GetValueConfidence()), - DefaultLocationSource, Variable()}); + finalParams.emplace_back("this", thisType->WithConfidence(payload.implicitThisParameterType->GetValueConfidence()), + DefaultLocationSource, Variable()); } for (auto& p : payload.params) { Ref pType = p.type ? p.type->Finalize(platform) : Ref(Type::VoidType()); uint8_t pTypeConfidence = p.type ? p.type->GetValueConfidence() : BN_FULL_CONFIDENCE; - finalParams.push_back({p.name, pType->WithConfidence(pTypeConfidence), DefaultLocationSource, Variable()}); + finalParams.emplace_back(p.name, pType->WithConfidence(pTypeConfidence), DefaultLocationSource, Variable()); } Confidence> callingConvention; if (payload.callingConventionName != NoCallingConvention) @@ -1400,7 +1701,8 @@ Ref DemangledTypeNode::Finalize(Platform* platform) const case NamedTypeReferenceClass: { - if (auto payload = std::get_if(&m_payload)) + if (std::get_if(&m_payload) || std::get_if(&m_payload) || + std::get_if(&m_payload)) { QualifiedName name(RenderTypeNameSegments(platform)); TypeBuilder tb = TypeBuilder::NamedType( diff --git a/demangler/demangled_type_node.h b/demangler/demangled_type_node.h index 39573d94a0..90eb6a0f84 100644 --- a/demangler/demangled_type_node.h +++ b/demangler/demangled_type_node.h @@ -15,7 +15,6 @@ #pragma once #ifdef BINARYNINJACORE_LIBRARY -#include "qualifiedname.h" #include "type.h" #include "architecture.h" #ifndef BN @@ -40,8 +39,9 @@ #endif #endif -#include +#include #include +#include #include #ifdef BINARYNINJACORE_LIBRARY @@ -52,6 +52,8 @@ namespace BinaryNinja { class Platform; } using StringList = _STD_VECTOR<_STD_STRING>; +BN::Platform& GetDemanglerFallbackPlatform(); + class DemangledTypeNode; struct DemangledTypeNodeParam @@ -67,25 +69,36 @@ class DemangledNamePart DemangledNamePart(); explicit DemangledNamePart(_STD_STRING base); + explicit DemangledNamePart(const char* base); + explicit DemangledNamePart(std::string_view base); DemangledNamePart(_STD_STRING base, std::shared_ptr baseTypeSuffix); DemangledNamePart(_STD_STRING base, _STD_VECTOR templateArgs, bool spaceAfterComma = false); - const _STD_STRING& GetBase() const { return m_base; } + [[nodiscard]] const _STD_STRING& GetBase() const { return m_base; } void SetBase(_STD_STRING base) { m_base = std::move(base); } void AppendBase(const _STD_STRING& suffix) { m_base += suffix; } - bool HasTemplateArguments() const { return m_hasTemplateArgs || !m_templateArgs.empty(); } + void AppendBase(std::string_view suffix) + { + if (!suffix.empty()) + m_base.append(suffix.data(), suffix.size()); + } + [[nodiscard]] bool HasTemplateArguments() const { return m_hasTemplateArgs || !m_templateArgs.empty(); } + [[nodiscard]] const _STD_VECTOR& GetTemplateArguments() const { return m_templateArgs; } _STD_VECTOR& GetMutableTemplateArguments() { return m_templateArgs; } void SetTemplateArguments(_STD_VECTOR args, bool spaceAfterComma = false); + void ClearTemplateArguments(); - void AppendString(_STD_STRING& out, BN::Platform* platform) const; - _STD_STRING GetString(BN::Platform* platform = nullptr) const; - bool IsStructurallyEqual(const DemangledNamePart& other) const; + void AppendString(_STD_STRING& out, BN::Platform& platform) const; + _STD_STRING GetString(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + [[nodiscard]] bool IsStructurallyEqual(const DemangledNamePart& other) const; static Ref CreateShared(DemangledNamePart part); static Ref CreateSharedCopy(const DemangledNamePart& part); private: + friend class DemangledTypeNode; + _STD_STRING m_base; std::shared_ptr m_baseTypeSuffix; _STD_VECTOR m_templateArgs; @@ -140,27 +153,39 @@ class DemangledTypeNode std::nullptr_t, _STD_VECTOR params); static DemangledTypeNode NamedType(BNNamedTypeReferenceClass cls, StringList nameSegments, size_t width = 0, bool isSigned = false); + static DemangledTypeNode NamedType(BNNamedTypeReferenceClass cls, + std::string_view nameSegment, size_t width = 0, bool isSigned = false); static DemangledTypeNode NamedType(BNNamedTypeReferenceClass cls, DemangledQualifiedName nameSegments, size_t width = 0, bool isSigned = false); - static DemangledTypeNode NamedTypeWithDefaultIntegerWidth(BNNamedTypeReferenceClass cls, - StringList nameSegments, bool isSigned = false); + // Use when the mangling provides a name but not a struct/class/union/enum kind. + static DemangledTypeNode NamedType(StringList nameSegments, size_t width = 0, bool isSigned = false); + static DemangledTypeNode NamedType(std::string_view nameSegment, size_t width = 0, bool isSigned = false); + static DemangledTypeNode NamedType(DemangledQualifiedName nameSegments, size_t width = 0, bool isSigned = false); static DemangledTypeNode PostfixType(NodeRef child, _STD_STRING suffix); static DemangledTypeNode PostfixType(NodeRef child, _STD_STRING separator, NodeRef suffixType); + static DemangledTypeNode UnaryExpression(_STD_STRING op, NodeRef child); + static DemangledTypeNode BinaryExpression(NodeRef left, _STD_STRING op, NodeRef right); static NodeRef CreateShared(DemangledTypeNode node); static NodeRef CreateSharedCopy(const DemangledTypeNode& node); - BNTypeClass GetClass() const { return GetPayloadClass(); } - const DemangledQualifiedName& GetName() const; - DemangledQualifiedName& GetMutableName(); - bool IsConst() const { return m_const; } - bool IsVolatile() const { return m_volatile; } - BNNameType GetNameType() const { return m_nameType; } - bool HasTemplateArguments() const; - uint8_t GetPointerSuffixBits() const { return m_pointerSuffixBits; } - BNNamedTypeReferenceClass GetNTRClass() const; + [[nodiscard]] BNTypeClass GetClass() const { return GetPayloadClass(); } + [[nodiscard]] const DemangledQualifiedName& GetName() const; + [[nodiscard]] DemangledQualifiedName& GetMutableName(); + [[nodiscard]] bool IsConst() const { return m_const; } + [[nodiscard]] bool IsVolatile() const { return m_volatile; } + [[nodiscard]] BNNameType GetNameType() const { return m_nameType; } + [[nodiscard]] bool HasTemplateArguments() const; + [[nodiscard]] uint8_t GetPointerSuffixBits() const { return m_pointerSuffixBits; } + [[nodiscard]] BNNamedTypeReferenceClass GetNTRClass() const; + bool GetIntegerTypeInfo(size_t& width, WidthKind& widthKind, bool& isSigned, std::string_view& altName) const; + bool GetWideCharTypeInfo(size_t& width, std::string_view& altName) const; + bool GetPointerChildType(const DemangledTypeNode*& childType, BNReferenceType& referenceType) const; void SetParenthesizedMemberPointer(bool parenthesized); - StringList RenderTypeNameSegments(BN::Platform* platform = nullptr) const; - bool IsStructurallyEqual(const DemangledTypeNode& other) const; + [[nodiscard]] StringList RenderTypeNameSegments(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + [[nodiscard]] bool IsStructurallyEqual(const DemangledTypeNode& other) const; + [[nodiscard]] bool ContainsNodeRef(const NodeRef& target) const; + bool MutateChildTypes(const std::function& mutator); + bool MutateQualifiedNames(const std::function& mutator); void SetName(DemangledQualifiedName name); void SetConst(bool c) { m_const = c; } @@ -175,15 +200,13 @@ class DemangledTypeNode void SetNTRType(BNNamedTypeReferenceClass cls); void SetImplicitThisParameter(DemangledTypeNode type); - void AppendString(_STD_STRING& out, BN::Platform* platform) const; - _STD_STRING GetString() const; - _STD_STRING GetString(BN::Platform* platform) const; - _STD_STRING GetStringBeforeName(BN::Platform* platform) const; - _STD_STRING GetStringAfterName(BN::Platform* platform) const; - _STD_STRING GetTypeAndName(const StringList& name) const; - _STD_STRING GetTypeAndName(const StringList& name, BN::Platform* platform) const; + void AppendString(_STD_STRING& out, BN::Platform& platform) const; + _STD_STRING GetString(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + _STD_STRING GetStringBeforeName(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + _STD_STRING GetStringAfterName(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; + _STD_STRING GetTypeAndName(const StringList& name, BN::Platform& platform = GetDemanglerFallbackPlatform()) const; - BN::Ref Finalize(BN::Platform* platform = nullptr) const; + BN::Ref Finalize(BN::Platform& platform = GetDemanglerFallbackPlatform()) const; private: struct VoidPayload {}; @@ -253,6 +276,19 @@ class DemangledTypeNode NodeRef suffixType; }; + struct UnaryExpressionPayload + { + _STD_STRING op; + NodeRef childType; + }; + + struct BinaryExpressionPayload + { + NodeRef leftType; + _STD_STRING op; + NodeRef rightType; + }; + using Payload = std::variant< VoidPayload, BoolPayload, @@ -265,13 +301,15 @@ class DemangledTypeNode ArrayPayload, FunctionPayload, NamedTypePayload, - PostfixPayload>; + PostfixPayload, + UnaryExpressionPayload, + BinaryExpressionPayload>; - bool HasUndeterminedTopLevelSize() const; - uint8_t GetValueConfidence() const; - BNTypeClass GetPayloadClass() const; - NodeRef GetPrimaryChild() const; - static size_t ResolveWidth(size_t width, WidthKind widthKind, BN::Platform* platform = nullptr); + [[nodiscard]] bool HasUndeterminedTopLevelSize() const; + [[nodiscard]] uint8_t GetValueConfidence() const; + [[nodiscard]] BNTypeClass GetPayloadClass() const; + [[nodiscard]] NodeRef GetPrimaryChild() const; + static size_t ResolveWidth(size_t width, WidthKind widthKind, const BN::Platform& platform = GetDemanglerFallbackPlatform()); BNNameType m_nameType; uint8_t m_pointerSuffixBits; @@ -283,13 +321,15 @@ class DemangledTypeNode // Helpers for string formatting static uint8_t PointerSuffixBit(BNPointerSuffix ps); void AddPointerSuffixes(BN::TypeBuilder& tb, bool omitPtr64 = true) const; - bool HasPostfixType() const; - void AppendPostfixType(_STD_STRING& out, BN::Platform* platform) const; + [[nodiscard]] bool HasPostfixType() const; + void AppendPostfixType(_STD_STRING& out, BN::Platform& platform) const; + void AppendUnaryExpression(_STD_STRING& out, BN::Platform& platform) const; + void AppendBinaryExpression(_STD_STRING& out, BN::Platform& platform) const; void AppendModifiers(_STD_STRING& out) const; void AppendPointerSuffix(_STD_STRING& out) const; static void AppendNamePartList(_STD_STRING& out, const DemangledQualifiedName& name, - BN::Platform* platform); - void AppendTypeName(_STD_STRING& out, BN::Platform* platform) const; - void AppendBeforeName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform* platform) const; - void AppendAfterName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform* platform) const; + BN::Platform& platform); + void AppendTypeName(_STD_STRING& out, BN::Platform& platform) const; + void AppendBeforeName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform& platform) const; + void AppendAfterName(_STD_STRING& out, const DemangledTypeNode* parentType, BN::Platform& platform) const; }; diff --git a/demangler/gnu3/CMakeLists.txt b/demangler/gnu3/CMakeLists.txt index 02abad58a6..22bd623ce7 100644 --- a/demangler/gnu3/CMakeLists.txt +++ b/demangler/gnu3/CMakeLists.txt @@ -6,6 +6,10 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.cpp *.c *.h + ../demangled_log.h + ../demangled_template_simplifier.cpp + ../demangled_template_simplifier.h + ../demangled_reader.h ../demangled_type_node.cpp ../demangled_type_node.h) diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 2121533764..e8c9613225 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -16,7 +16,9 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_gnu3.h" -#include +#include "demangler/demangled_log.h" +#include "demangler/demangled_template_simplifier.h" +#include #include #include @@ -28,341 +30,391 @@ using namespace BinaryNinja; using namespace std; #endif +namespace +{ + void AppendStringView(string& out, std::string_view s) + { + if (!s.empty()) + out.append(s.data(), s.size()); + } -static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 1024; -static constexpr size_t MAX_DEMANGLE_NODE_LENGTH = 8192; -static BNTypeClass GetFinalizedTypeClass(const Ref& type) -{ + BNTypeClass GetFinalizedTypeClass(const Ref& type) + { #ifdef BINARYNINJACORE_LIBRARY - return type->GetTypeClass(); + return type->GetTypeClass(); #else - return type->GetClass(); + return type->GetClass(); #endif -} + } -#define hash(x,y) (64 * x + y) +#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); +#ifdef GNUDEMANGLE_DEBUG + void LogWithIndentation(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + _STD_STRING indentedFormat = DemangleLogIndentation::Apply(fmt); + PerformLog(0, DebugLog, "", 0, indentedFormat.c_str(), args); + va_end(args); + } -void MyLogDebug(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - PerformLog(0, DebugLog, "", 0, (_indent + fmt).c_str(), args); - va_end(args); -} +#define LOG_INDENTATION_SCOPE DemangleLogIndentationScope logIndentationScope #else -#define indent() -#define dedent() -#define MyLogDebug(...) do {} while(0) +#define LogWithIndentation(...) do {} while(0) +#define LOG_INDENTATION_SCOPE do {} while(0) #endif -static size_t TotalStringSize(const StringList& v) -{ - size_t n = 0; - for (const auto& s : v) - n += s.size(); - return n; -} - - -static string JoinNameSegments(const StringList& name) -{ - if (name.empty()) - return {}; - if (name.size() == 1) - return name[0]; - - string out; - out.reserve(TotalStringSize(name) + (name.size() - 1) * 2); - out += name[0]; - for (size_t i = 1; i < name.size(); i++) + size_t TotalStringSize(const StringList& v) { - out += "::"; - out += name[i]; + size_t n = 0; + for (const auto& s : v) + n += s.size(); + return n; } - return out; -} -static bool TemplateArgsReferenceTemplateParam(const string& raw) -{ - if (raw.empty() || (raw[0] != 'I' && raw[0] != 'J')) - return false; - - size_t i = 0; - size_t depth = 0; - while (i < raw.size()) + string JoinNameSegments(const StringList& name) { - char c = raw[i++]; - if (c == 'I' || c == 'J') - { - depth++; - continue; - } - if (c == 'E') - { - if (depth == 0) - return false; - depth--; - if (depth == 0) - return false; - continue; - } - if (c == 'T') - return true; - if (c >= '0' && c <= '9') + if (name.empty()) + return {}; + if (name.size() == 1) + return name[0]; + + string out; + out.reserve(TotalStringSize(name) + (name.size() - 1) * 2); + out += name[0]; + for (size_t i = 1; i < name.size(); i++) { - size_t len = c - '0'; - while (i < raw.size() && raw[i] >= '0' && raw[i] <= '9') - len = (len * 10) + (raw[i++] - '0'); - i = std::min(raw.size(), i + len); + out += "::"; + out += name[i]; } + return out; } - return false; -} - - -static DemangledNamePart NameSegmentWithTemplateArgs(const string& name, vector args) -{ - return DemangledNamePart(name, std::move(args), true); -} -static string GetOperator(char elm1, char elm2) -{ - switch (hash(elm1, elm2)) + bool TemplateArgsReferenceTemplateParam(std::string_view raw) { - case hash('d','c'): return "dynamic_cast"; - case hash('s','c'): return "static_cast"; - case hash('c','c'): return "const_cast"; - case hash('r','c'): return "reinterpret_cast"; - case hash('t','i'): return "typeid"; - case hash('t','e'): return "typeid"; - case hash('s','t'): return "sizeof"; - case hash('s','z'): return "sizeof"; - case hash('a','t'): return "alignof"; - case hash('a','z'): return "alignof"; - case hash('a','w'): return "co_await"; - case hash('n','x'): return "noexcept"; - case hash('s','Z'): return "sizeof..."; - case hash('s','P'): return "sizeof..."; - case hash('s','p'): return ""; - case hash('t','w'): return "throw"; - case hash('t','r'): return "throw"; - case hash('l','s'): return "<<"; // << - case hash('r','s'): return ">>"; // >> - case hash('a','S'): return "="; // = - case hash('n','t'): return "!"; // ! - case hash('e','q'): return "=="; // == - case hash('n','e'): return "!="; // != - case hash('i','x'): return "[]"; // [] - case hash('d','t'): return "."; // . - case hash('p','t'): return "->"; // -> - case hash('m','l'): return "*"; // * - case hash('p','p'): return "++"; // ++ (postfix in context) - case hash('m','m'): return "--"; // -- (postfix in context) - case hash('n','g'): return "-"; // - (unary) - case hash('m','i'): return "-"; // - - case hash('p','s'): return "+"; // + (unary) - case hash('p','l'): return "+"; // + - case hash('a','d'): return "&"; // & (unary) - case hash('a','n'): return "&"; // & - case hash('p','m'): return "->*"; // ->* - case hash('d','v'): return "/"; // / - case hash('r','m'): return "%"; // % - case hash('l','t'): return "<"; // < - case hash('l','e'): return "<="; // <= - case hash('g','t'): return ">"; // > - case hash('g','e'): return ">="; // >= - case hash('c','m'): return ","; // , - case hash('c','l'): return "()"; // () - case hash('c','o'): return "~"; // ~ - case hash('e','o'): return "^"; // ^ - case hash('o','r'): return "|"; // | - case hash('a','a'): return "&&"; // && - case hash('o','o'): return "||"; // || - case hash('d','e'): return "*"; // * (unary) - case hash('m','L'): return "*="; // *= - case hash('p','L'): return "+="; // += - case hash('m','I'): return "-="; // -= - case hash('d','V'): return "/="; // /= - case hash('r','M'): return "%="; // %= - case hash('r','S'): return ">>="; // >>= - case hash('l','S'): return "<<="; // <<= - case hash('a','N'): return "&="; // &= - case hash('o','R'): return "|="; // |= - case hash('e','O'): return "^="; // ^= - case hash('s','s'): return "<=>"; // <=> - case hash('d','l'): return "delete"; // delete - case hash('d','a'): return "delete[]"; // delete[] - case hash('n','w'): return "new"; // new - case hash('n','a'): return "new[]"; // new [] - default: return ""; + if (raw.empty() || (raw[0] != 'I' && raw[0] != 'J')) + return false; + + size_t i = 0; + size_t depth = 0; + while (i < raw.size()) + { + char c = raw[i++]; + if (c == 'I' || c == 'J') + { + depth++; + continue; + } + if (c == 'E') + { + if (depth == 0) + return false; + depth--; + if (depth == 0) + return false; + continue; + } + if (c == 'T') + return true; + if (c >= '0' && c <= '9') + { + size_t len = c - '0'; + while (i < raw.size() && raw[i] >= '0' && raw[i] <= '9') + len = (len * 10) + (raw[i++] - '0'); + i = std::min(raw.size(), i + len); + } + } + return false; } -} -static BNNameType GetNameType(char elm1, char elm2) -{ - switch (hash(elm1, elm2)) + + DemangledNamePart NameSegmentWithTemplateArgs(std::string_view name, vector args) { - case hash('n','t'): return OperatorNotNameType; // ! - case hash('n','g'): return OperatorMinusNameType; // - (unary) - case hash('p','s'): return OperatorPlusNameType; // + (unary) - case hash('a','d'): return OperatorBitAndNameType; // & (unary) - case hash('d','e'): return OperatorStarNameType; // * (unary) - case hash('i','x'): return OperatorArrayNameType; // [] - case hash('p','p'): return OperatorIncrementNameType; // ++ (postfix in context) - case hash('m','m'): return OperatorDecrementNameType; // -- (postfix in context) - case hash('l','s'): return OperatorLeftShiftNameType; // << - case hash('r','s'): return OperatorRightShiftNameType; // >> - case hash('a','S'): return OperatorAssignNameType; // = - case hash('e','q'): return OperatorEqualNameType; // == - case hash('n','e'): return OperatorNotEqualNameType; // != - case hash('p','t'): return OperatorArrowNameType; // -> - case hash('m','l'): return OperatorStarNameType; // * - case hash('m','i'): return OperatorMinusNameType; // - - case hash('p','l'): return OperatorPlusNameType; // + - case hash('a','n'): return OperatorBitAndNameType; // & - case hash('p','m'): return OperatorArrowStarNameType; // ->* - case hash('d','v'): return OperatorDivideNameType; // / - case hash('r','m'): return OperatorModulusNameType; // % - case hash('l','t'): return OperatorLessThanNameType; // < - case hash('l','e'): return OperatorLessThanEqualNameType; // <= - case hash('g','t'): return OperatorGreaterThanNameType; // > - case hash('g','e'): return OperatorGreaterThanEqualNameType; // >= - case hash('c','m'): return OperatorCommaNameType; // , - case hash('c','l'): return OperatorParenthesesNameType; // () - case hash('c','o'): return OperatorTildeNameType; // ~ - case hash('e','o'): return OperatorXorNameType; // ^ - case hash('o','r'): return OperatorBitOrNameType; // | - case hash('a','a'): return OperatorLogicalAndNameType; // && - case hash('o','o'): return OperatorLogicalOrNameType; // || - case hash('m','L'): return OperatorStarEqualNameType; // *= - case hash('p','L'): return OperatorPlusEqualNameType; // += - case hash('m','I'): return OperatorMinusEqualNameType; // -= - case hash('d','V'): return OperatorDivideEqualNameType; // /= - case hash('r','M'): return OperatorModulusEqualNameType; // %= - case hash('r','S'): return OperatorRightShiftEqualNameType; // >>= - case hash('l','S'): return OperatorLeftShiftEqualNameType; // <<= - case hash('a','N'): return OperatorAndEqualNameType; // &= - case hash('o','R'): return OperatorOrEqualNameType; // |= - case hash('e','O'): return OperatorXorEqualNameType; // ^= - case hash('d','l'): return OperatorDeleteNameType; // delete - case hash('d','a'): return OperatorDeleteArrayNameType; // delete[] - case hash('n','w'): return OperatorNewNameType; // new - case hash('n','a'): return OperatorNewArrayNameType; // new [] - // Note: C1-C5 (constructor) and D0-D5 (destructor) are handled directly - // by DemangleUnqualifiedName with their own case blocks, so they never - // reach GetNameType. - default: - return NoNameType; + return {string(name), std::move(args), true}; } -} - + void AppendStructuredNameSegments(DemangledQualifiedName& name, const StringList& segments) + { + for (const auto& segment: segments) + name.emplace_back(segment); + } -// Decode a big-endian hex string into a float or double. -// Returns the decimal string representation, or the raw hex with a type -// prefix if decoding fails or the result is NaN/Inf. -static string DecodeHexFloat(const string& hex, size_t byteCount) -{ - if (hex.size() != byteCount * 2) - return hex; - // Parse big-endian hex into an integer, then reinterpret as float/double - uint64_t bits = 0; - for (size_t i = 0; i < hex.size(); i++) + void SetStructuredExpressionNode(DemangledTypeNode* outNode, const DemangledQualifiedName& name) { - char c = hex[i]; - uint64_t nibble; - if (c >= '0' && c <= '9') nibble = c - '0'; - else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10; - else return hex; - bits = (bits << 4) | nibble; + if (outNode && !name.empty()) + { + if (name.size() > 1 && !name.back().HasTemplateArguments()) + { + DemangledQualifiedName baseName(name.begin(), name.end() - 1); + DemangledTypeNode baseType = DemangledTypeNode::NamedType(std::move(baseName)); + *outNode = DemangledTypeNode::PostfixType( + DemangledTypeNode::CreateShared(std::move(baseType)), "::" + name.back().GetString()); + return; + } + *outNode = DemangledTypeNode::NamedType(name); + } } - if (byteCount == 4) + + std::string_view GetOperator(char elm1, char elm2) { - union { uint32_t i; float f; } u; - u.i = (uint32_t)bits; - if (std::isnan(u.f) || std::isinf(u.f)) - return "(float)" + hex; - return to_string(u.f); - } - else if (byteCount == 8) + switch (hash(elm1, elm2)) + { + case hash('d','c'): return "dynamic_cast"; + case hash('s','c'): return "static_cast"; + case hash('c','c'): return "const_cast"; + case hash('r','c'): return "reinterpret_cast"; + case hash('t','i'): // fall through + case hash('t','e'): return "typeid"; + case hash('s','t'): // fall through + case hash('s','z'): return "sizeof"; + case hash('a','t'): // fall through + case hash('a','z'): return "alignof"; + case hash('a','w'): return "co_await"; + case hash('n','x'): return "noexcept"; + case hash('s','Z'): // fall through + case hash('s','P'): return "sizeof..."; + case hash('s','p'): return ""; + case hash('t','w'): // fall through + case hash('t','r'): return "throw"; + case hash('l','s'): return "<<"; // << + case hash('r','s'): return ">>"; // >> + case hash('a','S'): return "="; // = + case hash('n','t'): return "!"; // ! + case hash('e','q'): return "=="; // == + case hash('n','e'): return "!="; // != + case hash('i','x'): return "[]"; // [] + case hash('d','t'): return "."; // . + case hash('p','t'): return "->"; // -> + case hash('m','l'): return "*"; // * + case hash('p','p'): return "++"; // ++ (postfix in context) + case hash('m','m'): return "--"; // -- (postfix in context) + case hash('n','g'): // fall through - (unary) + case hash('m','i'): return "-"; // - + case hash('p','s'): // fall through + (unary) + case hash('p','l'): return "+"; // + + case hash('a','d'): // fall through & (unary) + case hash('a','n'): return "&"; // & + case hash('p','m'): return "->*"; // ->* + case hash('d','v'): return "/"; // / + case hash('r','m'): return "%"; // % + case hash('l','t'): return "<"; // < + case hash('l','e'): return "<="; // <= + case hash('g','t'): return ">"; // > + case hash('g','e'): return ">="; // >= + case hash('c','m'): return ","; // , + case hash('c','l'): return "()"; // () + case hash('c','o'): return "~"; // ~ + case hash('e','o'): return "^"; // ^ + case hash('o','r'): return "|"; // | + case hash('a','a'): return "&&"; // && + case hash('o','o'): return "||"; // || + case hash('d','e'): return "*"; // * (unary) + case hash('m','L'): return "*="; // *= + case hash('p','L'): return "+="; // += + case hash('m','I'): return "-="; // -= + case hash('d','V'): return "/="; // /= + case hash('r','M'): return "%="; // %= + case hash('r','S'): return ">>="; // >>= + case hash('l','S'): return "<<="; // <<= + case hash('a','N'): return "&="; // &= + case hash('o','R'): return "|="; // |= + case hash('e','O'): return "^="; // ^= + case hash('s','s'): return "<=>"; // <=> + case hash('d','l'): return "delete"; // delete + case hash('d','a'): return "delete[]"; // delete[] + case hash('n','w'): return "new"; // new + case hash('n','a'): return "new[]"; // new [] + default: return ""; + } + } + + + BNNameType GetNameType(char elm1, char elm2) { - union { uint64_t i; double d; } u; - u.i = bits; - if (std::isnan(u.d) || std::isinf(u.d)) - return "(double)" + hex; - return to_string(u.d); + switch (hash(elm1, elm2)) + { + case hash('n','t'): return OperatorNotNameType; // ! + case hash('n','g'): return OperatorMinusNameType; // - (unary) + case hash('p','s'): return OperatorPlusNameType; // + (unary) + case hash('a','d'): return OperatorBitAndNameType; // & (unary) + case hash('d','e'): return OperatorStarNameType; // * (unary) + case hash('i','x'): return OperatorArrayNameType; // [] + case hash('p','p'): return OperatorIncrementNameType; // ++ (postfix in context) + case hash('m','m'): return OperatorDecrementNameType; // -- (postfix in context) + case hash('l','s'): return OperatorLeftShiftNameType; // << + case hash('r','s'): return OperatorRightShiftNameType; // >> + case hash('a','S'): return OperatorAssignNameType; // = + case hash('e','q'): return OperatorEqualNameType; // == + case hash('n','e'): return OperatorNotEqualNameType; // != + case hash('p','t'): return OperatorArrowNameType; // -> + case hash('m','l'): return OperatorStarNameType; // * + case hash('m','i'): return OperatorMinusNameType; // - + case hash('p','l'): return OperatorPlusNameType; // + + case hash('a','n'): return OperatorBitAndNameType; // & + case hash('p','m'): return OperatorArrowStarNameType; // ->* + case hash('d','v'): return OperatorDivideNameType; // / + case hash('r','m'): return OperatorModulusNameType; // % + case hash('l','t'): return OperatorLessThanNameType; // < + case hash('l','e'): return OperatorLessThanEqualNameType; // <= + case hash('g','t'): return OperatorGreaterThanNameType; // > + case hash('g','e'): return OperatorGreaterThanEqualNameType; // >= + case hash('c','m'): return OperatorCommaNameType; // , + case hash('c','l'): return OperatorParenthesesNameType; // () + case hash('c','o'): return OperatorTildeNameType; // ~ + case hash('e','o'): return OperatorXorNameType; // ^ + case hash('o','r'): return OperatorBitOrNameType; // | + case hash('a','a'): return OperatorLogicalAndNameType; // && + case hash('o','o'): return OperatorLogicalOrNameType; // || + case hash('m','L'): return OperatorStarEqualNameType; // *= + case hash('p','L'): return OperatorPlusEqualNameType; // += + case hash('m','I'): return OperatorMinusEqualNameType; // -= + case hash('d','V'): return OperatorDivideEqualNameType; // /= + case hash('r','M'): return OperatorModulusEqualNameType; // %= + case hash('r','S'): return OperatorRightShiftEqualNameType; // >>= + case hash('l','S'): return OperatorLeftShiftEqualNameType; // <<= + case hash('a','N'): return OperatorAndEqualNameType; // &= + case hash('o','R'): return OperatorOrEqualNameType; // |= + case hash('e','O'): return OperatorXorEqualNameType; // ^= + case hash('d','l'): return OperatorDeleteNameType; // delete + case hash('d','a'): return OperatorDeleteArrayNameType; // delete[] + case hash('n','w'): return OperatorNewNameType; // new + case hash('n','a'): return OperatorNewArrayNameType; // new [] + // Note: C1-C5 (constructor) and D0-D5 (destructor) are handled directly + // by DemangleUnqualifiedName with their own case blocks, so they never + // reach GetNameType. + default: + return NoNameType; + } + } + + + // Decode a big-endian hex string into a float or double. + // Returns the decimal string representation, or the raw hex with a type + // prefix if decoding fails or the result is NaN/Inf. + string DecodeHexFloat(std::string_view hex, size_t byteCount) + { + if (hex.size() != byteCount * 2) + return string(hex); + + // Parse big-endian hex into an integer, then reinterpret as float/double + uint64_t bits = 0; + for (size_t i = 0; i < hex.size(); i++) + { + char c = hex[i]; + uint64_t nibble; + if (c >= '0' && c <= '9') nibble = c - '0'; + else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10; + else return string(hex); + bits = (bits << 4) | nibble; + } + + if (byteCount == 4) + { + union { uint32_t i; float f; } u; + u.i = static_cast(bits); + if (std::isnan(u.f) || std::isinf(u.f)) + { + string out = "(float)"; + AppendStringView(out, hex); + return out; + } + return to_string(u.f); + } + else if (byteCount == 8) + { + union { uint64_t i; double d; } u; + u.i = bits; + if (std::isnan(u.d) || std::isinf(u.d)) + { + string out = "(double)"; + AppendStringView(out, hex); + return out; + } + return to_string(u.d); + } + return string(hex); } - return hex; -} -// ===== Reader implementation (non-templated) ===== - -DemangleGNU3Reader::DemangleGNU3Reader(const string& data): m_data(data), m_offset(0) -{} + void ExtendTypeName(DemangledTypeNode& type, std::string_view extend) + { + if (type.GetClass() != NamedTypeReferenceClass) + return; + DemangledQualifiedName name = type.GetName(); + if (name.empty()) + { + name.emplace_back(extend); + type.SetName(std::move(name)); + return; + } -void DemangleGNU3Reader::Reset(const string& data) -{ - m_data = data; - m_offset = 0; -} + name.back().AppendBase(extend); + type.SetName(std::move(name)); + } -string DemangleGNU3Reader::PeekString(size_t count) -{ - if (count > Length()) - return "\0"; - return m_data.substr(m_offset, count); -} + void ApplyTemplateArgs(DemangledTypeNode& type, DemangleGNU3::ParamList args) + { + if (type.GetClass() != NamedTypeReferenceClass) + return; + DemangledQualifiedName qn = type.GetName(); + if (qn.empty()) + qn.emplace_back(""); + qn.back().SetTemplateArguments(std::move(args), true); + type.SetName(std::move(qn)); + } -#ifdef GNUDEMANGLE_DEBUG -string DemangleGNU3Reader::GetRaw() -{ - return m_data.substr(m_offset); -} -#endif + void AppendTypeName(DemangledTypeNode& type, const DemangledTypeNode& extend) + { + if (type.GetClass() != NamedTypeReferenceClass) + return; + DemangledQualifiedName newName = type.GetName(); + DemangledQualifiedName extendName = extend.GetName(); + newName.reserve(newName.size() + extendName.size()); + newName.insert(newName.end(), extendName.begin(), extendName.end()); + type.SetName(std::move(newName)); + } -string DemangleGNU3Reader::ReadString(size_t count) -{ - if (count > Length()) - throw DemangleException(); - if (count > MAX_DEMANGLE_NODE_LENGTH) - throw DemangleException("Demangled node exceeds maximum length"); - - const string out = m_data.substr(m_offset, count); - m_offset += count; - return out; -} + std::string_view LastTypeNameSegmentBase(const DemangledTypeNode& type) + { + const auto& qn = type.GetName(); + if (!qn.empty()) + return qn.back().GetBase(); + return {}; + } + bool LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNode& type) + { + const auto& qn = type.GetName(); + if (qn.empty()) + return false; + return qn.back().HasTemplateArguments(); + } +} // ===== DemangleGNU3 implementation ===== -DemangleGNU3::DemangleGNU3(Platform* platform, const string& mangledName) : - m_reader(mangledName), +DemangleGNU3::DemangleGNU3(Platform& platform, string mangledName) : + m_mangledName(std::move(mangledName)), + m_reader(m_mangledName, MAX_DEMANGLE_NODE_LENGTH, false), m_platform(platform), m_lastTypeRef(nullptr), m_isParameter(false), - m_shouldDeleteReader(true), m_topLevel(true), m_isOperatorOverload(false), m_parsingLambdaParams(false), @@ -371,41 +423,20 @@ DemangleGNU3::DemangleGNU3(Platform* platform, const string& mangledName) : m_inLocalName(false), m_nestingDepth(0) { - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); } - -DemangleGNU3::NestingGuard::NestingGuard(DemangleGNU3& demangler) : m_demangler(demangler) +void DemangleGNU3::Reset(Platform& platform, string mangledName) { - m_demangler.m_nestingDepth++; - if (m_demangler.m_nestingDepth > MAX_DEMANGLE_NESTING_DEPTH) - { - m_demangler.m_nestingDepth--; - throw DemangleException("Detected adversarial mangled string"); - } -} - - -DemangleGNU3::NestingGuard::~NestingGuard() -{ - m_demangler.m_nestingDepth--; -} - - -void DemangleGNU3::Reset(Platform* platform, const string& mangledName) -{ - m_reader.Reset(mangledName); - m_platform = platform; + m_mangledName = std::move(mangledName); + m_reader.Reset(m_mangledName); + m_platform = std::ref(platform); m_substitute.clear(); m_templateSubstitute.clear(); m_functionSubstitute.clear(); m_lastTypeRef = nullptr; m_lastName.clear(); - m_nameType = {}; - m_localType = {}; - m_hasReturnType = {}; m_isParameter = false; - m_shouldDeleteReader = true; m_topLevel = true; m_isOperatorOverload = false; m_parsingLambdaParams = false; @@ -417,108 +448,9 @@ void DemangleGNU3::Reset(Platform* platform, const string& mangledName) } -DemangledTypeNode DemangleGNU3::CreateUnknownType(const StringList& s) +void DemangleGNU3::PushEmptyTemplateParamSubstitution() { - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, s); -} - - -DemangledTypeNode DemangleGNU3::CreateUnknownType(const string& s) -{ - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{s}); -} - - -static DemangledQualifiedName CopyQualifiedName(const DemangledTypeNode& type) -{ - return type.GetName(); -} - - -void DemangleGNU3::ExtendTypeName(DemangledTypeNode& type, const string& extend) -{ - if (type.GetClass() != NamedTypeReferenceClass) - return; - - DemangledQualifiedName name = CopyQualifiedName(type); - if (name.empty()) - { - name.emplace_back(extend); - type.SetName(std::move(name)); - return; - } - - name.back().AppendBase(extend); - type.SetName(std::move(name)); -} - - -void DemangleGNU3::ApplyTemplateArgs(DemangledTypeNode& type, ParamList args) -{ - if (type.GetClass() != NamedTypeReferenceClass) - return; - - DemangledQualifiedName qn = CopyQualifiedName(type); - if (qn.empty()) - qn.emplace_back(""); - - qn.back().SetTemplateArguments(std::move(args), true); - type.SetName(std::move(qn)); -} - - -void DemangleGNU3::AppendTypeName(DemangledTypeNode& type, const DemangledTypeNode& extend) -{ - if (type.GetClass() != NamedTypeReferenceClass) - return; - - DemangledQualifiedName newName = CopyQualifiedName(type); - DemangledQualifiedName extendName = CopyQualifiedName(extend); - newName.reserve(newName.size() + extendName.size()); - newName.insert(newName.end(), extendName.begin(), extendName.end()); - type.SetName(std::move(newName)); -} - - -string DemangleGNU3::LastTypeNameSegmentBase(const DemangledTypeNode& type) -{ - const auto& qn = type.GetName(); - if (!qn.empty()) - return qn.back().GetBase(); - return {}; -} - - -bool DemangleGNU3::LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNode& type) -{ - const auto& qn = type.GetName(); - if (qn.empty()) - return false; - return qn.back().HasTemplateArguments(); -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(NodeRef type) -{ - if (type) - m_templateSubstitute.push_back(std::move(type)); - return type; -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(const DemangledTypeNode& type) -{ - auto ref = DemangledTypeNode::CreateSharedCopy(type); - m_templateSubstitute.push_back(ref); - return ref; -} - - -DemangleGNU3::NodeRef DemangleGNU3::PushTemplateType(DemangledTypeNode&& type) -{ - auto ref = DemangledTypeNode::CreateShared(std::move(type)); - m_templateSubstitute.push_back(ref); - return ref; + m_templateSubstitute.emplace_back(NodeRef::TemplateParamPack({})); } @@ -527,25 +459,17 @@ const DemangledTypeNode& DemangleGNU3::GetTemplateType(size_t ref) { if (ref >= m_templateSubstitute.size()) throw DemangleException(); - if (!m_templateSubstitute[ref]) + if (m_templateSubstitute[ref].emptyTemplatePack || !m_templateSubstitute[ref]) throw DemangleException(); return *m_templateSubstitute[ref]; } #endif -DemangleGNU3::NodeRef DemangleGNU3::PushType(NodeRef type) -{ - if (type) - m_substitute.push_back(std::move(type)); - return type; -} - - DemangleGNU3::NodeRef DemangleGNU3::PushType(const DemangledTypeNode& type) { auto ref = DemangledTypeNode::CreateSharedCopy(type); - m_substitute.push_back(ref); + m_substitute.emplace_back(ref); return ref; } @@ -553,7 +477,7 @@ DemangleGNU3::NodeRef DemangleGNU3::PushType(const DemangledTypeNode& type) DemangleGNU3::NodeRef DemangleGNU3::PushType(DemangledTypeNode&& type) { auto ref = DemangledTypeNode::CreateShared(std::move(type)); - m_substitute.push_back(ref); + m_substitute.emplace_back(ref); return ref; } @@ -562,7 +486,7 @@ DemangleGNU3::NodeRef DemangleGNU3::GetTypeRef(size_t ref) { if (ref >= m_substitute.size()) throw DemangleException(); - if (!m_substitute[ref]) + if (m_substitute[ref].emptyTemplatePack || !m_substitute[ref]) throw DemangleException(); return m_substitute[ref]; } @@ -580,13 +504,19 @@ void DemangleGNU3::PrintTables() LogDebug("Substitution Table\n"); for (int i = 0; (size_t)i < m_substitute.size(); i++) { - LogDebug("[%d] %s\n", i-1, GetType(i).GetString().c_str()); + if (m_substitute[i].emptyTemplatePack) + LogDebug("[%d] \n", i - 1); + else + LogDebug("[%d] %s\n", i - 1, GetType(i).GetString().c_str()); } LogDebug("Template Table\n"); for (int i = 0; (size_t)i < m_templateSubstitute.size(); i++) { - LogDebug("[%d] %s\n", i-1, GetTemplateType(i).GetString().c_str()); + if (m_templateSubstitute[i].emptyTemplatePack) + LogDebug("[%d] \n", i - 1); + else + LogDebug("[%d] %s\n", i - 1, GetTemplateType(i).GetString().c_str()); } } #endif @@ -596,40 +526,39 @@ void DemangleGNU3::DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct) { cnst = false; vltl = false; rstrct = false; //[] - while (1) - { - switch (m_reader.Peek()) - { - case 'r': rstrct = true; break; - case 'V': vltl = true; break; - case 'K': cnst = true; break; - default: return; - } - m_reader.Consume(1); + while (true) + { + if (m_reader.ConsumeIf('r')) + rstrct = true; + else if (m_reader.ConsumeIf('V')) + vltl = true; + else if (m_reader.ConsumeIf('K')) + cnst = true; + else + return; } } -string DemangleGNU3::DemangleSourceName() +std::string_view DemangleGNU3::DemangleSourceName() { - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); - m_lastName = m_reader.ReadString(DemangleNumber()); - dedent(); - return m_lastName; + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); + std::string_view name = m_reader.ReadStringView(DemangleNumber()); + m_lastName = name; + return name; } DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) { - NestingGuard nestingGuard(*this); - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + NestingGuard nestingGuard(m_nestingDepth); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); bool old_isparam; - if (m_reader.Peek() == 'Y') + if (m_reader.ConsumeIf('Y')) { // TODO: This function is external, should we do anything with that info? - m_reader.Consume(); } DemangledTypeNode retType = DemangleType(); @@ -638,21 +567,22 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) ParamList params; old_isparam = m_isParameter; m_isParameter = true; - m_functionSubstitute.push_back({}); + m_functionSubstitute.emplace_back(); [[maybe_unused]] int i = 0; - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { DemangledTypeNode param = DemangleType(); NodeRef paramRef = m_lastTypeRef; + if (AppendTemplateParamPackExpansion(params, paramRef, true)) + continue; if (param.GetClass() == VoidTypeClass) continue; - MyLogDebug("Var_%d - %s\n", i++, param.GetString().c_str()); + LogWithIndentation("Var_%d - %s\n", i++, param.GetString().c_str()); if (!paramRef) paramRef = DemangledTypeNode::CreateShared(std::move(param)); - m_functionSubstitute.back().push_back(paramRef); + m_functionSubstitute.back().emplace_back(paramRef); params.push_back({"", paramRef}); } - m_reader.Consume(); m_functionSubstitute.pop_back(); m_isParameter = old_isparam; if (!retTypeRef) @@ -665,8 +595,7 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) if (cnst || vltl) PushType(newType); - MyLogDebug("After %s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); - dedent(); + LogWithIndentation("After %s : %s\n", __FUNCTION__, m_reader.GetRaw()); return newType; } @@ -681,20 +610,25 @@ void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode&, const ParamLis continue; if (ref.index >= args.size() || !args[ref.index].type) throw DemangleException(); + if (args[ref.index].type->ContainsNodeRef(ref.typeRef)) + { + LogWarnF("Rejecting GNU3 demangle: recursive forward template reference T{}_", ref.index); + throw DemangleException("Detected recursive forward template reference"); + } *ref.typeRef = *args[ref.index].type; } m_pendingForwardRefs.clear(); } -DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef) +DemangleGNU3::NodeRef DemangleGNU3::DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef) { - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); if (outTypeRef) *outTypeRef = nullptr; size_t number = 0; - char elm = m_reader.Peek(); + char elm = m_reader.PeekOr(); if (elm == '_') { number = 0; @@ -702,7 +636,7 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef else if (isdigit(elm)) { size_t n = 0; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) n = n * 10 + (m_reader.Read() - '0'); number = n + 1; } @@ -716,19 +650,21 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef throw DemangleException(); } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) { throw DemangleException(); } - dedent(); if (number < m_templateSubstitute.size()) { - if (!m_templateSubstitute[number]) + const auto& entry = m_templateSubstitute[number]; + if (entry.emptyTemplatePack) + return entry; + if (!entry) throw DemangleException(); if (outTypeRef) - *outTypeRef = m_templateSubstitute[number]; - return *m_templateSubstitute[number]; + *outTypeRef = entry; + return entry; } // If forward template references are permitted (e.g. inside a cv conversion @@ -736,30 +672,47 @@ DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef // replaced once the outer template args are known. if (m_permitForwardTemplateRefs) { - auto typeRef = DemangledTypeNode::CreateShared(CreateUnknownType("auto")); + auto typeRef = DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType("auto")); m_pendingForwardRefs.push_back({number, typeRef}); if (outTypeRef) *outTypeRef = typeRef; - return *typeRef; + return typeRef; } if (m_parsingLambdaParams && number >= m_lambdaTemplateParamBase) { - auto typeRef = DemangledTypeNode::CreateShared(CreateUnknownType("auto")); + auto typeRef = DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType("auto")); if (outTypeRef) *outTypeRef = typeRef; - return *typeRef; + return typeRef; } throw DemangleException(); } +DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution(NodeRef* outTypeRef) +{ + NodeRef entry = DemangleTemplateSubstitutionEntry(outTypeRef); + if (entry.IsTemplateParamPack()) + { + if (outTypeRef) + *outTypeRef = entry; + if (entry.emptyTemplatePack) + return DemangledTypeNode::VoidType(); + return *entry; + } + if (entry.emptyTemplatePack || !entry) + throw DemangleException(); + return *entry; +} + + DemangledTypeNode DemangleGNU3::DemangleType() { - NestingGuard nestingGuard(*this); - indent(); - MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + NestingGuard nestingGuard(m_nestingDepth); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s : %s\n", __FUNCTION__, m_reader.GetRaw()); m_lastTypeRef = nullptr; DemangledTypeNode type; NodeRef typeRef = nullptr; @@ -786,12 +739,12 @@ DemangledTypeNode DemangleGNU3::DemangleType() { case 'S': { - if (isdigit(m_reader.Peek()) || m_reader.Peek() == '_' || isupper(m_reader.Peek())) + char next = m_reader.PeekOr(); + if (isdigit(next) || next == '_' || isupper(next)) { type = DemangleSubstitution(&typeRef); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); ApplyTemplateArgs(type, std::move(args)); @@ -801,11 +754,10 @@ DemangledTypeNode DemangleGNU3::DemangleType() } else { - if (m_reader.Peek() == 't') + if (m_reader.ConsumeIf('t')) { - m_reader.Consume(1); type = DemangleUnqualifiedName(); - auto qn = CopyQualifiedName(type); + DemangledQualifiedName qn = type.GetName(); qn.insert(qn.begin(), DemangledNamePart("std")); type.SetName(std::move(qn)); substitute = true; @@ -814,9 +766,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() { type = DemangleSubstitution(&typeRef); } - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); bool dependentTemplatePrefix = LastTypeNameSegmentBase(type) == "basic_ostream" && TemplateArgsReferenceTemplateParam("I" + m_reader.PeekString(m_reader.Length())); if (substitute && !dependentTemplatePrefix) @@ -837,23 +788,20 @@ DemangledTypeNode DemangleGNU3::DemangleType() ::= Tu # dependent elaborated type specifier using 'union' ::= Te # dependent elaborated type specifier using 'enum' */ - if (m_reader.Peek() == 's') + if (m_reader.ConsumeIf('s')) { - m_reader.Consume(); - type = DemangledTypeNode::NamedType(StructNamedTypeClass, StringList{DemangleSourceName()}); + type = DemangledTypeNode::NamedType(StructNamedTypeClass, DemangleSourceName()); break; } - else if (m_reader.Peek() == 'u') + else if (m_reader.ConsumeIf('u')) { - m_reader.Consume(); - type = DemangledTypeNode::NamedType(UnionNamedTypeClass, StringList{DemangleSourceName()}); + type = DemangledTypeNode::NamedType(UnionNamedTypeClass, DemangleSourceName()); break; } - else if (m_reader.Peek() == 'e') + else if (m_reader.ConsumeIf('e')) { - m_reader.Consume(); - type = DemangledTypeNode::NamedTypeWithDefaultIntegerWidth( - EnumNamedTypeClass, StringList{DemangleSourceName()}); + type = DemangledTypeNode::NamedType( + EnumNamedTypeClass, DemangleSourceName(), m_platform.get().GetArchitecture()->GetDefaultIntegerSize()); break; } @@ -862,10 +810,9 @@ DemangledTypeNode DemangleGNU3::DemangleType() // In forward-ref mode (cv conversion operator type parsing), do not consume // trailing IE — it belongs to the enclosing nested-name and will be // processed by DemangleNestedName's 'I' case, which resolves forward refs. - substitute = !m_permitForwardTemplateRefs; - if (!m_permitForwardTemplateRefs && m_reader.Peek() == 'I') + substitute = !m_permitForwardTemplateRefs && !typeRef.IsTemplateParamPack(); + if (!m_permitForwardTemplateRefs && m_reader.ConsumeIf('I')) { - m_reader.Consume(); if (substitute) PushType(type); ParamList args; @@ -914,17 +861,16 @@ DemangledTypeNode DemangleGNU3::DemangleType() // Commonly used for Objective-C block pointers: // U13block_pointer -> "void (params...) block_pointer" DemangledNamePart extName(DemangleSourceName()); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList targs; DemangleTemplateArgs(targs); if (!targs.empty()) extName.SetTemplateArguments(std::move(targs), true); } DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(inner)); - auto extType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, DemangledQualifiedName{std::move(extName)}); + NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); + auto extType = DemangledTypeNode::NamedType(DemangledQualifiedName{std::move(extName)}); NodeRef extNameRef = DemangledTypeNode::CreateShared(std::move(extType)); type = DemangledTypeNode::PostfixType(innerRef, " ", extNameRef); substitute = true; @@ -935,15 +881,14 @@ DemangledTypeNode DemangleGNU3::DemangleType() // Vendor extended type: u [] // e.g. u14__remove_cvref, u20__remove_reference_t DemangledNamePart extName(DemangleSourceName()); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList targs; DemangleTemplateArgs(targs); if (!targs.empty()) extName.SetTemplateArguments(std::move(targs), true); } - type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, DemangledQualifiedName{std::move(extName)}); + type = DemangledTypeNode::NamedType(DemangledQualifiedName{std::move(extName)}); substitute = true; break; } @@ -971,10 +916,10 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'M': // TODO: Make into pointer to function member { DemangledTypeNode memberName = DemangleType(); - NodeRef memberNameRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(memberName)); + NodeRef memberNameRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(memberName))); DemangledTypeNode member = DemangleType(); - NodeRef memberRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(member)); - type = DemangledTypeNode::MemberPointerType(memberRef, CopyQualifiedName(*memberNameRef), cnst, vltl); + NodeRef memberRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(member))); + type = DemangledTypeNode::MemberPointerType(memberRef, memberNameRef->GetName(), cnst, vltl); type.SetParenthesizedMemberPointer(true); substitute = true; break; @@ -987,34 +932,42 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'e': type = DemangledTypeNode::FloatType(16, "decimal128"); break; case 'f': type = DemangledTypeNode::FloatType(4, "decimal32"); break; case 'h': type = DemangledTypeNode::FloatType(2); break; + case 'u': type = DemangledTypeNode::IntegerType(1, false, "char8_t"); break; case 'i': type = DemangledTypeNode::WideCharType(4, "char32_t"); break; case 's': type = DemangledTypeNode::WideCharType(2, "char16_t"); break; - case 'a': type = CreateUnknownType("auto"); break; //auto type - case 'c': type = CreateUnknownType("decltype(auto)"); break; //decltype(auto) + case 'a': type = DemangledTypeNode::NamedType("auto"); break; //auto type + case 'c': type = DemangledTypeNode::NamedType("decltype(auto)"); break; //decltype(auto) case 'n': { static const StringList stdNullptrTName(vector{"std", "nullptr_t"}); - type = CreateUnknownType(stdNullptrTName); + type = DemangledTypeNode::NamedType(stdNullptrTName); break; } case 'p': { DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(inner)); + NodeRef innerRef = (m_lastTypeRef || m_lastTypeRef.IsTemplateParamPack()) ? + m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); + if (innerRef.IsTemplateParamPack()) + { + typeRef = NodeRef::TemplateParamPackExpansion(*innerRef.templatePack); + type = typeRef ? *typeRef : DemangledTypeNode::VoidType(); + break; + } type = DemangledTypeNode::PostfixType(innerRef, "..."); break; } case 't': case 'T': - type = CreateUnknownType("decltype(" + DemangleExpression() + ")"); - if (m_reader.Read() != 'E') + type = DemangledTypeNode::NamedType("decltype(" + DemangleExpression() + ")"); + if (!m_reader.ConsumeIf('E')) throw DemangleException(); break; case 'v': { // vector of size uint64_t size = DemangleNumber(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); NodeRef childRef = nullptr; DemangledTypeNode child = DemangleType(); @@ -1024,7 +977,7 @@ DemangledTypeNode DemangleGNU3::DemangleType() break; } default: - MyLogDebug("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("Unsupported type: %s:'%s'\n", __FUNCTION__, m_reader.GetRaw()); throw DemangleException(); } break; @@ -1035,11 +988,11 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'A': // ::= A _ // ::= A [] _ - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { // _ uint64_t size = DemangleNumber(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); NodeRef childRef = nullptr; DemangledTypeNode child = DemangleType(); @@ -1053,15 +1006,15 @@ DemangledTypeNode DemangleGNU3::DemangleType() //Since our type system doesn't support expressions as dimensions //we preserve the element type node and render a synthetic name at finalization. string dimension = "[]"; - if (m_reader.Peek() != '_') + if (m_reader.PeekOr() != '_') { dimension = "[" + DemangleExpression() + "]"; } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); DemangledTypeNode inner = DemangleType(); - NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(inner)); + NodeRef innerRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(inner))); type = DemangledTypeNode::PostfixType(innerRef, std::move(dimension)); } substitute = true; @@ -1071,16 +1024,15 @@ DemangledTypeNode DemangleGNU3::DemangleType() m_reader.UnRead(); type = DemangleName(); - string lastName = LastTypeNameSegmentBase(type); + std::string_view lastName = LastTypeNameSegmentBase(type); if (lastName.empty()) throw DemangleException(); m_lastName = lastName; substitute = true; - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { substitute = false; - m_reader.Consume(); PushType(type); ParamList args; DemangleTemplateArgs(args); @@ -1093,8 +1045,6 @@ DemangledTypeNode DemangleGNU3::DemangleType() if (substitute) typeRef = PushType(type); m_lastTypeRef = typeRef; - - dedent(); return type; } @@ -1111,10 +1061,9 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) static const StringList stdStringName(vector{"std", "string"}); static const StringList stdName(vector{"std"}); - indent() - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); - char elm; - elm = m_reader.Read(); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); + char elm = m_reader.Read(); StringList name; size_t number = 0; switch (elm) @@ -1138,14 +1087,14 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) // The actual substitution index = base36_value + 1. // This handles both single-char (S0_ ... SZ_) and // multi-char (S10_, S11_, ...) seq-ids. - size_t base36 = isdigit(elm) ? (size_t)(elm - '0') : (size_t)(elm - 'A' + 10); - while (m_reader.Peek() != '_') + size_t base36 = isdigit(elm) ? static_cast(elm - '0') : static_cast(elm - 'A' + 10); + while (m_reader.PeekOr() != '_') { char c = m_reader.Read(); if (isdigit(c)) - base36 = base36 * 36 + (size_t)(c - '0'); + base36 = base36 * 36 + static_cast(c - '0'); else if (isupper(c)) - base36 = base36 * 36 + (size_t)(c - 'A' + 10); + base36 = base36 * 36 + static_cast(c - 'A' + 10); else throw DemangleException(); } @@ -1157,15 +1106,31 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) throw DemangleException(); } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) { throw DemangleException(); } - - dedent(); - auto ref = GetTypeRef(number); + NodeRef ref; + if (number < m_substitute.size()) + { + ref = GetTypeRef(number); + } + else if (number == m_substitute.size() && m_reader.PeekOr() == 'I' && !m_substitute.empty()) + { + // GNU/libstdc++ pack expansions can elide an empty template-id that + // LLVM still effectively treats as occupying this substitution slot. + // When the next production immediately replaces template arguments, + // use the previous template-id as the prefix. + if (m_substitute.back().emptyTemplatePack || !m_substitute.back()) + throw DemangleException(); + ref = m_substitute.back(); + } + else + { + ref = GetTypeRef(number); + } const DemangledTypeNode& resolved = *ref; - string lastName = LastTypeNameSegmentBase(resolved); + std::string_view lastName = LastTypeNameSegmentBase(resolved); if (!lastName.empty()) m_lastName = lastName; if (outTypeRef) @@ -1173,21 +1138,19 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution(NodeRef* outTypeRef) return resolved; } m_lastName = name.back(); - dedent(); - return CreateUnknownType(name); + return DemangledTypeNode::NamedType(std::move(name)); } string DemangleGNU3::DemangleNumberAsString() { bool negativeFactor = false; - if (m_reader.Peek() == 'n') + if (m_reader.ConsumeIf('n')) { negativeFactor = true; - m_reader.Consume(); } string number; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) { number += m_reader.Read(); } @@ -1200,36 +1163,34 @@ string DemangleGNU3::DemangleNumberAsString() int64_t DemangleGNU3::DemangleNumber() { bool negative = false; - if (m_reader.Peek() == 'n') + if (m_reader.ConsumeIf('n')) { negative = true; - m_reader.Consume(); } - if (!isdigit(m_reader.Peek())) + if (!isdigit(m_reader.PeekOr())) throw DemangleException(); int64_t result = 0; do { result = result * 10 + (m_reader.Read() - '0'); - } while (isdigit(m_reader.Peek())); + } while (isdigit(m_reader.PeekOr())); return negative ? -result : result; } string DemangleGNU3::DemanglePrimaryExpression() { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); char elm1 = '\0'; string out; StringList tmpList; bool oldTopLevel; //expr-primary - if (m_reader.PeekString(2) == "_Z") + if (m_reader.ConsumeIf("_Z")) { - m_reader.Consume(2); // The embedded _Z... is an independent mangled name with its own // template scope. Save and clear the template substitution table // so inner T_ / T0_ etc. resolve within this symbol, not the outer @@ -1241,14 +1202,12 @@ string DemangleGNU3::DemanglePrimaryExpression() DemangledTypeNode t = DemangleSymbol(tmpList); m_topLevel = oldTopLevel; m_templateSubstitute = std::move(savedTemplateSubstitute); - out += t.GetTypeAndName(tmpList, m_platform.GetPtr()); - dedent() + out += t.GetTypeAndName(tmpList, m_platform); return out; } // LZE: function address template arg (GCC/Clang, without leading underscore) - if (m_reader.Peek() == 'Z') + if (m_reader.ConsumeIf('Z')) { - m_reader.Consume(); // 'Z' auto savedTemplateSubstitute2 = m_templateSubstitute; m_templateSubstitute.clear(); oldTopLevel = m_topLevel; @@ -1256,8 +1215,7 @@ string DemangleGNU3::DemanglePrimaryExpression() DemangledTypeNode t2 = DemangleSymbol(tmpList); m_topLevel = oldTopLevel; m_templateSubstitute = std::move(savedTemplateSubstitute2); - out += t2.GetTypeAndName(tmpList, m_platform.GetPtr()); - dedent(); + out += t2.GetTypeAndName(tmpList, m_platform); return out; } switch (m_reader.Read()) @@ -1302,40 +1260,75 @@ string DemangleGNU3::DemanglePrimaryExpression() break; } } - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); - - dedent(); return out; } -string DemangleGNU3::DemangleUnarySuffixExpression(const string& op) +string DemangleGNU3::DemangleUnarySuffixExpression(std::string_view op) { - return "(" + DemangleExpression() + ")" + op; + string out = "(" + DemangleExpression() + ")"; + out += op; + return out; } -string DemangleGNU3::DemangleUnaryPrefixExpression(const string& op) +string DemangleGNU3::DemangleUnaryPrefixExpression(std::string_view op, DemangledTypeNode* outNode) { - return op + "(" + DemangleExpression() + ")"; + DemangledTypeNode exprNode; + string expr = DemangleExpression(outNode ? &exprNode : nullptr); + if (outNode) + { + if (exprNode.GetClass() == VoidTypeClass) + exprNode = DemangledTypeNode::NamedType(expr); + *outNode = DemangledTypeNode::UnaryExpression( + string(op), DemangledTypeNode::CreateShared(std::move(exprNode))); + } + string out(op); + out += "("; + out += expr; + out += ")"; + return out; } -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(); - return lhs + " " + op + " " + rhs; +string DemangleGNU3::DemangleBinaryExpression(std::string_view op, DemangledTypeNode* outNode) +{ + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); + DemangledTypeNode lhsNode; + DemangledTypeNode rhsNode; + const string lhsExpr = DemangleExpression(outNode ? &lhsNode : nullptr); + const string rhsExpr = DemangleExpression(outNode ? &rhsNode : nullptr); + const string lhs = "(" + lhsExpr + ")"; + const string rhs = "(" + rhsExpr + ")"; + if (outNode) + { + if (lhsNode.GetClass() == VoidTypeClass) + lhsNode = DemangledTypeNode::NamedType(lhsExpr); + if (rhsNode.GetClass() == VoidTypeClass) + rhsNode = DemangledTypeNode::NamedType(rhsExpr); + *outNode = DemangledTypeNode::BinaryExpression( + DemangledTypeNode::CreateShared(std::move(lhsNode)), string(op), + DemangledTypeNode::CreateShared(std::move(rhsNode))); + } + string out = lhs; + out += " "; + out += op; + out += " "; + out += rhs; + return out; } -string DemangleGNU3::DemangleUnaryPrefixType(const string& op) +string DemangleGNU3::DemangleUnaryPrefixType(std::string_view op) { - return op + "(" + DemangleTypeString() + ")"; + string out(op); + out += "("; + out += DemangleTypeString(); + out += ")"; + return out; } @@ -1347,31 +1340,30 @@ string DemangleGNU3::DemangleTypeString() string DemangleGNU3::DemangleExpressionList() { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); string expr; bool first = true; - m_functionSubstitute.push_back({}); - while (m_reader.Peek() != 'E') + m_functionSubstitute.emplace_back(); + while (!m_reader.ConsumeIf('E')) { if (!first) expr += ", "; const string e = DemangleExpression(); expr += e; - m_functionSubstitute.back().push_back(DemangledTypeNode::CreateShared(CreateUnknownType(e))); + m_functionSubstitute.back().emplace_back( + DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType(e))); first = false; } m_functionSubstitute.pop_back(); - m_reader.Consume(); - dedent(); return expr; } DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() { - indent() - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode outType; char elm1 = m_reader.Read(); @@ -1422,9 +1414,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('o','R'): // |= case hash('e','O'): // ^= case hash('s','s'): // <=> - outType = CreateUnknownType("operator" + GetOperator(elm1, elm2)); + { + string name = "operator"; + name += GetOperator(elm1, elm2); + outType = DemangledTypeNode::NamedType(name); outType.SetNameType(GetNameType(elm1, elm2)); break; + } case hash('t','i'): case hash('t','e'): case hash('s','t'): @@ -1440,9 +1436,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('d','a'): // delete[] case hash('n','w'): // new case hash('n','a'): // new [] - outType = CreateUnknownType("operator " + GetOperator(elm1, elm2)); + { + string name = "operator "; + name += GetOperator(elm1, elm2); + outType = DemangledTypeNode::NamedType(name); outType.SetNameType(GetNameType(elm1, elm2)); break; + } case hash('v','0'): case hash('v','1'): case hash('v','2'): @@ -1460,7 +1460,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('C','3'): case hash('C','4'): case hash('C','5'): - outType = CreateUnknownType(m_lastName); + outType = DemangledTypeNode::NamedType(m_lastName); outType.SetNameType(ConstructorNameType); break; case hash('C','I'): // Inheriting constructor: CI1 or CI2 @@ -1472,7 +1472,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() string savedLastName = m_lastName; DemangleType(); m_lastName = savedLastName; - outType = CreateUnknownType(m_lastName); + outType = DemangledTypeNode::NamedType(m_lastName); outType.SetNameType(ConstructorNameType); break; } @@ -1482,14 +1482,12 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() case hash('D','3'): case hash('D','4'): case hash('D','5'): - outType = CreateUnknownType("~" + m_lastName); + outType = DemangledTypeNode::NamedType("~" + m_lastName); outType.SetNameType(DestructorNameType); break; case hash('D','t'): case hash('D','T'): - outType = CreateUnknownType(DemangleExpression()); - // if (m_reader.Read() != 'E') - // throw DemangleException(); + outType = DemangledTypeNode::NamedType(DemangleExpression()); break; case hash('U','l'): //Lambda { @@ -1509,16 +1507,17 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() if (param.GetClass() == VoidTypeClass) break; lambdaParams.push_back(std::move(param)); - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (m_reader.PeekOr() != 'E'); + if (!m_reader.ConsumeIf('E')) + throw DemangleException(); m_parsingLambdaParams = savedParsingLambdaParams; m_lambdaTemplateParamBase = savedLambdaTemplateParamBase; - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { name += DemangleNumberAsString(); } - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); name += "'("; @@ -1530,7 +1529,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() } name += ")"; m_lastName = name; - outType = CreateUnknownType(name); + outType = DemangledTypeNode::NamedType(name); PushType(outType); break; } @@ -1539,17 +1538,17 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() string name; name = "'unnamed"; - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { name += DemangleNumberAsString(); } name += "\'"; - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); m_lastName = name; - outType = CreateUnknownType(name); + outType = DemangledTypeNode::NamedType(name); break; } case hash('c','v'): //type (expression) @@ -1565,21 +1564,20 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() m_pendingForwardRefs.clear(); m_permitForwardTemplateRefs = true; DemangledTypeNode cvType = DemangleType(); - NodeRef cvTypeRef = m_lastTypeRef ? m_lastTypeRef : DemangledTypeNode::CreateShared(std::move(cvType)); + NodeRef cvTypeRef = m_lastTypeRef ? m_lastTypeRef : NodeRef(DemangledTypeNode::CreateShared(std::move(cvType))); m_permitForwardTemplateRefs = savedPermit; - outType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, - DemangledQualifiedName{DemangledNamePart("operator ", std::move(cvTypeRef))}); + outType = DemangledTypeNode::NamedType(DemangledQualifiedName{DemangledNamePart("operator ", std::move(cvTypeRef))}); break; } default: m_reader.UnRead(2); - if (isdigit(m_reader.Peek()) || m_reader.Read() == 'L') + if (isdigit(m_reader.PeekOr()) || m_reader.ConsumeIf('L')) { - string name = DemangleSourceName(); + string name(DemangleSourceName()); if (name.size() > 11 && name.substr(0, 11) == "_GLOBAL__N_") name = "(anonymous namespace)"; m_lastName = name; - outType = CreateUnknownType(name); + outType = DemangledTypeNode::NamedType(name); } else { @@ -1588,15 +1586,15 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() } // Consume ABI tags: B => [abi:tagname] // Applies to source names, operator names, and unnamed types. - while (m_reader.Peek() == 'B') + while (m_reader.ConsumeIf('B')) { - m_reader.Consume(); - string tag = "[abi:" + DemangleSourceName() + "]"; + string tag = "[abi:"; + AppendStringView(tag, DemangleSourceName()); + tag += "]"; ExtendTypeName(outType, tag); - string lastName = LastTypeNameSegmentBase(outType); - m_lastName = lastName.empty() ? tag : lastName; + std::string_view lastName = LastTypeNameSegmentBase(outType); + m_lastName = lastName.empty() ? tag : string(lastName); } - dedent(); return outType; } @@ -1609,69 +1607,66 @@ StringList DemangleGNU3::DemangleBaseUnresolvedName() // ::= dn # destructor or pseudo-destructor; // # e.g. ~X or ~X - indent() - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); StringList out; if (m_reader.Length() > 1) { - const string str = m_reader.PeekString(2); - if (str == "on") + if (m_reader.ConsumeIf("on")) { - m_reader.Consume(); m_reader.Consume(); // skip 'o','n' prefix char op1 = m_reader.Read(); char op2 = m_reader.Read(); - out.push_back(GetOperator(op1, op2)); - if (m_reader.Peek() == 'I') + out.emplace_back(GetOperator(op1, op2)); + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); out.back() = NameSegmentWithTemplateArgs(out.back(), std::move(args)).GetString(); - PushType(CreateUnknownType(out)); + PushType(DemangledTypeNode::NamedType(out)); } } - else if (str == "dn") + else if (m_reader.PeekMatch("dn")) { string name = DemangleUnresolvedType().GetString(); if (name.empty()) - out.push_back("~" + DemangleSourceName()); + { + string dtorName = "~"; + AppendStringView(dtorName, DemangleSourceName()); + out.push_back(std::move(dtorName)); + } else out.push_back("~" + name); } else { // - out.push_back(DemangleSourceName()); - if (m_reader.Peek() == 'I') + out.emplace_back(DemangleSourceName()); + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); out.back() = NameSegmentWithTemplateArgs(out.back(), std::move(args)).GetString(); } } } - dedent(); return out; } DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() { - indent(); - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); // ::= [ ] # T:: or T:: // ::= # decltype(p):: // ::= DemangledTypeNode type; - if (m_reader.Peek() == 'T') + if (m_reader.ConsumeIf('T')) { - m_reader.Consume(); type = DemangleTemplateSubstitution(); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { PushType(type); - m_reader.Consume(); ParamList args; DemangleTemplateArgs(args); ApplyTemplateArgs(type, std::move(args)); @@ -1685,32 +1680,28 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() PushType(type); } } - else if (m_reader.Length() > 2 && (m_reader.PeekString(2) == "Dt" || m_reader.PeekString(2) == "DT")) + else if (m_reader.Length() > 2 && (m_reader.ConsumeIf("Dt") || m_reader.ConsumeIf("DT"))) { - m_reader.Consume(); // 'D' - m_reader.Consume(); // 't' or 'T' const string name = "decltype(" + DemangleExpression() + ")"; - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); - type = CreateUnknownType(name); + type = DemangledTypeNode::NamedType(name); } - else if (m_reader.Peek() == 'S') + else if (m_reader.ConsumeIf('S')) { - m_reader.Consume(); type = DemangleSubstitution(); } else { throw DemangleException(); } - dedent(); return type; } -string DemangleGNU3::DemangleExpression() +string DemangleGNU3::DemangleExpression(DemangledTypeNode* outNode) { - MyLogDebug("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s: '%s'\n", __FUNCTION__, m_reader.GetRaw()); /* ::= ::= @@ -1765,7 +1756,10 @@ string DemangleGNU3::DemangleExpression() } else if (elm1 == 'T') // { - return DemangleTemplateSubstitution().GetString(); + DemangledTypeNode type = DemangleTemplateSubstitution(); + if (outNode) + *outNode = type; + return type.GetString(); } elm2 = m_reader.Read(); @@ -1793,10 +1787,10 @@ string DemangleGNU3::DemangleExpression() case hash('c','c'): case hash('r','c'): { - const string op = GetOperator(elm1, elm2); + std::string_view op = GetOperator(elm1, elm2); const string castType = DemangleTypeString(); const string castExpr = DemangleExpression(); - return op + "<" + castType + ">(" + castExpr + ")"; + return string(op) + "<" + castType + ">(" + castExpr + ")"; } case hash('t','i'): case hash('t','e'): @@ -1805,9 +1799,8 @@ string DemangleGNU3::DemangleExpression() case hash('a','t'): case hash('a','z'): case hash('n','x'): - return GetOperator(elm1, elm2) + "(" + DemangleTypeString() + ")"; case hash('s','Z'): - return GetOperator(elm1, elm2) + "(" + DemangleTypeString() + ")"; + return string(GetOperator(elm1, elm2)) + "(" + DemangleExpression() + ")"; case hash('s','P'): { ParamList args; @@ -1817,15 +1810,15 @@ string DemangleGNU3::DemangleExpression() case hash('s','p'): return "(" + DemangleExpression() + ")..."; case hash('t','w'): - return GetOperator(elm1, elm2) + DemangleExpression(); + return string(GetOperator(elm1, elm2)) + DemangleExpression(); case hash('t','r'): - return GetOperator(elm1, elm2); //rethrow + return string(GetOperator(elm1, elm2)); //rethrow case hash('n','t'): // ! case hash('n','g'): // - (unary) case hash('p','s'): // + (unary) case hash('a','d'): // & (unary) case hash('d','e'): // * (unary) - return DemangleUnaryPrefixExpression(GetOperator(elm1, elm2)); + return DemangleUnaryPrefixExpression(GetOperator(elm1, elm2), outNode); case hash('i','x'): // [] case hash('p','p'): // ++ (postfix in context) case hash('m','m'): // -- (postfix in context) @@ -1874,7 +1867,7 @@ string DemangleGNU3::DemangleExpression() case hash('a','N'): // &= case hash('o','R'): // |= case hash('e','O'): // ^= - return DemangleBinaryExpression(GetOperator(elm1, elm2)); + return DemangleBinaryExpression(GetOperator(elm1, elm2), outNode); case hash('d','l'): // delete case hash('d','a'): // delete[] case hash('n','w'): // new @@ -1892,26 +1885,25 @@ string DemangleGNU3::DemangleExpression() const string callable = DemangleExpression(); string args; bool firstArg = true; - m_functionSubstitute.push_back({}); - while (m_reader.Peek() != 'E') + m_functionSubstitute.emplace_back(); + while (!m_reader.ConsumeIf('E')) { if (!firstArg) args += ", "; const string e = DemangleExpression(); args += e; - m_functionSubstitute.back().push_back(DemangledTypeNode::CreateShared(CreateUnknownType(e))); + m_functionSubstitute.back().emplace_back( + DemangledTypeNode::CreateShared(DemangledTypeNode::NamedType(e))); firstArg = false; } m_functionSubstitute.pop_back(); - m_reader.Consume(); // 'E' return callable + "(" + args + ")"; } case hash('c','v'): //type (expression) { DemangledTypeNode type = DemangleType(); out = type.GetString(); - if (m_reader.Peek() == '_') + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(); // consume '_' delimiter before expression list out += " (" + DemangleExpressionList() + ")"; } else @@ -1926,7 +1918,7 @@ string DemangleGNU3::DemangleExpression() } case hash('i', 'l'): //{expr-list}, braced-init-list in any other context out = DemangleExpression(); - if (m_reader.Read() != 'E') + if (!m_reader.ConsumeIf('E')) throw DemangleException(); return out; case hash('f','p'): @@ -1949,16 +1941,15 @@ string DemangleGNU3::DemangleExpression() // type before function params are known), the fallback paths below produce // a placeholder string "fp" / "fpN". listNumber = DemangleNumber() + 1; - if (listNumber < 0 || m_reader.Read() != 'p') + if (listNumber < 0 || !m_reader.ConsumeIf('p')) throw DemangleException(); } DemangleCVQualifiers(cnst, vltl, rstrct); - elm = m_reader.Peek(); - if (elm == '_') + elm = m_reader.PeekOr(); + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(1); - if ((uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || - (size_t)elementNum >= m_functionSubstitute[listNumber].size()) + if (static_cast(listNumber) >= static_cast(m_functionSubstitute.size()) || + static_cast(elementNum) >= m_functionSubstitute[listNumber].size()) { // fp_ used before params are known (e.g., in decltype return type) out = (elementNum == 0) ? "fp" : "fp" + std::to_string(elementNum - 1); @@ -1971,11 +1962,11 @@ string DemangleGNU3::DemangleExpression() else if (isdigit(elm) || isupper(elm)) { elementNum = DemangleNumber() + 1; - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); if (elementNum < 0 || - (uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || - (size_t)elementNum >= m_functionSubstitute[listNumber].size()) + static_cast(listNumber) >= static_cast(m_functionSubstitute.size()) || + static_cast(elementNum) >= m_functionSubstitute[listNumber].size()) { // fpN_ used before params are known out = "fp" + std::to_string(elementNum - 1); @@ -1990,9 +1981,12 @@ string DemangleGNU3::DemangleExpression() throw DemangleException(); } out = type.GetString(); + if (outNode) + *outNode = type; break; } case hash('s','r'): + { /* ::= ::= # T::x / decltype(p)::x @@ -2012,39 +2006,50 @@ string DemangleGNU3::DemangleExpression() ::= dn # destructor or pseudo-destructor; # e.g. ~X or ~X */ - if (m_reader.Peek() == 'N') + DemangledQualifiedName structuredName; + if (m_reader.ConsumeIf('N')) { - m_reader.Consume(); // Standard form: N + E // where is T_, Dt, or S. // GCC extension: N + E // When the first component is a digit (source name), skip the // unresolved-type and let the loop below handle all qualifiers. - if (!isdigit(m_reader.Peek())) - out += DemangleUnresolvedType().GetString() + "::"; + if (!isdigit(m_reader.PeekOr())) + { + DemangledTypeNode unresolvedType = DemangleUnresolvedType(); + out += unresolvedType.GetString() + "::"; + AppendStructuredNameSegments(structuredName, unresolvedType.RenderTypeNameSegments()); + } do { - out += DemangleSourceName(); + std::string_view segName = DemangleSourceName(); + const size_t segmentStart = out.size(); + AppendStringView(out, segName); + DemangledNamePart structuredSegment(segName); // Push bare name (before template args) to substitution table. - PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); - if (m_reader.Peek() == 'I') + PushType(DemangledTypeNode::NamedType(StringList{out})); + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); // DemangleTemplateArgs(args); - out = NameSegmentWithTemplateArgs(out, std::move(args)).GetString(); + structuredSegment = NameSegmentWithTemplateArgs(segName, std::move(args)); + out.resize(segmentStart); + out += structuredSegment.GetString(); // Also push the template instantiation (name+args). - PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{out})); + PushType(DemangledTypeNode::NamedType(StringList{out})); } + structuredName.push_back(std::move(structuredSegment)); out += "::"; - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (!m_reader.ConsumeIf('E')); - out += JoinNameSegments(DemangleBaseUnresolvedName()); + StringList baseName = DemangleBaseUnresolvedName(); + out += JoinNameSegments(baseName); + AppendStructuredNameSegments(structuredName, baseName); + SetStructuredExpressionNode(outNode, structuredName); return out; } - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) { // + E // GCC sometimes omits the explicit qualifier-list 'E' when the last @@ -2060,112 +2065,192 @@ string DemangleGNU3::DemangleExpression() do { hadTemplateArgs = false; - const string segName = DemangleSourceName(); - out += segName; + std::string_view segName = DemangleSourceName(); + const size_t segmentStart = out.size(); + AppendStringView(out, segName); + DemangledNamePart structuredSegment(segName); // Push bare name to substitution table. - PushType(CreateUnknownType(out)); - if (m_reader.Peek() == 'I') + PushType(DemangledTypeNode::NamedType(out)); + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); DemangleTemplateArgs(args); // consumes the trailing 'E' - out = NameSegmentWithTemplateArgs(out, std::move(args)).GetString(); + structuredSegment = NameSegmentWithTemplateArgs(segName, std::move(args)); + out.resize(segmentStart); + out += structuredSegment.GetString(); // Also push the template instantiation. - PushType(CreateUnknownType(out)); + PushType(DemangledTypeNode::NamedType(out)); hadTemplateArgs = true; } + structuredName.push_back(std::move(structuredSegment)); out += "::"; - }while (!hadTemplateArgs && m_reader.Peek() != 'E'); + }while (!hadTemplateArgs && m_reader.PeekOr() != 'E'); // Consume qualifier-list 'E' if present. GCC sometimes omits it when // the last qualifier had template-args whose 'E' served double duty, // so check rather than unconditionally consuming. - if (m_reader.Peek() == 'E') - m_reader.Consume(); - out += JoinNameSegments(DemangleBaseUnresolvedName()); + m_reader.ConsumeIf('E'); + StringList baseName = DemangleBaseUnresolvedName(); + out += JoinNameSegments(baseName); + AppendStructuredNameSegments(structuredName, baseName); + SetStructuredExpressionNode(outNode, structuredName); return out; } else { - out += DemangleUnresolvedType().GetString() + "::"; + DemangledTypeNode unresolvedType = DemangleUnresolvedType(); + out += unresolvedType.GetString() + "::"; + AppendStructuredNameSegments(structuredName, unresolvedType.RenderTypeNameSegments()); // GCC may encode multi-level scoped names without the 'N' qualifier // prefix, e.g. "sr St 6__and_IE 5value" for std::__and_::value. // Process any digit-started names: if a name has template args AND // another source name follows, it is an intermediate qualifier level; // otherwise it is the final base-unresolved-name. - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) { - const string segName = DemangleSourceName(); - if (m_reader.Peek() == 'I') + std::string_view segName = DemangleSourceName(); + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); DemangleTemplateArgs(args); - if (isdigit(m_reader.Peek())) + DemangledNamePart structuredSegment = NameSegmentWithTemplateArgs(segName, args); + if (isdigit(m_reader.PeekOr())) { // Another source name follows — intermediate qualifier. // Push to the substitution table, mirroring what the // N-prefix sr branch does for each nested qualifier. string segment = NameSegmentWithTemplateArgs(segName, std::move(args)).GetString(); - PushType(CreateUnknownType(out + segment)); + PushType(DemangledTypeNode::NamedType(out + segment)); out += segment + "::"; + structuredName.push_back(std::move(structuredSegment)); } else { // No more source names — this template-id is the final name. out += NameSegmentWithTemplateArgs(segName, std::move(args)).GetString(); + structuredName.push_back(std::move(structuredSegment)); + SetStructuredExpressionNode(outNode, structuredName); return out; } } else { // Plain source name with no template args — final base name. - out += segName; + AppendStringView(out, segName); + structuredName.emplace_back(segName); + SetStructuredExpressionNode(outNode, structuredName); return out; } } // peek is not a digit: fall back for operator-names ("on") / destructor-names ("dn"). - out += JoinNameSegments(DemangleBaseUnresolvedName()); + StringList baseName = DemangleBaseUnresolvedName(); + out += JoinNameSegments(baseName); + AppendStructuredNameSegments(structuredName, baseName); + SetStructuredExpressionNode(outNode, structuredName); } return out; + } default: m_reader.UnRead(2); out = DemangleSourceName(); - if (m_reader.Peek() == 'I') + DemangledNamePart structuredSegment(out); + if (m_reader.ConsumeIf('I')) { ParamList args; - m_reader.Consume(); // DemangleTemplateArgs(args); - out = NameSegmentWithTemplateArgs(out, std::move(args)).GetString(); + structuredSegment = NameSegmentWithTemplateArgs(out, std::move(args)); + out = structuredSegment.GetString(); } + if (outNode) + *outNode = DemangledTypeNode::NamedType(DemangledQualifiedName{std::move(structuredSegment)}); break; } return out; } +bool DemangleGNU3::TryDemangleTemplateParamExpressionPackExpansion(string& expr, bool& emptyPack) +{ + if (!m_reader.ConsumeIf("spT")) + return false; + + NodeRef entry = DemangleTemplateSubstitutionEntry(); + if (entry.emptyTemplatePack) + { + emptyPack = true; + return true; + } + if (!entry) + throw DemangleException(); + + expr = entry->GetString(); + return true; +} + + +bool DemangleGNU3::AppendTemplateParamPackExpansion(ParamList& params, const NodeRef& expansion, bool functionParameter) +{ + if (!expansion.IsTemplateParamPackExpansion()) + return false; + + if (expansion.emptyTemplatePack) + { + m_substitute.emplace_back(NodeRef::EmptyTemplatePack()); + return true; + } + + for (const auto& arg : *expansion.templatePack) + { + if (!arg.type) + throw DemangleException(); + NodeRef paramRef = PushType(*arg.type); + m_lastTypeRef = paramRef; + if (functionParameter && !m_functionSubstitute.empty()) + m_functionSubstitute.back().push_back(paramRef); + params.push_back({"", paramRef}); + } + return true; +} + + bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) { DemangledTypeNode tmp; NodeRef tmpRef; bool tmpValid = false; - string expr; bool topLevel; switch (m_reader.Read()) { case 'L': + { + string expr; expr = DemanglePrimaryExpression(); - tmp = CreateUnknownType(expr); + tmp = DemangledTypeNode::NamedType(expr); tmpRef = DemangledTypeNode::CreateShared(std::move(tmp)); args.push_back({"", tmpRef}); tmpValid = true; - if (hadNonTypeArg) *hadNonTypeArg = true; + if (hadNonTypeArg) + *hadNonTypeArg = true; break; + } case 'X': { - DemangledTypeNode exprNode = CreateUnknownType(DemangleExpression()); - args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); - if (m_reader.Read() != 'E') + string expr; + DemangledTypeNode exprNode; + bool haveExprNode = false; + bool emptyPack = false; + if (!TryDemangleTemplateParamExpressionPackExpansion(expr, emptyPack)) + { + expr = DemangleExpression(&exprNode); + haveExprNode = true; + } + if (!emptyPack) + { + if (!haveExprNode || exprNode.GetClass() == VoidTypeClass) + exprNode = DemangledTypeNode::NamedType(expr); + args.push_back({"", DemangledTypeNode::CreateShared(std::move(exprNode))}); + } + if (!m_reader.ConsumeIf('E')) throw DemangleException(); if (hadNonTypeArg) *hadNonTypeArg = true; break; @@ -2174,13 +2259,19 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) case 'J': { size_t prevTemplateSize = m_templateSubstitute.size(); + size_t prevArgSize = args.size(); DemangleTemplateArgs(args, hadNonTypeArg); - if (m_topLevel && m_templateSubstitute.size() == prevTemplateSize) - PushTemplateType(CreateUnknownType("auto")); + if (m_topLevel) + { + ParamList packArgs(args.begin() + prevArgSize, args.end()); + while (m_templateSubstitute.size() > prevTemplateSize) + m_templateSubstitute.pop_back(); + m_templateSubstitute.emplace_back(NodeRef::TemplateParamPack(std::move(packArgs))); + } break; } case 'T': - if (m_reader.Peek() == 'n') + if (m_reader.ConsumeIf('n')) { // ::= // ::= Tn # non-type parameter @@ -2189,7 +2280,6 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) // for the following argument. Binary Ninja does not print those // synthetic parameter names, so consume the declaration type and // keep only the actual following template argument. - m_reader.Consume(); topLevel = m_topLevel; m_topLevel = false; DemangleType(); @@ -2203,14 +2293,16 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) m_topLevel = false; tmp = DemangleType(); m_topLevel = topLevel; + if (AppendTemplateParamPackExpansion(args, m_lastTypeRef, false)) + return true; tmpRef = DemangledTypeNode::CreateShared(std::move(tmp)); args.push_back({"", tmpRef}); tmpValid = true; } if (m_topLevel && tmpValid) { - MyLogDebug("Adding template ref: %s\n", tmpRef ? tmpRef->GetString().c_str() : ""); - PushTemplateType(tmpRef); + LogWithIndentation("Adding template ref: %s\n", tmpRef ? tmpRef->GetString().c_str() : ""); + m_templateSubstitute.emplace_back(tmpRef); } return true; } @@ -2218,25 +2310,22 @@ bool DemangleGNU3::DemangleTemplateArg(ParamList& args, bool* hadNonTypeArg) void DemangleGNU3::DemangleTemplateArgs(ParamList& args, bool* hadNonTypeArg) { - NestingGuard nestingGuard(*this); - indent(); - MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + NestingGuard nestingGuard(m_nestingDepth); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); const string lastName = m_lastName; - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { if (!DemangleTemplateArg(args, hadNonTypeArg)) break; } - m_reader.Consume(); m_lastName = lastName; - dedent(); - return; } DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bool pushBareTemplatePrefix) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); /* This can be either a qualified name like: "foo::bar::bas" or it can be a qualified type like: "foo::bar::bas & const" thus we return either @@ -2267,9 +2356,9 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo ::= */ - indent(); - MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); - DemangledTypeNode type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{}); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); + DemangledTypeNode type = DemangledTypeNode::NamedType(StringList{}); bool cnst = false, vltl = false, rstrct = false; bool ref = false; bool rvalueRef = false; @@ -2281,19 +2370,17 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo DemangleCVQualifiers(cnst, vltl, rstrct); //[] - if (m_reader.Peek() == 'R') + if (m_reader.ConsumeIf('R')) { - m_reader.Consume(); ref = true; } - else if (m_reader.Peek() == 'O') + else if (m_reader.ConsumeIf('O')) { - m_reader.Consume(); ref = true; rvalueRef = true; } - while (m_reader.Peek() != 'E') + while (!m_reader.ConsumeIf('E')) { isTemplate = false; substitute = true; @@ -2350,18 +2437,17 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo // We append them as "[abi:tag]" to the last name segment for display. // Save/restore m_lastName so that a following C1/D1 ctor/dtor name // still resolves to the class name, not the ABI tag string. - while (m_reader.Peek() == 'B') + while (m_reader.ConsumeIf('B')) { - m_reader.Consume(); string savedLastName = m_lastName; - string abiTag = DemangleSourceName(); + string abiTag(DemangleSourceName()); m_lastName = savedLastName; ExtendTypeName(type, "[abi:" + abiTag + "]"); } - bool dependentTemplatePrefix = !pushBareTemplatePrefix && m_reader.Peek() == 'I' && + bool dependentTemplatePrefix = !pushBareTemplatePrefix && m_reader.PeekOr() == 'I' && LastTypeNameSegmentBase(type) == "basic_ostream" && TemplateArgsReferenceTemplateParam(m_reader.PeekString(m_reader.Length())); - if (substitute && m_reader.Peek() != 'E' && !dependentTemplatePrefix) + if (substitute && m_reader.PeekOr() != 'E' && !dependentTemplatePrefix) { //Those template arguments were not the primary arguments so clear them from the sub listType while (m_templateSubstitute.size() > startSize) @@ -2370,9 +2456,8 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo } PushType(type); } - MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + LogWithIndentation("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw()); } - m_reader.Consume(); if (cnst || vltl || rstrct) { @@ -2387,16 +2472,15 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs, bo type.AddPointerSuffix(rvalueRef?LvalueSuffix:ReferenceSuffix); PushType(type); } - dedent(); return type; } DemangledTypeNode DemangleGNU3::DemangleLocalName() { - NestingGuard nestingGuard(*this); - indent(); - MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + NestingGuard nestingGuard(m_nestingDepth); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode type; StringList varName; // The local function has its own template scope. Save the outer template @@ -2413,25 +2497,23 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() type = DemangleSymbol(varName); m_inLocalName = savedInLocalName; - if (varName.size() > 0) - varName.back() += type.GetStringAfterName(m_platform.GetPtr()); + if (!varName.empty()) + varName.back() += type.GetStringAfterName(m_platform); else varName.push_back(type.GetString()); - if (m_reader.Peek() != 's') + if (!m_reader.ConsumeIf('s')) { // Handle default argument context: d [] _ - if (m_reader.Peek() == 'd') + if (m_reader.ConsumeIf('d')) { - m_reader.Consume(); - if (isdigit(m_reader.Peek())) + if (isdigit(m_reader.PeekOr())) DemangleNumber(); - if (m_reader.Peek() == '_') - m_reader.Consume(); + m_reader.ConsumeIf('_'); } // DemangledTypeNode tmpType = DemangleName(); - type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); + type = DemangledTypeNode::NamedType(varName); AppendTypeName(type, tmpType); type.SetNTRType(tmpType.GetNTRClass()); type.SetConst(tmpType.IsConst()); @@ -2442,21 +2524,18 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() } else { - m_reader.Consume(); - type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); + type = DemangledTypeNode::NamedType(varName); m_templateSubstitute = std::move(savedTemplateSubstitute); m_topLevel = oldTopLevel; } // [] //TODO: What do we do with discriminators? - if (m_reader.Peek() == '_') + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(); - if (m_reader.Peek() == '_') + if (m_reader.ConsumeIf('_')) { - m_reader.Consume(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); } else @@ -2464,16 +2543,15 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() DemangleNumberAsString(); } } - dedent(); return type; } DemangledTypeNode DemangleGNU3::DemangleName() { - NestingGuard nestingGuard(*this); - indent(); - MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); + NestingGuard nestingGuard(m_nestingDepth); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s '%s'\n", __FUNCTION__, m_reader.GetRaw()); /* ::= ::= @@ -2491,11 +2569,10 @@ DemangledTypeNode DemangleGNU3::DemangleName() switch (m_reader.Read()) { case 'S': - if (m_reader.Peek() == 't') + if (m_reader.ConsumeIf('t')) { - m_reader.Consume(1); type = DemangleUnqualifiedName(); - auto qn = CopyQualifiedName(type); + DemangledQualifiedName qn = type.GetName(); qn.insert(qn.begin(), DemangledNamePart("std")); type.SetName(std::move(qn)); substitute = true; @@ -2505,9 +2582,8 @@ DemangledTypeNode DemangleGNU3::DemangleName() type = DemangleSubstitution(); } - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { - m_reader.Consume(); if (substitute) PushType(type); ParamList args; @@ -2539,36 +2615,33 @@ DemangledTypeNode DemangleGNU3::DemangleName() ::= */ m_reader.UnRead(); - if (m_reader.Peek() == 'L') - m_reader.Consume(); + m_reader.ConsumeIf('L'); type = DemangleUnqualifiedName(); - if (m_reader.Peek() == 'I') + if (m_reader.ConsumeIf('I')) { PushType(type); // ParamList args; - m_reader.Consume(); // DemangleTemplateArgs(args); ApplyTemplateArgs(type, std::move(args)); } } - dedent(); return type; } -DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) +DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName, bool simplifyTemplates) { - NestingGuard nestingGuard(*this); - indent(); - MyLogDebug("%s: %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); + NestingGuard nestingGuard(m_nestingDepth); + LOG_INDENTATION_SCOPE; + LogWithIndentation("%s: %s\n", __FUNCTION__, m_reader.GetRaw()); DemangledTypeNode returnType; NodeRef returnTypeRef = nullptr; bool isReturnTypeUnknown = false; DemangledTypeNode type; ParamList params; - bool cnst = false, vltl = false, rstrct = false; + bool cnst = false, vltl = false; bool oldTopLevel; StringList name; @@ -2578,7 +2651,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) ::= */ // - switch (m_reader.Peek()) + switch (m_reader.PeekOr()) { case 'G': m_reader.Consume(); @@ -2595,15 +2668,13 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) DemangledTypeNode nameNode = DemangleName(); // Consume optional base-36 seq-id (digits + uppercase A-Z) before '_'. string seqId; - while (m_reader.Length() > 0 && m_reader.Peek() != '_') + while (m_reader.Length() > 0 && !m_reader.ConsumeIf('_')) seqId += m_reader.Read(); - if (m_reader.Length() > 0) - m_reader.Consume(); // consume '_' string result = "reference_temporary_for_" + nameNode.GetString(); if (!seqId.empty()) result += "[" + seqId + "]"; varName.push_back(result); - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); + return DemangledTypeNode::NamedType(varName); } case 'T': // transaction clone: GTt (safe) or GTn (non-safe) { @@ -2613,17 +2684,16 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform.GetPtr())}); + return DemangledTypeNode::NamedType(StringList{JoinNameSegments(name) + " [transaction clone]" + t.GetStringAfterName(m_platform)}); } case 'V': { // Disambiguate: Intel Vector Function ABI (_ZGV...) vs guard variable (_ZGV). // Intel Vector ABI isa codes: b c d e x y Y z Z // Guard variable encoding starts with: N (nested), L (local), S (substitution), digit, etc. - char peekChar = m_reader.Peek(); + char peekChar = m_reader.PeekOr(); bool isVectorABI = (peekChar == 'b' || peekChar == 'c' || peekChar == 'd' || peekChar == 'e' || peekChar == 'x' || peekChar == 'y' || peekChar == 'Y'); // 'z'/'Z' are ambiguous: also used as Z-local-name prefix in guard variables @@ -2635,11 +2705,11 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) // that don't appear in valid vparameter sequences. if (!isVectorABI && (peekChar == 'z' || peekChar == 'Z')) { - _STD_STRING ahead = m_reader.PeekString(std::min((size_t)32, m_reader.Length())); + _STD_STRING ahead = m_reader.PeekString(std::min(static_cast(32), m_reader.Length())); if (ahead.size() >= 3 && (ahead[1] == 'M' || ahead[1] == 'N')) { size_t pos = 2; - while (pos < ahead.size() && isdigit((unsigned char)ahead[pos])) + while (pos < ahead.size() && isdigit(static_cast(ahead[pos]))) pos++; if (pos > 2) // had at least one vlen digit { @@ -2650,7 +2720,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) { char c = ahead[pos]; if (c == 'v' || c == 'l' || c == 'u' || c == 'R' || - c == 'L' || c == 's' || isdigit((unsigned char)c)) + c == 'L' || c == 's' || isdigit(static_cast(c))) pos++; else { @@ -2665,8 +2735,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) if (!isVectorABI) { // Guard variable (original behavior) - DemangledTypeNode t = DemangleSymbol(name); - varName.push_back("guard_variable_for_" + t.GetTypeAndName(name, m_platform.GetPtr())); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); + varName.push_back("guard_variable_for_" + t.GetTypeAndName(name, m_platform)); type = DemangledTypeNode::IntegerType(1, false); if (m_reader.Length() == 0) return type; @@ -2676,18 +2746,18 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) bool first = true; do { - if (m_reader.Peek() == 'v') + if (m_reader.ConsumeIf('v')) { - m_reader.Consume(); break; } if (!first) paramList += ", "; paramList += DemangleTypeString(); - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (m_reader.PeekOr() != 'E'); + if (!m_reader.ConsumeIf('E')) + throw DemangleException(); varName.back() += paramList + ")"; - varName.push_back(DemangleSourceName()); + varName.emplace_back(DemangleSourceName()); return type; } @@ -2721,10 +2791,10 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) const char* maskName = (maskChar == 'M') ? "mask" : "nomask"; // Parse vlen: non-negative decimal integer - if (!isdigit(m_reader.Peek())) + if (!isdigit(m_reader.PeekOr())) throw DemangleException(); string vlenStr; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) vlenStr += m_reader.Read(); // Parse vparameters until '_' separator @@ -2734,7 +2804,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) // ::= empty | 'a' string paramsStr; bool firstParam = true; - while (m_reader.Length() > 0 && m_reader.Peek() != '_') + while (m_reader.Length() > 0 && m_reader.PeekOr() != '_') { if (!firstParam) paramsStr += ','; @@ -2755,16 +2825,15 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) if (hasStride) { - if (m_reader.Peek() == 's') + if (m_reader.ConsumeIf('s')) { // linear_step passed as another argument at given 0-based position - m_reader.Consume(); string argPos; - while (isdigit(m_reader.Peek())) + while (isdigit(m_reader.PeekOr())) argPos += m_reader.Read(); paramsStr += "(step=arg" + argPos + ")"; } - else if (isdigit(m_reader.Peek()) || m_reader.Peek() == 'n') + else if (isdigit(m_reader.PeekOr()) || m_reader.PeekOr() == 'n') { // Literal stride; 'n' prefix means negative string stride = DemangleNumberAsString(); @@ -2774,19 +2843,18 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) } // Optional alignment: 'a' - if (m_reader.Peek() == 'a') + if (m_reader.ConsumeIf('a')) { - m_reader.Consume(); - while (isdigit(m_reader.Peek())) - m_reader.Read(); + while (isdigit(m_reader.PeekOr())) + (void)m_reader.Read(); } } // Consume the '_' separator between parameters and routine name - if (m_reader.Length() == 0 || m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); - // Remainder is the scalar routine name (may be a plain C name or a _Z mangled name) + // Remainder is the scalar routine name (maybe a plain C name or a _Z mangled name) string routineName = m_reader.ReadString(m_reader.Length()); // Build the human-readable annotation @@ -2804,8 +2872,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) } annotation += ']'; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{routineName + annotation}); + return DemangledTypeNode::NamedType(StringList{routineName + annotation}); } default: throw DemangleException(); @@ -2835,16 +2902,16 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) if (kind == 'h') { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); } else if (kind == 'v') { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); } else @@ -2854,20 +2921,18 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) consumeCallOffset(); // return-value adjustment oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); + return DemangledTypeNode::NamedType(StringList{"covariant_return_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'C': { DemangledTypeNode t = DemangleType(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"construction_vtable_for_" + DemangleTypeString() + "-in-" + t.GetString()}); + return DemangledTypeNode::NamedType(StringList{"construction_vtable_for_" + DemangleTypeString() + "-in-" + t.GetString()}); } case 'D': LogWarn("Unsupported: 'typeinfo common proxy'\n"); @@ -2878,27 +2943,24 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) case 'h': //TODO: Convert to whatever the actual type is! { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); + return DemangledTypeNode::NamedType(StringList{"non-virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'H': // TLS init function { oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform.GetPtr())}); + return DemangledTypeNode::NamedType(StringList{"tls_init_function_for_" + t.GetTypeAndName(name, m_platform)}); } case 'I': - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"typeinfo_for_" + DemangleTypeString()}); + return DemangledTypeNode::NamedType(StringList{"typeinfo_for_" + DemangleTypeString()}); case 'J': LogWarn("Unsupported: 'java class'\n"); throw DemangleException(); @@ -2918,17 +2980,16 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) case 'v': // virtual thunk { DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); DemangleNumberAsString(); - if (m_reader.Read() != '_') + if (!m_reader.ConsumeIf('_')) throw DemangleException(); oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform.GetPtr())}); + return DemangledTypeNode::NamedType(StringList{"virtual_thunk_to_" + JoinNameSegments(name) + t.GetStringAfterName(m_platform)}); } case 'V': //Vtable return DemangledTypeNode::NamedType(StructNamedTypeClass, @@ -2937,10 +2998,9 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) { oldTopLevel = m_topLevel; m_topLevel = false; - DemangledTypeNode t = DemangleSymbol(name); + DemangledTypeNode t = DemangleSymbol(name, simplifyTemplates); m_topLevel = oldTopLevel; - return DemangledTypeNode::NamedType(UnknownNamedTypeClass, - StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform.GetPtr())}); + return DemangledTypeNode::NamedType(StringList{"tls_wrapper_function_for_" + t.GetTypeAndName(name, m_platform)}); } default: throw DemangleException(); @@ -2955,39 +3015,45 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) return type; } - if (m_reader.Peek() == 'E') + if (m_reader.ConsumeIf('E')) { - m_reader.Consume(); return type; } cnst = type.IsConst(); vltl = type.IsVolatile(); auto suffix = type.GetPointerSuffixBits(); - if (m_reader.Peek() == 'J') + if (m_reader.ConsumeIf('J')) { - m_reader.Consume(); // TODO: If we get here we have a return type. What can we do with this info? } // Consume any ABI tags on the function/data name (e.g. B5cxx11). // For nested names these are already consumed inside DemangleNestedName(); // this handles the global-scope case. - while (m_reader.Peek() == 'B') + while (m_reader.ConsumeIf('B')) { - m_reader.Consume(); string savedLastName = m_lastName; - string abiTag = DemangleSourceName(); + string abiTag(DemangleSourceName()); m_lastName = savedLastName; ExtendTypeName(type, "[abi:" + abiTag + "]"); } const bool nameRequiresReturnType = m_isParameter || LastTypeNameSegmentHasTemplateArguments(type); - varName = type.RenderTypeNameSegments(m_platform.GetPtr()); + if (simplifyTemplates) + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); + varName = type.RenderTypeNameSegments(m_platform); + BNNameType nameType = type.GetNameType(); if (m_isOperatorOverload || - type.GetNameType() == ConstructorNameType || - type.GetNameType() == DestructorNameType) + nameType == ConstructorNameType || + nameType == DestructorNameType || + nameType == OperatorDeleteNameType || + nameType == OperatorDeleteArrayNameType) { returnType = DemangledTypeNode::VoidType(); } + else if (nameType == OperatorNewNameType || nameType == OperatorNewArrayNameType) + { + returnType = DemangledTypeNode::PointerType(DemangledTypeNode::VoidType(), false, false, PointerReferenceType); + } else if (nameRequiresReturnType) { returnType = DemangleType(); @@ -2999,15 +3065,14 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) returnType = DemangledTypeNode::AddressSizedIntegerType(true); } - m_functionSubstitute.push_back({}); + m_functionSubstitute.emplace_back(); while (m_reader.Length() > 0) { - if (m_reader.Peek() == 'E') + if (m_reader.ConsumeIf('E')) { - m_reader.Consume(); break; } - if (m_reader.Peek() == '.') + if (m_reader.PeekOr() == '.') { // Extension, consume the rest string ext = m_reader.ReadString(m_reader.Length()); @@ -3018,22 +3083,23 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) else if (ext == ".debug_frame") ext = "debug frame"; // On the off chance some invalid mangled string is passed in. - if (varName.size() > 0) + if (!varName.empty()) varName.back() += " " + ext; break; } m_isParameter = true; - MyLogDebug("Var: %s\n", m_reader.GetRaw().c_str()); - if (m_reader.PeekString(2) == "@@") + LogWithIndentation("Var: %s\n", m_reader.GetRaw()); + if (m_reader.PeekMatch("@@")) break; DemangledTypeNode param = DemangleType(); NodeRef paramRef = m_lastTypeRef; + if (AppendTemplateParamPackExpansion(params, paramRef, true)) + continue; if (param.GetClass() == VoidTypeClass) { - if (m_reader.Peek() == 'E') + if (m_reader.ConsumeIf('E')) { - m_reader.Consume(); break; } break; @@ -3045,10 +3111,7 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) params.push_back({"", paramRef}); if (isVarArgs) { - if (m_reader.Peek() == 'E') - { - m_reader.Consume(); - } + m_reader.ConsumeIf('E'); break; } @@ -3065,23 +3128,21 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(StringList& varName) type.SetPointerSuffixBits(suffix); type.SetConst(cnst); type.SetVolatile(vltl); - if (rstrct) - type.SetPointerSuffixBits(1u << RestrictSuffix); + if (simplifyTemplates) + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); // PrintTables(); - MyLogDebug("Done: %s%s%s\n", type.GetStringBeforeName(m_platform.GetPtr()).c_str(), JoinNameSegments(varName).c_str(), - type.GetStringAfterName(m_platform.GetPtr()).c_str()); - - dedent(); + LogWithIndentation("Done: %s%s%s\n", type.GetStringBeforeName(m_platform).c_str(), JoinNameSegments(varName).c_str(), + type.GetStringAfterName(m_platform).c_str()); return type; } // ===== Non-templated static methods ===== -bool DemangleGNU3Static::IsGNU3MangledString(const string& name) +bool DemangleGNU3Static::IsGNU3MangledString(std::string_view name) { - string headerless = name; + string headerless(name); string header; if (DemangleGlobalHeader(headerless, header)) return true; @@ -3095,12 +3156,17 @@ bool DemangleGNU3Static::IsGNU3MangledString(const string& name) bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) { + if (name.empty()) + return false; + size_t strippedCount = 0; string encoded = name; - while (encoded[0] == '_') + while (!encoded.empty() && encoded[0] == '_') { encoded.erase(0, 1); strippedCount ++; + if (encoded.empty()) + return false; } if (strippedCount == 0) @@ -3128,43 +3194,48 @@ bool DemangleGNU3Static::DemangleGlobalHeader(string& name, string& header) namespace { - static bool DemangleStringGNU3Segments( - Platform* platform, const string& name, Ref& outType, StringList& outVarName) + std::optional DemangleGNU3WithConfig(const DemanglerConfig& config, std::string_view name) { + if (name.empty()) + return std::nullopt; + + Platform& platform = config.GetPlatform(); + bool simplifyTemplates = config.simplifyTemplates; + // Handle _block_invoke[.N] and _block_invoke_N suffixes (Clang/Apple block invocations). // E.g. ____ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_Pm_block_invoke.110 // -> "invocation_function_for_block_in_dyld::_main(...)" - static const string blockInvokeSuffix = "_block_invoke"; + static constexpr std::string_view blockInvokeSuffix = "_block_invoke"; size_t blockPos = name.rfind(blockInvokeSuffix); - if (blockPos != string::npos) + if (blockPos != std::string_view::npos) { // Verify the suffix is _block_invoke optionally followed by [._] only - string tail = name.substr(blockPos + blockInvokeSuffix.size()); + std::string_view tail = name.substr(blockPos + blockInvokeSuffix.size()); bool validSuffix = tail.empty(); if (!validSuffix && (tail[0] == '.' || tail[0] == '_')) { size_t i = 1; - while (i < tail.size() && isdigit((unsigned char)tail[i])) + while (i < tail.size() && isdigit(static_cast(tail[i]))) i++; validSuffix = (i == tail.size() && i > 1); } if (validSuffix) { // Extract the base symbol: everything before _block_invoke - string base = name.substr(0, blockPos); + std::string_view base = name.substr(0, blockPos); // Normalize leading underscores: find 'Z' after underscores, keep one '_' before it size_t zPos = base.find_first_not_of('_'); - if (zPos != string::npos && base[zPos] == 'Z') + if (zPos != std::string_view::npos && base[zPos] == 'Z') { - string normalized = "_" + base.substr(zPos); - Ref baseType; - StringList baseName; - if (DemangleStringGNU3Segments(platform, normalized, baseType, baseName)) + string normalized = "_"; + AppendStringView(normalized, base.substr(zPos)); + if (auto baseResult = DemangleGNU3WithConfig(config, normalized)) { - outVarName.clear(); - outVarName.push_back("invocation_function_for_block_in_" + JoinNameSegments(baseName)); - outType = baseType; - return true; + DemanglerResult result; + result.name = QualifiedName(StringList{ + "invocation_function_for_block_in_" + JoinNameSegments(StringList(baseResult->name.begin(), baseResult->name.end()))}); + result.type = baseResult->type; + return result; } } } @@ -3172,26 +3243,22 @@ namespace // Handle macOS thread-local variable initializer suffix: $tlv$init // E.g. __ZL9recursive$tlv$init -> demangle "__ZL9recursive" then annotate. - static const string tlvInitSuffix = "$tlv$init"; + static constexpr std::string_view tlvInitSuffix = "$tlv$init"; if (name.size() > tlvInitSuffix.size() && name.compare(name.size() - tlvInitSuffix.size(), tlvInitSuffix.size(), tlvInitSuffix) == 0) { - string base = name.substr(0, name.size() - tlvInitSuffix.size()); - Ref baseType; - StringList baseName; - if (DemangleStringGNU3Segments(platform, base, baseType, baseName)) + std::string_view base = name.substr(0, name.size() - tlvInitSuffix.size()); + if (auto result = DemangleGNU3WithConfig(config, base)) { - outVarName = std::move(baseName); - if (outVarName.size() > 0) - outVarName[outVarName.size() - 1] += "$tlv$init"; + if (result->name.size() > 0) + result->name[result->name.size() - 1] += "$tlv$init"; else - outVarName.push_back("$tlv$init"); - outType = baseType; - return true; + result->name = QualifiedName(StringList{"$tlv$init"}); + return result; } } - string encoding = name; + string encoding(name); string header; bool foundHeader = DemangleGNU3Static::DemangleGlobalHeader(encoding, header); @@ -3201,108 +3268,77 @@ namespace encoding = encoding.substr(3); else if (foundHeader && !header.empty()) { - outVarName.clear(); - outVarName.push_back(header); - outVarName.push_back(encoding); - outType = DemangledTypeNode::NamedType(UnknownNamedTypeClass, outVarName).Finalize(platform); - return true; + DemanglerResult result; + StringList nameSegments{header, encoding}; + result.name = QualifiedName(nameSegments); + result.type = DemangledTypeNode::NamedType(nameSegments).Finalize(platform); + return result; } else - return false; + return std::nullopt; - thread_local DemangleGNU3 demangle(platform, encoding); + thread_local ::DemangleGNU3 demangle(platform, encoding); demangle.Reset(platform, encoding); try { - outType = demangle.DemangleSymbol(outVarName).Finalize(platform); + DemanglerResult result; + StringList nameSegments; + DemangledTypeNode type = demangle.DemangleSymbol(nameSegments, simplifyTemplates); + if (simplifyTemplates) + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(type); + result.type = type.Finalize(platform); - if (outVarName.size() == 0) + if (nameSegments.empty()) { - if (GetFinalizedTypeClass(outType) == NamedTypeReferenceClass && - outType->GetNamedTypeReference()->GetTypeReferenceClass() == UnknownNamedTypeClass) + if (GetFinalizedTypeClass(result.type) == NamedTypeReferenceClass && + result.type->GetNamedTypeReference()->GetTypeReferenceClass() == UnknownNamedTypeClass) { - const auto typeName = outType->GetTypeName(); - outVarName = StringList(typeName.begin(), typeName.end()); - outType = nullptr; + const auto typeName = result.type->GetTypeName(); + nameSegments = StringList(typeName.begin(), typeName.end()); + result.type = nullptr; } - else if (GetFinalizedTypeClass(outType) == NamedTypeReferenceClass) + else if (GetFinalizedTypeClass(result.type) == NamedTypeReferenceClass) { - auto typeName = outType->GetTypeName(); + auto typeName = result.type->GetTypeName(); if (typeName.size() > 0) - outVarName = StringList{"_" + typeName[typeName.size() - 1]}; + nameSegments = StringList{"_" + typeName[typeName.size() - 1]}; } } if (foundHeader && !header.empty()) - outVarName.insert(outVarName.begin(), header); + nameSegments.insert(nameSegments.begin(), header); + result.name = QualifiedName(nameSegments); + return result; } - catch (const DemangleException&) + catch (DemangleException& e) { - return false; + LogDebugF("GNU3 demangling failed '{}' '{}'", name, e.what()); } - return true; + catch (std::exception& e) + { + LogDebugF("GNU3 demangling failed '{}' '{}'", name, e.what()); + } + return std::nullopt; } } -bool DemangleGNU3Static::DemangleStringGNU3(Platform* platform, const string& name, Ref& outType, - QualifiedName& outVarName) -{ - StringList outVarNameSegments; - if (!DemangleStringGNU3Segments(platform, name, outType, outVarNameSegments)) - return false; - outVarName = QualifiedName(outVarNameSegments); - return true; -} - - -bool DemangleGNU3Static::DemangleStringGNU3(Architecture* arch, const string& name, Ref& outType, - QualifiedName& outVarName) -{ - Ref platform; - if (arch) - platform = arch->GetStandalonePlatform(); - return DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName); -} - - -// ===== Explicit template instantiation ===== - - -// ===== Demangler plugin registration ===== - class GNU3Demangler: public Demangler { public: - GNU3Demangler(): Demangler("GNU3") + GNU3Demangler(): Demangler("gnu3") { } - ~GNU3Demangler() override {} + ~GNU3Demangler() override = default; - virtual bool IsMangledString(const string& name) override + bool IsMangledString(const string& name) override { return DemangleGNU3Static::IsGNU3MangledString(name); } -#ifdef BINARYNINJACORE_LIBRARY - virtual bool Demangle(Architecture* arch, const string& name, Ref& outType, QualifiedName& outVarName, - BinaryView* view) override -#else - virtual bool Demangle(Ref arch, const string& name, Ref& outType, QualifiedName& outVarName, - Ref view) override -#endif + std::optional Demangle(const string& name, const Config& config) override { - if (view) - { - auto platform = view->GetDefaultPlatform(); - if (platform) -#ifdef BINARYNINJACORE_LIBRARY - return DemangleGNU3Static::DemangleStringGNU3(platform, name, outType, outVarName); -#else - return DemangleGNU3Static::DemangleStringGNU3(platform.GetPtr(), name, outType, outVarName); -#endif - } - return DemangleGNU3Static::DemangleStringGNU3(arch, name, outType, outVarName); + return DemangleGNU3WithConfig(config, name); } }; @@ -3321,8 +3357,7 @@ extern "C" BINARYNINJAPLUGIN bool CorePluginInit() #endif { - static GNU3Demangler* demangler = new GNU3Demangler(); - Demangler::Register(demangler); - return true; + static auto demangler = new GNU3Demangler(); + return Demangler::Register(demangler); } } diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 0e06baa1ba..591e460c50 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -13,16 +13,17 @@ // limitations under the License. #pragma once -#include -#include +#include +#include +#include // XXX: Compiled directly into the core for performance reasons // Will still work fine compiled independently, just at about a // 50-100% performance penalty due to FFI overhead +// TODO: the above assessment needs to be re-evaluated after the +// refactor to use DemangledTypeNode and the simplifier refactor +// its very possible this performance penalty is completely gone #ifdef BINARYNINJACORE_LIBRARY -#include "qualifiedname.h" -#include "type.h" -#include "architecture.h" #include "binaryview.h" #include "demangle.h" #define BN BinaryNinjaCore @@ -35,80 +36,76 @@ #define _STD_VECTOR std::vector #endif -#include "demangler/demangled_type_node.h" - -class DemangleException: public std::exception -{ - _STD_STRING m_message; -public: - DemangleException(_STD_STRING msg="Attempt to read beyond bounds or missing expected character"): m_message(msg){} - virtual const char* what() const noexcept { return m_message.c_str(); } -}; - -class DemangleGNU3Reader -{ -public: - DemangleGNU3Reader(const _STD_STRING& data); - void Reset(const _STD_STRING& data); - _STD_STRING PeekString(size_t count=1); -#ifdef GNUDEMANGLE_DEBUG - _STD_STRING GetRaw(); -#endif - _STD_STRING ReadString(size_t count=1); - - size_t Length() const { return m_data.length() - m_offset; } - - char Peek() - { - if (1 > Length()) - return '\0'; - return (char)m_data[m_offset]; - } - - char Read() - { - if (1 > Length()) - throw DemangleException(); - return m_data[m_offset++]; - } - - void Consume(size_t count=1) - { - if (count > Length()) - throw DemangleException(); - m_offset += count; - } - - void UnRead(size_t count=1) - { - if (count <= m_offset) - m_offset -= count; - } - -private: - _STD_STRING m_data; - size_t m_offset; -}; +#include "demangler/demangled_reader.h" class DemangleGNU3 { +public: using ParamList = _STD_VECTOR; - using NodeRef = DemangledTypeNode::NodeRef; + using TypeNodeRef = DemangledTypeNode::NodeRef; +private: + struct NodeRef + { + TypeNodeRef type; + std::shared_ptr templatePack; + bool emptyTemplatePack = false; + bool templatePackExpansion = false; + + NodeRef() = default; + NodeRef(std::nullptr_t) {} + NodeRef(TypeNodeRef typeRef): type(std::move(typeRef)) {} + + static NodeRef EmptyTemplatePack() + { + NodeRef ref; + ref.emptyTemplatePack = true; + return ref; + } + + static NodeRef TemplateParamPack(ParamList args) + { + NodeRef ref; + ref.templatePack = std::make_shared(std::move(args)); + ref.emptyTemplatePack = ref.templatePack->empty(); + for (auto& arg : *ref.templatePack) + { + if (arg.type) + { + ref.type = arg.type; + break; + } + } + return ref; + } + + static NodeRef TemplateParamPackExpansion(ParamList args) + { + NodeRef ref = TemplateParamPack(std::move(args)); + ref.templatePackExpansion = true; + return ref; + } + + explicit operator bool() const { return type != nullptr; } + [[nodiscard]] bool IsTemplateParamPack() const { return templatePack != nullptr; } + [[nodiscard]] bool IsTemplateParamPackExpansion() const { return templatePackExpansion; } + DemangledTypeNode& operator*() const { return *type; } + DemangledTypeNode* operator->() const { return type.get(); } + operator TypeNodeRef() const { return type; } + }; using NodeRefList = _STD_VECTOR; - DemangleGNU3Reader m_reader; - BN::Ref m_platform; + static constexpr size_t MAX_DEMANGLE_NODE_LENGTH = 8192; + static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 1024; + _STD_STRING m_mangledName; + DemangleReader m_reader{m_mangledName, MAX_DEMANGLE_NODE_LENGTH, false}; + std::reference_wrapper m_platform; NodeRefList m_substitute; NodeRefList m_templateSubstitute; _STD_VECTOR m_functionSubstitute; NodeRef m_lastTypeRef; _STD_STRING m_lastName; - BNNameType m_nameType; - bool m_localType; - bool m_hasReturnType; bool m_isParameter; - bool m_shouldDeleteReader; bool m_topLevel; bool m_isOperatorOverload; bool m_parsingLambdaParams; @@ -127,28 +124,24 @@ class DemangleGNU3 NodeRef typeRef; }; _STD_VECTOR m_pendingForwardRefs; - class NestingGuard - { - DemangleGNU3& m_demangler; - public: - NestingGuard(DemangleGNU3& demangler); - ~NestingGuard(); - }; + using NestingGuard = DemangleNestingGuard; void ResolveForwardTemplateRefs(DemangledTypeNode& type, const ParamList& args); enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name}; StringList DemangleBaseUnresolvedName(); DemangledTypeNode DemangleUnresolvedType(); - _STD_STRING DemangleUnarySuffixExpression(const _STD_STRING& op); - _STD_STRING DemangleUnaryPrefixExpression(const _STD_STRING& op); - _STD_STRING DemangleBinaryExpression(const _STD_STRING& op); - _STD_STRING DemangleUnaryPrefixType(const _STD_STRING& op); + _STD_STRING DemangleUnarySuffixExpression(std::string_view op); + _STD_STRING DemangleUnaryPrefixExpression(std::string_view op, DemangledTypeNode* outNode = nullptr); + _STD_STRING DemangleBinaryExpression(std::string_view op, DemangledTypeNode* outNode = nullptr); + _STD_STRING DemangleUnaryPrefixType(std::string_view op); _STD_STRING DemangleTypeString(); _STD_STRING DemangleExpressionList(); DemangledTypeNode DemangleUnqualifiedName(); - _STD_STRING DemangleSourceName(); + std::string_view DemangleSourceName(); _STD_STRING DemangleNumberAsString(); - _STD_STRING DemangleExpression(); + _STD_STRING DemangleExpression(DemangledTypeNode* outNode = nullptr); _STD_STRING DemanglePrimaryExpression(); + NodeRef DemangleTemplateSubstitutionEntry(NodeRef* outTypeRef = nullptr); + bool TryDemangleTemplateParamExpressionPackExpansion(_STD_STRING& expr, bool& emptyPack); DemangledTypeNode DemangleName(); DemangledTypeNode DemangleLocalName(); @@ -161,22 +154,12 @@ class DemangleGNU3 DemangledTypeNode DemangleType(); int64_t DemangleNumber(); DemangledTypeNode DemangleNestedName(bool* allTypeTemplateArgs = nullptr, bool pushBareTemplatePrefix = true); - NodeRef PushTemplateType(NodeRef type); - NodeRef PushTemplateType(const DemangledTypeNode& type); - NodeRef PushTemplateType(DemangledTypeNode&& type); - NodeRef PushType(NodeRef type); + void PushEmptyTemplateParamSubstitution(); NodeRef PushType(const DemangledTypeNode& type); NodeRef PushType(DemangledTypeNode&& type); NodeRef GetTypeRef(size_t ref); const DemangledTypeNode& GetType(size_t ref); - - DemangledTypeNode CreateUnknownType(const StringList& s); - DemangledTypeNode CreateUnknownType(const _STD_STRING& s); - static void ExtendTypeName(DemangledTypeNode& type, const _STD_STRING& extend); - static void ApplyTemplateArgs(DemangledTypeNode& type, ParamList args); - static void AppendTypeName(DemangledTypeNode& type, const DemangledTypeNode& extend); - static _STD_STRING LastTypeNameSegmentBase(const DemangledTypeNode& type); - static bool LastTypeNameSegmentHasTemplateArguments(const DemangledTypeNode& type); + bool AppendTemplateParamPackExpansion(ParamList& params, const NodeRef& expansion, bool functionParameter); #ifdef GNUDEMANGLE_DEBUG const DemangledTypeNode& GetTemplateType(size_t ref); @@ -184,18 +167,15 @@ class DemangleGNU3 #endif public: - DemangleGNU3(BN::Platform* platform, const _STD_STRING& mangledName); - void Reset(BN::Platform* platform, const _STD_STRING& mangledName); - DemangledTypeNode DemangleSymbol(StringList& varName); + DemangleGNU3(BN::Platform& platform, _STD_STRING mangledName); + void Reset(BN::Platform& platform, _STD_STRING mangledName); + DemangledTypeNode DemangleSymbol(StringList& varName, bool simplifyTemplates = false); }; class DemangleGNU3Static { public: - static bool IsGNU3MangledString(const _STD_STRING& name); + static bool IsGNU3MangledString(std::string_view name); static bool DemangleGlobalHeader(_STD_STRING& name, _STD_STRING& header); - - static bool DemangleStringGNU3(BN::Platform* platform, const _STD_STRING& name, BN::Ref& outType, BN::QualifiedName& outVarName); - static bool DemangleStringGNU3(BN::Architecture* arch, const _STD_STRING& name, BN::Ref& outType, BN::QualifiedName& outVarName); }; diff --git a/demangler/msvc/CMakeLists.txt b/demangler/msvc/CMakeLists.txt index f89aee5135..53b51453b0 100644 --- a/demangler/msvc/CMakeLists.txt +++ b/demangler/msvc/CMakeLists.txt @@ -6,8 +6,12 @@ file(GLOB SOURCES CONFIGURE_DEPENDS *.cpp *.c *.h + ../demangled_log.h + ../demangled_reader.h ../demangled_type_node.cpp - ../demangled_type_node.h) + ../demangled_type_node.h + ../demangled_template_simplifier.cpp + ../demangled_template_simplifier.h) if(DEMO) add_library(${PROJECT_NAME} STATIC ${SOURCES}) diff --git a/demangler/msvc/demangle_msvc.cpp b/demangler/msvc/demangle_msvc.cpp index 27d44cd396..448bd22448 100644 --- a/demangler/msvc/demangle_msvc.cpp +++ b/demangler/msvc/demangle_msvc.cpp @@ -16,8 +16,12 @@ // See https://llvm.org/LICENSE.txt for license information. #include "demangle_msvc.h" -#include "unicode.h" +#include "demangler/demangled_log.h" +#include "demangler/demangled_template_simplifier.h" #include "base/unicode.h" +#ifdef BINARYNINJACORE_LIBRARY +#include "unicode.h" +#endif #include #include #include @@ -32,8 +36,6 @@ using namespace std; #endif -// The largest observed depth in a real-world corpus of roughly 200k MSVC symbols was 54. -static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 256; static constexpr size_t MAX_ENCODED_NUMBER_HEX_DIGITS = 16; static constexpr size_t MAX_BACKREFS = 10; @@ -65,32 +67,19 @@ static _STD_STRING FormatEncodedNumberLiteral(uint64_t magnitude, bool negative) // Define MSVC_DEMANGLE_DEBUG to enable trace logging #ifdef MSVC_DEMANGLE_DEBUG -#define MSVC_TRACE(...) LogTraceF(__VA_ARGS__) -#else -#define MSVC_TRACE(...) do {} while(0) -#endif - -_STD_STRING Demangle::Reader::ReadString(size_t count) +template +void MsvcTraceWithIndentation(fmt::format_string format, Args&&... args) { - if (count > Length()) - throw DemangleException(); - _STD_STRING out(m_ptr, count); - m_ptr += count; - return out; -} - - -_STD_STRING Demangle::Reader::ReadUntil(char sentinel) -{ - const char* found = static_cast(memchr(m_ptr, sentinel, m_end - m_ptr)); - if (!found) - throw DemangleException(); - size_t count = found - m_ptr; - _STD_STRING out = ReadString(count); - Consume(); // sentinel - return out; + LogTraceF("{}{}", DemangleLogIndentation::Prefix(), + fmt::format(format, std::forward(args)...)); } +#define MSVC_TRACE(...) MsvcTraceWithIndentation(__VA_ARGS__) +#define MSVC_TRACE_SCOPE DemangleLogIndentationScope msvcTraceIndentationScope +#else +#define MSVC_TRACE(...) do {} while(0) +#define MSVC_TRACE_SCOPE do {} while(0) +#endif DemangledTypeNode::NodeRef Demangle::BackrefList::GetTypeBackrefRef(size_t reference) { @@ -239,61 +228,19 @@ void Demangle::BackrefContextSwitch::Swap(BackrefList& left, BackrefList& right) -Demangle::Demangle(Architecture* arch, _STD_STRING mangledName) : - m_mangledName(std::move(mangledName)), - m_reader(m_mangledName), - m_arch(arch), - m_platform(nullptr), - m_view(nullptr) -{ -} - - -Demangle::Demangle(Ref platform, _STD_STRING mangledName) : - m_mangledName(std::move(mangledName)), - m_reader(m_mangledName), - m_arch(nullptr), - m_platform(std::move(platform)), - m_view(nullptr) -{ -} - - -Demangle::Demangle(Ref view, _STD_STRING mangledName) : +Demangle::Demangle(const DemanglerConfig& config, _STD_STRING mangledName) : m_mangledName(std::move(mangledName)), m_reader(m_mangledName), - m_arch(nullptr), - m_platform(nullptr), - m_view(std::move(view)) + m_config(config) { } - -Demangle::NestingGuard::NestingGuard(Demangle& demangler) : m_demangler(demangler) -{ - m_demangler.m_nestingDepth++; - if (m_demangler.m_nestingDepth > MAX_DEMANGLE_NESTING_DEPTH) - { - m_demangler.m_nestingDepth--; - throw DemangleException("Detected adversarial mangled string"); - } -} - - -Demangle::NestingGuard::~NestingGuard() -{ - m_demangler.m_nestingDepth--; -} - - -void Demangle::Reset(Architecture* arch, const _STD_STRING& mangledName) +void Demangle::Reset(const DemanglerConfig& config, const _STD_STRING& mangledName) { m_mangledName = mangledName; m_reader.Reset(m_mangledName); m_backrefList.Clear(); - m_arch = arch; - m_platform = nullptr; - m_view = nullptr; + m_config = config; m_templateParamDepth = 0; m_nestingDepth = 0; } @@ -326,17 +273,23 @@ void Demangle::RewriteTemplateBackrefName(NameList& typeName, const BackrefList& _STD_STRING Demangle::FormatTypeAndName(const DemangledTypeNode& type, const NameList& name) const { StringList nameSegments = FinalizeNameList(name); + Platform& platform = GetRenderingPlatform(); if (type.GetNameType() == OperatorReturnTypeNameType) { - Ref finalizedType = type.Finalize(m_platform.GetPtr()); - if (finalizedType) + if (Ref finalizedType = type.Finalize(platform)) return finalizedType->GetTypeAndName(QualifiedName(nameSegments)); } - return type.GetTypeAndName(nameSegments); + return type.GetTypeAndName(nameSegments, platform); +} + +Platform& Demangle::GetRenderingPlatform() const +{ + return m_config.GetPlatform(); } DemangledTypeNode Demangle::DemangleReferencedSymbolValue(BackrefList& varList) { + MSVC_TRACE_SCOPE; // Match LLVM's TemplateParameterReferenceNode parsing: referenced-symbol // non-type template arguments are parsed in the active backref context, so // later template arguments may refer to names/types introduced inside the @@ -352,6 +305,7 @@ DemangledTypeNode Demangle::DemangleReferencedSymbolValue(BackrefList& varList) DemangledTypeNode Demangle::DemangleAutoNonTypeTemplateParam(BackrefList& varList) { + MSVC_TRACE_SCOPE; if (m_reader.ConsumeIf('0')) { return DemangledTypeNode::NamedType(UnknownNamedTypeClass, StringList{DecodeEncodedNumberLiteral()}); @@ -367,7 +321,8 @@ DemangledTypeNode Demangle::DemangleAutoNonTypeTemplateParam(BackrefList& varLis DemangledTypeNode Demangle::DemangleVarType(BackrefList& varList, bool isReturn, bool includeImplicitThis, DemangledTypeNode::NodeRef* outTypeBackref, TypeBackrefMode typeBackrefMode) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}' - {}", __FUNCTION__, m_reader.GetRaw(), varList.nameList.size()); if (outTypeBackref) *outTypeBackref = nullptr; @@ -802,6 +757,7 @@ DemangledTypeNode Demangle::DemangleVarType(BackrefList& varList, bool isReturn, Demangle::EncodedNumber Demangle::DecodeEncodedNumber() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool negative = m_reader.ConsumeIf('?'); if (m_reader.Length() == 0) @@ -861,6 +817,7 @@ _STD_STRING Demangle::DecodeEncodedNumberLiteral() char Demangle::DemangleChar() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // Basic char is just the char if (!m_reader.ConsumeIf('?')) @@ -961,6 +918,7 @@ char Demangle::DemangleChar() void Demangle::DemangleVariableList(_STD_VECTOR& paramList, BackrefList& varList, bool typeBackrefs) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; uint8_t suffix = 0; @@ -1168,6 +1126,7 @@ void Demangle::DemangleNameTypeRtti(BNNameType& classFunctionType, void Demangle::DemangleTypeNameLookup(_STD_STRING& out, BNNameType& functionType) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); switch (m_reader.Read()) { @@ -1303,6 +1262,7 @@ void Demangle::DemangleTypeNameLookup(_STD_STRING& out, BNNameType& functionType DemangledNamePart Demangle::DemangleTemplateInstantiationName(BackrefList& nameBackrefList) { + MSVC_TRACE_SCOPE; DemangledNamePart out; MSVC_TRACE("DemangleTemplateInstantiationName: '{}'", m_reader.GetRaw()); if (!m_reader.ConsumeIf("?$")) @@ -1325,6 +1285,7 @@ DemangledNamePart Demangle::DemangleTemplateInstantiationName(BackrefList& nameB DemangledNamePart Demangle::DemangleTemplateInstantiationNameInLocalContext(BackrefList& nameBackrefList) { + MSVC_TRACE_SCOPE; DemangledNamePart out; BNNameType dummyFunctionType = NoNameType; MSVC_TRACE("DemangleTemplateInstantiationNameInLocalContext: '{}'", m_reader.GetRaw()); @@ -1352,7 +1313,8 @@ DemangledNamePart Demangle::DemangleTemplateInstantiationNameInLocalContext(Back void Demangle::DemangleTemplateParams(_STD_VECTOR& params, BackrefList& nameBackrefList, DemangledNamePart& out) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; params.clear(); const bool nestedTemplateContext = (m_templateParamDepth > 0); struct NameBackrefScopeGuard @@ -1394,6 +1356,7 @@ void Demangle::DemangleTemplateParams(_STD_VECTOR& par DemangledNamePart Demangle::DemangleUnqualifiedSymbolName(BackrefList& nameBackrefList, BNNameType& classFunctionType, bool& backrefEligible) { + MSVC_TRACE_SCOPE; backrefEligible = true; DemangledNamePart out; _STD_STRING text; @@ -1421,6 +1384,7 @@ DemangledNamePart Demangle::DemangleUnqualifiedSymbolName(BackrefList& nameBackr DemangledTypeNode Demangle::DemangleString(NameList& symbolName) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // ??_C@_@ if (!m_reader.ConsumeIf('_')) @@ -1559,13 +1523,17 @@ DemangledTypeNode Demangle::DemangleString(NameList& symbolName) } } symbolName.clear(); - symbolName.push_back(MakeNameSegment(fmt::bnformat("{}\"{}\"{}", literalPrefix, name, truncated ? "..." : ""))); + _STD_STRING literalName = literalPrefix + "\"" + name + "\""; + if (truncated) + literalName += "..."; + symbolName.push_back(MakeNameSegment(literalName)); return type; } DemangledTypeNode Demangle::DemangleTypeInfoName(NameList& symbolName) { + MSVC_TRACE_SCOPE; if (m_reader.Read() != '?') throw DemangleException("Unknown raw name type"); bool _const = false; @@ -1651,9 +1619,10 @@ bool Demangle::FunctionTypeHasPointerSuffix(char functionType) } -_STD_STRING Demangle::FormatFunctionScopeSignature(const DemangledTypeNode& type, const NameList& scopeName) +_STD_STRING Demangle::FormatFunctionScopeSignature( + const DemangledTypeNode& type, const NameList& scopeName, Platform& platform) { - _STD_STRING out = type.GetTypeAndName(FinalizeNameList(scopeName)); + _STD_STRING out = type.GetTypeAndName(FinalizeNameList(scopeName), platform); while (!out.empty() && out.back() == ' ') out.pop_back(); return out; @@ -1684,7 +1653,8 @@ void Demangle::AppendLocalScope(NameList& nameList, BackrefList& nameBackrefList scopeFunctionType, FunctionTypeHasPointerSuffix(ft), nameBackrefList).type; PrependNameComponent(nameList, MakeNameSegment("`" + to_string(scopeOrdinal) + "'")); - PrependNameComponent(nameList, MakeNameSegment("`" + FormatFunctionScopeSignature(scopeType, scopeName) + "'")); + PrependNameComponent( + nameList, MakeNameSegment("`" + FormatFunctionScopeSignature(scopeType, scopeName, GetRenderingPlatform()) + "'")); } @@ -1754,7 +1724,8 @@ void Demangle::DemangleName(NameList& nameList, BackrefList& nameBackrefList, bool typeNameContext) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; // NameList is stored outermost-first for QualifiedName, but MSVC encodes // names leaf-first. Ordinary parsed components are prepended; constructor // and destructor branches recurse to parse the class scope, then append the @@ -1938,6 +1909,7 @@ void Demangle::DemangleName(NameList& nameList, BNCallingConventionName Demangle::DemangleCallingConvention() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); switch (m_reader.Read()) { @@ -1975,6 +1947,7 @@ void Demangle::ConsumeExtendedModifierPrefix() uint8_t Demangle::DemanglePointerSuffix() { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); uint8_t suffix = 0; if (m_reader.PeekOr() == '@') @@ -2002,6 +1975,7 @@ uint8_t Demangle::DemanglePointerSuffix() void Demangle::DemangleModifiers(bool& _const, bool& _volatile, bool &isMember) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // Always write the out params, even when `@` marks the no-modifiers case. _const = false; @@ -2115,7 +2089,8 @@ void Demangle::ApplySymbolFunctionContext(DemangledFunction& function, NameList& Demangle::DemangledFunction Demangle::DemangleFunction(BNNameType classFunctionType, bool pointerSuffix, BackrefList& nameBackrefList, int funcClass) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false; uint8_t suffix = 0; @@ -2226,6 +2201,7 @@ Demangle::DemangledFunction Demangle::DemangleFunction(BNNameType classFunctionT DemangledTypeNode Demangle::DemangleData(BackrefList& varList) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; DemangledTypeNode newType = DemangleVarType(varList, false); @@ -2248,6 +2224,7 @@ DemangledTypeNode Demangle::DemangleData(BackrefList& varList) DemangledTypeNode Demangle::DemangleRTTI(BNNameType nameType, const NameList& symbolName) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; if (m_reader.Length() > 0) @@ -2265,6 +2242,7 @@ DemangledTypeNode Demangle::DemangleRTTI(BNNameType nameType, const NameList& sy DemangledTypeNode Demangle::DemangleVTable(BackrefList& nameBackrefList, NameList& symbolName) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); bool _const = false, _volatile = false, isMember = false; DemangleModifiers(_const, _volatile, isMember); @@ -2305,6 +2283,7 @@ DemangledTypeNode Demangle::DemangleVTable(BackrefList& nameBackrefList, NameLis // backtick pair: `dynamic initializer for `int foo''. Demangle::DemangleContext Demangle::DemangleDynamicInitFini(bool isDtor, BackrefList& backrefList) { + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); // /d2FH4 may replace a long wrapped target with an MD5 name (??@@). @@ -2435,7 +2414,8 @@ Demangle::DemangleContext Demangle::DemangleSymbol() Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) { - NestingGuard nestingGuard(*this); + NestingGuard nestingGuard(m_nestingDepth); + MSVC_TRACE_SCOPE; MSVC_TRACE("{}: '{}'", __FUNCTION__, m_reader.GetRaw()); BNNameType classFunctionType = NoNameType; NameList varName; @@ -2606,166 +2586,65 @@ Demangle::DemangleContext Demangle::DemangleSymbol(BackrefList& backrefList) return finishContext(); } -std::pair, QualifiedName> Demangle::Finalize(BinaryView* view) +DemanglerResult Demangle::Finalize() { DemangleContext context = DemangleSymbol(); if (m_reader.Length() != 0) LogDebugF("Demangling Succeeded with trailing characters '{}' in '{}'", m_reader.GetRaw(), m_mangledName); - Ref platform = m_platform; - if (!platform && view) - platform = view->GetDefaultPlatform(); - - Architecture* arch = m_arch; -#ifdef BINARYNINJACORE_LIBRARY - if (!arch && platform) - arch = platform->GetArchitecture(); - if (!arch && view) - arch = view->GetDefaultArchitecture(); -#else - Ref viewArch; - Ref platformArch; - if (!arch && platform) - { - platformArch = platform->GetArchitecture(); - arch = platformArch.GetPtr(); - } - if (!arch && view) + if (m_config.simplifyTemplates) { - viewArch = view->GetDefaultArchitecture(); - arch = viewArch.GetPtr(); + DemangledTemplateSimplifier::SimplifyTypeNodeInPlace(context.type); + DemangledTemplateSimplifier::SimplifyNameSegmentsInPlace(context.name); } -#endif - if (!arch) - throw DemangleException(); - if (!platform) - platform = arch->GetStandalonePlatform(); - - return {context.type.Finalize(platform.GetPtr()), QualifiedName(FinalizeNameList(context.name))}; + DemanglerResult result; + result.type = context.type.Finalize(m_config.GetPlatform()); + result.name = QualifiedName(FinalizeNameList(context.name)); + return result; } -std::pair, QualifiedName> Demangle::Finalize() +namespace { - return Finalize(m_view.GetPtr()); -} - -template -static bool DemangleMSImpl(const _STD_STRING& mangledName, Ref& outType, QualifiedName& outVarName, - DemangleBody&& demangleBody) -{ - outType = nullptr; - if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) - return false; - - try - { - auto result = demangleBody(); - outType = std::move(result.first); - outVarName = std::move(result.second); - return true; - } - catch (DemangleException& e) + std::optional DemangleMSWithConfig(const DemanglerConfig& config, const _STD_STRING& mangledName) { - LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); - return false; - } - catch (std::exception& e) - { - LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); - return false; - } -} + if (mangledName.empty() || (mangledName[0] != '?' && mangledName[0] != '.')) + return std::nullopt; -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, const Ref& view) -{ - if (view) - { - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - Demangle demangle(arch, mangledName); - return demangle.Finalize(view.GetPtr()); - }); + try + { + thread_local Demangle demangle(config, mangledName); + demangle.Reset(config, mangledName); + return demangle.Finalize(); + } + catch (DemangleException& e) + { + LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); + } + catch (std::exception& e) + { + LogDebugF("Demangling Failed '{}' '{}'", mangledName, e.what()); + } + return std::nullopt; } - return DemangleMS(arch, mangledName, outType, outVarName); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view) -{ - if (view) - return DemangleMS(arch, mangledName, outType, outVarName, Ref(view)); - return DemangleMS(arch, mangledName, outType, outVarName); } -bool Demangle::DemangleMS(Platform* platform, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName) -{ - outType = nullptr; - if (!platform) - return false; - - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - Demangle demangle(Ref(platform), mangledName); - return demangle.Finalize(); - }); -} - -bool Demangle::DemangleMS(Architecture* arch, const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName) -{ - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - thread_local Demangle demangle(arch, mangledName); - demangle.Reset(arch, mangledName); - return demangle.Finalize(); - }); -} - - -bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, const Ref& view) -{ - return DemangleMSImpl(mangledName, outType, outVarName, [&]() { - // Can't use thread_local here — BinaryView overload needs platform/view state - Demangle demangle(view, mangledName); - return demangle.Finalize(); - }); -} - -bool Demangle::DemangleMS(const _STD_STRING& mangledName, Ref& outType, - QualifiedName& outVarName, BinaryView* view) -{ - outType = nullptr; - if (!view) - return false; - return DemangleMS(mangledName, outType, outVarName, Ref(view)); -} - - class MSDemangler: public Demangler { public: - MSDemangler(): Demangler("MS") + MSDemangler(): Demangler("msvc") { } ~MSDemangler() override = default; - bool IsMangledString(const _STD_STRING& name) override + virtual bool IsMangledString(const _STD_STRING& name) override { return !name.empty() && (name[0] == '?' || name[0] == '.'); } -#ifdef BINARYNINJACORE_LIBRARY - bool Demangle(Architecture* arch, const _STD_STRING& name, Ref& outType, QualifiedName& outVarName, - BinaryView* view) override -#else - virtual bool Demangle(Ref arch, const _STD_STRING& name, Ref& outType, QualifiedName& outVarName, - Ref view) override -#endif + virtual std::optional Demangle(const string& name, const Config& config) override { - if (view) - return Demangle::DemangleMS(arch, name, outType, outVarName, view); - return Demangle::DemangleMS(arch, name, outType, outVarName); + return DemangleMSWithConfig(config, name); } }; @@ -2784,7 +2663,6 @@ extern "C" #endif { static auto demangler = new MSDemangler(); - Demangler::Register(demangler); - return true; + return Demangler::Register(demangler); } } diff --git a/demangler/msvc/demangle_msvc.h b/demangler/msvc/demangle_msvc.h index bb5e94ecb5..83154b1f54 100644 --- a/demangler/msvc/demangle_msvc.h +++ b/demangler/msvc/demangle_msvc.h @@ -13,17 +13,15 @@ // limitations under the License. #pragma once -#include #include -#include // XXX: Compiled directly into the core for performance reasons // Will still work fine compiled independently, just at about a // 50-100% performance penalty due to FFI overhead +// TODO: the above assessment needs to be re-evaluated after the +// refactor to use DemangledTypeNode and the simplifier refactor +// its very possible this performance penalty is completely gone #ifdef BINARYNINJACORE_LIBRARY -#include "qualifiedname.h" -#include "type.h" -#include "architecture.h" #include "binaryview.h" #include "demangle.h" #define BN BinaryNinjaCore @@ -36,16 +34,7 @@ #define _STD_VECTOR std::vector #endif -#include "demangler/demangled_type_node.h" - -class DemangleException: public std::exception -{ - _STD_STRING m_message; -public: - DemangleException(_STD_STRING msg="Attempt to read beyond bounds or missing expected character"): m_message(std::move(msg)){} - [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } -}; - +#include "demangler/demangled_reader.h" class Demangle { @@ -74,90 +63,6 @@ class Demangle }; private: - class Reader - { - public: - Reader(const _STD_STRING& data) - { - Reset(data); - } - void Reset(const _STD_STRING& data) - { - m_ptr = data.c_str(); - m_end = data.c_str() + data.size(); - ValidatePrintableAscii(); - } - bool PeekMatch(const char* str, size_t len) const - { - if (len > Length()) - return false; - return memcmp(m_ptr, str, len) == 0; - } - [[nodiscard]] char PeekAt(size_t offset) const - { - if (offset >= Length()) - throw DemangleException(); - return m_ptr[offset]; - } - [[nodiscard]] char Peek() const - { - if (m_ptr >= m_end) - throw DemangleException(); - return *m_ptr; - } - [[nodiscard]] char PeekOr(char fallback = '\0') const - { - if (Length() == 0) - return fallback; - return *m_ptr; - } - [[nodiscard]] const char* GetRaw() const { return m_ptr; } - void SetRaw(const char* p) { m_ptr = p; } - [[nodiscard]] char Read() - { - if (m_ptr >= m_end) - throw DemangleException(); - return *m_ptr++; - } - bool ConsumeIf(char ch) - { - if (PeekOr() != ch) - return false; - Consume(); - return true; - } - bool ConsumeIf(const char* str, size_t len) - { - if (!PeekMatch(str, len)) - return false; - Consume(len); - return true; - } - template - bool ConsumeIf(const char (&str)[N]) - { - return ConsumeIf(str, N - 1); - } - void Consume(size_t count = 1) - { - if (count > Length()) - throw DemangleException(); - m_ptr += count; - } - [[nodiscard]] size_t Length() const { return static_cast(m_end - m_ptr); } - _STD_STRING ReadString(size_t count); - _STD_STRING ReadUntil(char sentinel); - private: - void ValidatePrintableAscii() const - { - for (const char* p = m_ptr; p < m_end; p++) - if (*p < 0x20 || *p > 0x7e) - throw DemangleException(); - } - const char* m_ptr; - const char* m_end; - }; - class BackrefList { public: @@ -239,31 +144,27 @@ class Demangle } _STD_STRING m_mangledName; // Owns the string; Reader points into it - Reader m_reader; + DemangleReader m_reader; BackrefList m_backrefList; - BN::Architecture* m_arch; - BN::Ref m_platform; - BN::Ref m_view; + BN::DemanglerConfig m_config; size_t m_templateParamDepth = 0; size_t m_nestingDepth = 0; - class NestingGuard - { - Demangle& m_demangler; - public: - NestingGuard(Demangle& demangler); - ~NestingGuard(); - }; + // The largest observed depth in a real-world corpus of roughly 200k MSVC symbols was 54. + static constexpr size_t MAX_DEMANGLE_NESTING_DEPTH = 256; + using NestingGuard = DemangleNestingGuard; static void RewriteTemplateBackrefName(NameList& typeName, const BackrefList& nameBackrefList); static void PrependNameComponent(NameList& nameList, DemangledNamePart name); void AppendStringName(NameList& nameList, BackrefList& nameBackrefList); static void FinalizeConstructorTemplateName(NameList& nameList, size_t nameListSizeAtEntry, bool pending); static bool FunctionTypeHasPointerSuffix(char functionType); - static _STD_STRING FormatFunctionScopeSignature(const DemangledTypeNode& type, const NameList& scopeName); + static _STD_STRING FormatFunctionScopeSignature( + const DemangledTypeNode& type, const NameList& scopeName, BN::Platform& platform); + [[nodiscard]] BN::Platform& GetRenderingPlatform() const; void AppendLocalScope(NameList& nameList, BackrefList& nameBackrefList, uint64_t scopeOrdinal, bool typeNameContext); bool TryAppendLocalScopeAt(NameList& nameList, BackrefList& nameBackrefList, const char* encodedNumberStart, bool typeNameContext); - _STD_STRING FormatTypeAndName(const DemangledTypeNode& type, const NameList& name) const; + [[nodiscard]] _STD_STRING FormatTypeAndName(const DemangledTypeNode& type, const NameList& name) const; enum class TypeBackrefMode { RecordTopLevel, @@ -339,32 +240,14 @@ class Demangle DemangledTypeNode DemangleTypeInfoName(NameList& symbolName); DemangleContext DemangleDynamicInitFini(bool isDtor, BackrefList& backrefList); DemangleContext DemangleSymbol(BackrefList& backrefList); - std::pair, BN::QualifiedName> Finalize(BN::BinaryView* view); public: - Demangle(BN::Architecture* arch, _STD_STRING mangledName); - Demangle(BN::Ref view, _STD_STRING mangledName); - Demangle(BN::Ref platform, _STD_STRING mangledName); + Demangle(const BN::DemanglerConfig& config, _STD_STRING mangledName); + void Reset(const BN::DemanglerConfig& config, const _STD_STRING& mangledName); Demangle(const Demangle&) = delete; Demangle(Demangle&&) = delete; Demangle& operator=(const Demangle&) = delete; Demangle& operator=(Demangle&&) = delete; - void Reset(BN::Architecture* arch, const _STD_STRING& mangledName); DemangleContext DemangleSymbol(); - std::pair, BN::QualifiedName> Finalize(); - - // Be careful not to accidentally implicitly cast a BinaryView* to a bool - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, const BN::Ref& view); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, BN::BinaryView* view); - static bool DemangleMS(BN::Platform* platform, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName); - static bool DemangleMS(BN::Architecture* arch, const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName); - - static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, const BN::Ref& view); - static bool DemangleMS(const _STD_STRING& mangledName, BN::Ref& outType, - BN::QualifiedName& outVarName, BN::BinaryView* view); + BN::DemanglerResult Finalize(); }; diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 6c1d78a8ac..1187b24ff7 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -24,7 +24,6 @@ use binaryninja::{ platform::Platform, rc::*, symbol::SymbolType, - template_simplifier::simplify_str_to_fqn, types::{FunctionParameter, Type}, variable::NamedVariableWithType, }; @@ -38,6 +37,37 @@ use std::{cmp::Ordering, collections::HashMap, hash::Hash}; pub(crate) type TypeUID = usize; +fn qualified_name_segment_count(name: &str) -> usize { + let mut count = 1; + let mut angle_depth = 0; + let mut paren_depth = 0; + let mut bracket_depth = 0; + let bytes = name.as_bytes(); + let mut index = 0; + while index < bytes.len() { + match bytes[index] { + b'<' => angle_depth += 1, + b'>' if angle_depth > 0 => angle_depth -= 1, + b'(' => paren_depth += 1, + b')' if paren_depth > 0 => paren_depth -= 1, + b'[' => bracket_depth += 1, + b']' if bracket_depth > 0 => bracket_depth -= 1, + b':' if index + 1 < bytes.len() + && bytes[index + 1] == b':' + && angle_depth == 0 + && paren_depth == 0 + && bracket_depth == 0 => + { + count += 1; + index += 1; + } + _ => {} + } + index += 1; + } + count +} + ///////////////////////// // FunctionInfoBuilder @@ -733,10 +763,8 @@ impl DebugInfoBuilder { let symbol_full_name = symbol.full_name(); // If our name has fewer namespaces than the existing name, assume we lost the namespace info - if simplify_str_to_fqn(func_full_name, true).items.len() - < simplify_str_to_fqn(symbol_full_name.clone(), true) - .items - .len() + if qualified_name_segment_count(func_full_name) + < qualified_name_segment_count(&symbol_full_name.to_string_lossy()) { func.full_name = Some(symbol_full_name.to_string_lossy().to_string()); } diff --git a/plugins/dwarf/dwarf_import/src/functions.rs b/plugins/dwarf/dwarf_import/src/functions.rs index 6755f847f5..48e68ed6e6 100644 --- a/plugins/dwarf/dwarf_import/src/functions.rs +++ b/plugins/dwarf/dwarf_import/src/functions.rs @@ -18,7 +18,6 @@ use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; use crate::types::get_type; use crate::{helpers::*, ReaderType}; -use binaryninja::template_simplifier::simplify_str_to_str; use cpp_demangle::DemangleOptions; use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit}; use regex::Regex; @@ -134,8 +133,7 @@ pub(crate) fn parse_function_entry( if let Ok(sym) = cpp_demangle::Symbol::new(possibly_mangled_name) { if let Ok(demangled) = sym.demangle(demangle_options) { let cleaned = abi_regex.replace_all(&demangled, ""); - let simplified = simplify_str_to_str(&cleaned); - full_name = Some(simplified.to_string_lossy().to_string()); + full_name = Some(cleaned.to_string()); } } } diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs index 6808e60625..8d9068b92b 100644 --- a/plugins/dwarf/dwarf_import/src/lib.rs +++ b/plugins/dwarf/dwarf_import/src/lib.rs @@ -33,7 +33,6 @@ use binaryninja::{ binary_view::BinaryView, debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}, settings::Settings, - template_simplifier::simplify_str_to_str, }; use dwarfreader::create_section_reader_object; @@ -247,15 +246,11 @@ fn recover_names_internal( } debug_info_builder_context.set_name( get_uid(dwarf, &unit, entry), - simplify_str_to_str( - namespace_qualifiers - .iter() - .map(|(_, namespace)| namespace.to_owned()) - .collect::>() - .join("::"), - ) - .to_string_lossy() - .to_string(), + namespace_qualifiers + .iter() + .map(|(_, namespace)| namespace.to_owned()) + .collect::>() + .join("::"), ); } constants::DW_TAG_typedef @@ -264,16 +259,12 @@ fn recover_names_internal( if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) { debug_info_builder_context.set_name( get_uid(dwarf, &unit, entry), - simplify_str_to_str( - namespace_qualifiers - .iter() - .chain(vec![&(-1, name)].into_iter()) - .map(|(_, namespace)| namespace.to_owned()) - .collect::>() - .join("::"), - ) - .to_string_lossy() - .to_string(), + namespace_qualifiers + .iter() + .chain(vec![&(-1, name)].into_iter()) + .map(|(_, namespace)| namespace.to_owned()) + .collect::>() + .join("::"), ); } } diff --git a/plugins/idb_import/CMakeLists.txt b/plugins/idb_import/CMakeLists.txt index 735ac3308d..aca4635e76 100644 --- a/plugins/idb_import/CMakeLists.txt +++ b/plugins/idb_import/CMakeLists.txt @@ -12,6 +12,7 @@ file(GLOB API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/* ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs) if(CMAKE_BUILD_TYPE MATCHES Debug) diff --git a/plugins/pdb-ng/CMakeLists.txt b/plugins/pdb-ng/CMakeLists.txt index 50f0f2af8a..5c2681fc65 100644 --- a/plugins/pdb-ng/CMakeLists.txt +++ b/plugins/pdb-ng/CMakeLists.txt @@ -60,6 +60,7 @@ file(GLOB API_SOURCES CONFIGURE_DEPENDS ${BN_API_SOURCE_DIR}/rust/binaryninjacore-sys/Cargo.toml ${BN_API_SOURCE_DIR}/rust/binaryninjacore-sys/src/* ${BN_API_SOURCE_DIR}/rust/Cargo.toml + ${BN_API_SOURCE_DIR}/rust/src/*.rs ${BN_API_SOURCE_DIR}/rust/src/*/*.rs) set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_FILE_PATH ${OUTPUT_FILE_PATH}) diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index c15566deab..765c1741f2 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -39,7 +39,7 @@ use crate::PDBParserInstance; use binaryninja::architecture::{Architecture, ArchitectureExt, Register, RegisterId}; use binaryninja::binary_view::BinaryViewBase; use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; -use binaryninja::demangle::demangle_ms_with_view; +use binaryninja::demangle::demangle_ms; use binaryninja::rc::Ref; use binaryninja::types::{FunctionParameter, QualifiedName, StructureBuilder, Type, TypeClass}; use binaryninja::variable::{Variable, VariableSourceType}; @@ -1813,9 +1813,15 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { raw_name: &String, rva: Rva, ) -> Result<(Option>>, Option)> { - let (mut t, mut name) = match demangle_ms_with_view(&self.arch, raw_name, Some(self.bv)) { - Some((name, Some(t))) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name), - Some((name, _)) => (None, name), + let simplify_templates = self.settings.get_bool_with_opts( + "analysis.types.templateSimplifier", + &mut self.settings_query_opts.clone(), + ); + let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, simplify_templates) { + Some(result) => ( + result.ty.map(|ty| Conf::new(ty, DEMANGLE_CONFIDENCE)), + result.name, + ), _ => (None, QualifiedName::new(vec![raw_name.clone()])), }; diff --git a/plugins/rtti/itanium.cpp b/plugins/rtti/itanium.cpp index a2ddc59a45..1bd62f8ca0 100644 --- a/plugins/rtti/itanium.cpp +++ b/plugins/rtti/itanium.cpp @@ -672,6 +672,7 @@ ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref &view, bool use { m_view = view; m_logger = view->CreateLogger("Itanium RTTI"); + m_simplifyTemplates = Settings::Instance()->Get("analysis.types.templateSimplifier", view); allowMangledClassNames = useMangled; checkWritableRData = checkRData; m_classInfo = {}; diff --git a/plugins/rtti/microsoft.cpp b/plugins/rtti/microsoft.cpp index ba0757cba8..bba16ad98f 100644 --- a/plugins/rtti/microsoft.cpp +++ b/plugins/rtti/microsoft.cpp @@ -368,7 +368,8 @@ std::vector MicrosoftRTTIProcessor::ProcessClassHierarchyDescript continue; } auto baseClassTypeDesc = TypeDescriptor(m_view, baseClassTypeDescAddr); - auto baseClassName = DemangleNameMS(m_view, allowMangledClassNames, baseClassTypeDesc.name); + auto baseClassName = DemangleNameMS( + m_view, allowMangledClassNames, baseClassTypeDesc.name, m_simplifyTemplates); if (!baseClassName.has_value()) { m_logger->LogWarnF("Skipping BaseClassDescriptor with mangled name {:#x}", baseClassTypeDescAddr); @@ -415,7 +416,7 @@ std::optional MicrosoftRTTIProcessor::ProcessRTTI(uint64_t coLocatorA // Get type descriptor then check to see if the class name was demangled. auto typeDescAddr = resolveAddr(coLocator->pTypeDescriptor); auto typeDesc = TypeDescriptor(m_view, typeDescAddr); - auto className = DemangleNameMS(m_view, allowMangledClassNames, typeDesc.name); + auto className = DemangleNameMS(m_view, allowMangledClassNames, typeDesc.name, m_simplifyTemplates); if (!className.has_value()) return std::nullopt; @@ -607,6 +608,7 @@ MicrosoftRTTIProcessor::MicrosoftRTTIProcessor(const Ref &view, bool { m_view = view; m_logger = view->CreateLogger("Microsoft RTTI"); + m_simplifyTemplates = Settings::Instance()->Get("analysis.types.templateSimplifier", view); allowMangledClassNames = useMangled; allowAnonymousClassNames = allowAnonymous; checkWritableRData = checkRData; diff --git a/plugins/rtti/rtti.cpp b/plugins/rtti/rtti.cpp index f713aef77d..e5dfe9875b 100644 --- a/plugins/rtti/rtti.cpp +++ b/plugins/rtti/rtti.cpp @@ -34,11 +34,12 @@ Ref RTTI::GetRealSymbol(BinaryView *view, uint64_t relocAddr, uint64_t s } -std::optional RTTI::DemangleNameMS(BinaryView* view, bool allowMangled, const std::string &mangledName) +std::optional RTTI::DemangleNameMS( + BinaryView* view, bool allowMangled, const std::string &mangledName, bool simplifyTemplates) { QualifiedName demangledName = {}; Ref outType = {}; - if (!DemangleMS(view->GetDefaultArchitecture(), mangledName, outType, demangledName, view)) + if (!DemangleMS(view->GetDefaultPlatform(), mangledName, outType, demangledName, simplifyTemplates)) return DemangleNameLLVM(allowMangled, mangledName); return NormalizeRTTIClassName(demangledName.GetString()); } @@ -62,7 +63,7 @@ std::optional RTTI::DemangleNameGNU3(BinaryView* view, bool allowMa if (adjustedMangledName.rfind("_Z", 0) != 0) adjustedMangledName = "_Z" + adjustedMangledName; - if (!DemangleGNU3(view->GetDefaultArchitecture(), adjustedMangledName, outType, demangledName, true)) + if (!DemangleGNU3(view->GetDefaultPlatform(), adjustedMangledName, outType, demangledName, true)) return allowMangled ? std::optional(mangledName) : std::nullopt; // Because we might have a generic name such as "PackageListGui::PackageListGui" returned, we must attempt to diff --git a/plugins/rtti/rtti.h b/plugins/rtti/rtti.h index 00b105ebaa..4a05a3a11d 100644 --- a/plugins/rtti/rtti.h +++ b/plugins/rtti/rtti.h @@ -8,7 +8,8 @@ constexpr int RTTI_CONFIDENCE = 100; namespace BinaryNinja::RTTI { Ref GetRealSymbol(BinaryView *view, uint64_t relocAddr, uint64_t symAddr); - std::optional DemangleNameMS(BinaryView* view, bool allowMangled, const std::string &mangledName); + std::optional DemangleNameMS( + BinaryView* view, bool allowMangled, const std::string &mangledName, bool simplifyTemplates); std::optional DemangleNameGNU3(BinaryView* view, bool allowMangled, const std::string &mangledName); @@ -81,6 +82,7 @@ namespace BinaryNinja::RTTI { Ref m_view; Ref m_logger; + bool m_simplifyTemplates = false; std::map m_classInfo; std::map m_unhandledClassInfo; diff --git a/plugins/warp/src/convert/symbol.rs b/plugins/warp/src/convert/symbol.rs index 5d794d30f8..dfb3f10ea3 100644 --- a/plugins/warp/src/convert/symbol.rs +++ b/plugins/warp/src/convert/symbol.rs @@ -1,4 +1,5 @@ use binaryninja::binary_view::BinaryView; +use binaryninja::demangle::{demangle_any, DemanglerConfig}; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::Symbol as BNSymbol; use binaryninja::symbol::SymbolType as BNSymbolType; @@ -75,17 +76,13 @@ pub fn to_bn_symbol_at_address(view: &BinaryView, symbol: &Symbol, addr: u64) -> let raw_name = symbol.name.as_str(); let mut symbol_builder = BNSymbol::builder(symbol_type, &symbol.name, addr); // Demangle symbol name (short is with simplifications). - if let Some(arch) = view.default_arch() { - if let Some((full_name, _)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - symbol_builder = symbol_builder.full_name(full_name); - } - if let Some((short_name, _)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - symbol_builder = symbol_builder.short_name(short_name); - } + let full_config = DemanglerConfig::for_binary_view(view, false); + if let Some(result) = demangle_any(raw_name, &full_config) { + symbol_builder = symbol_builder.full_name(result.name); + } + let short_config = DemanglerConfig::for_binary_view(view, true); + if let Some(result) = demangle_any(raw_name, &short_config) { + symbol_builder = symbol_builder.short_name(result.name); } symbol_builder.create() } diff --git a/plugins/workflow_objc/CMakeLists.txt b/plugins/workflow_objc/CMakeLists.txt index 0d4262fd4b..187b985498 100644 --- a/plugins/workflow_objc/CMakeLists.txt +++ b/plugins/workflow_objc/CMakeLists.txt @@ -81,6 +81,7 @@ file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs) find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin) diff --git a/plugins/workflow_swift/CMakeLists.txt b/plugins/workflow_swift/CMakeLists.txt index d9b027f325..ff9a59de68 100644 --- a/plugins/workflow_swift/CMakeLists.txt +++ b/plugins/workflow_swift/CMakeLists.txt @@ -81,6 +81,7 @@ file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs ${PROJECT_SOURCE_DIR}/../../rust/src/*/*.rs) find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin) diff --git a/plugins/workflow_swift/src/demangler/mod.rs b/plugins/workflow_swift/src/demangler/mod.rs index cee0fcc7bd..9523920f30 100644 --- a/plugins/workflow_swift/src/demangler/mod.rs +++ b/plugins/workflow_swift/src/demangler/mod.rs @@ -2,12 +2,10 @@ mod function_type; mod name; mod type_reconstruction; -use binaryninja::architecture::CoreArchitecture; use binaryninja::binary_view::BinaryView; -use binaryninja::demangle::CustomDemangler; -use binaryninja::rc::Ref; +use binaryninja::demangle::{CustomDemangler, DemanglerConfig, DemanglerResult}; use binaryninja::settings::{QueryOptions, Settings}; -use binaryninja::types::{QualifiedName, Type}; +use binaryninja::types::QualifiedName; fn should_extract_types(view: Option<&BinaryView>) -> bool { let mut opts = match view { @@ -30,27 +28,25 @@ impl CustomDemangler for SwiftDemangler { || name.starts_with("_T") } - fn demangle( - &self, - arch: &CoreArchitecture, - name: &str, - view: Option>, - ) -> Option<(QualifiedName, Option>)> { + fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option { let ctx = swift_demangler::Context::new(); let symbol = swift_demangler::Symbol::parse(&ctx, name)?; - if should_extract_types(view.as_deref()) { - let ty = function_type::build_function_type(&symbol, arch); + if should_extract_types(config.view.as_deref()) { + let ty = config + .platform + .as_ref() + .and_then(|platform| function_type::build_function_type(&symbol, &platform.arch())); let qname = if ty.is_some() { name::build_short_name(&symbol) } else { None } .unwrap_or_else(|| QualifiedName::from(symbol.display())); - Some((qname, ty)) + Some(DemanglerResult::new(qname, ty)) } else { let qname = QualifiedName::from(symbol.display()); - Some((qname, None)) + Some(DemanglerResult::new(qname, None)) } } } diff --git a/plugins/workflow_swift/src/lib.rs b/plugins/workflow_swift/src/lib.rs index d199bf0898..911770ae1a 100644 --- a/plugins/workflow_swift/src/lib.rs +++ b/plugins/workflow_swift/src/lib.rs @@ -30,7 +30,5 @@ pub extern "C" fn CorePluginInit() -> bool { }"#, ); - Demangler::register("Swift", SwiftDemangler); - - true + Demangler::register("Swift", SwiftDemangler) } diff --git a/python/demangle.py b/python/demangle.py index 76bc604625..1de5db99ed 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -19,7 +19,6 @@ # IN THE SOFTWARE. import ctypes -import traceback # Binary Ninja components import binaryninja @@ -27,9 +26,88 @@ from . import binaryview from . import types from .log import log_error_for_exception -from .architecture import Architecture, CoreArchitecture +from .architecture import Architecture from .platform import Platform -from typing import Iterable, List, Optional, Union, Tuple, Any +from .settings import Settings +from typing import Callable, Iterable, List, Optional, Union, Any, NamedTuple + + +class DemangleResult(NamedTuple): + """ + Tuple-compatible demangle result. + """ + + type: Optional['types.Type'] + name: Union['types.QualifiedName', List[str], str] + + +def _demangle_result_from_core_and_free( + result: core.BNDemanglerResult, + qualified_name: bool = False +) -> DemangleResult: + out_type = None + try: + if result.type: + out_type = core.BNNewTypeReference(result.type) + result_var_name = types.QualifiedName._from_core_struct(result.name) + + result_type = None + if out_type: + result_type = types.Type.create(handle=out_type) + out_type = None + result_name = result_var_name if qualified_name else result_var_name.name + return DemangleResult(result_type, result_name) + finally: + if out_type: + core.BNFreeType(out_type) + core.BNFreeDemanglerResult(result) + + +class DemanglerConfig: + """ + Configuration used by demangler APIs. + """ + + def __init__( + self, + arch_or_platform: Optional[Union[Architecture, Platform]] = None, + view: Optional['binaryview.BinaryView'] = None, + simplify: bool = False + ): + if isinstance(arch_or_platform, Architecture): + platform_obj = arch_or_platform.standalone_platform + elif isinstance(arch_or_platform, Platform): + platform_obj = arch_or_platform + elif arch_or_platform is None: + platform_obj = view.platform if view is not None else None + else: + raise TypeError("Unexpected arch or platform type") + + self.platform = platform_obj + self.view = view + self.simplify_templates = simplify + + def _to_core_struct(self) -> core.BNDemanglerConfig: + config = core.BNDemanglerConfig() + config.platform = self.platform.handle if self.platform is not None else None + config.view = self.view.handle if self.view is not None else None + config.simplifyTemplates = self.simplify_templates + return config + + @classmethod + def _from_core_struct(cls, config: core.BNDemanglerConfig) -> 'DemanglerConfig': + if hasattr(config, "contents"): + config = config.contents + + platform = None + if config.platform: + platform = Platform(handle=core.BNNewPlatformReference(config.platform)) + + view = None + if config.view: + view = binaryview.BinaryView(handle=core.BNNewViewReference(config.view)) + + return cls(platform, view, config.simplifyTemplates) def get_qualified_name(names: Iterable[str]): @@ -42,20 +120,54 @@ def get_qualified_name(names: Iterable[str]): :rtype: str :Example: - >>> type, name = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - >>> get_qualified_name(name) + >>> result = demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + >>> get_qualified_name(result.name) 'Foobar::testf' >>> """ return "::".join(names) +def demangle_any( + mangled_name: str, + config: DemanglerConfig +) -> Optional[DemangleResult]: + """ + ``demangle_any`` demangles a mangled symbol name using a prebuilt demangler config. + + :param str mangled_name: a mangled symbol name + :param DemanglerConfig config: Platform/view/options used while demangling + :return: returns a DemangleResult with type and name fields, or None on error. DemangleResult can be unpacked as (type, name). + :rtype: Optional[DemangleResult] + :Example: + + >>> config = DemanglerConfig(Architecture["x86_64"]) + >>> result = demangle_any("?testf@Foobar@@SA?AW4foo@1@W421@@Z", config) + >>> result.type + + >>> result.name + ['Foobar', 'testf'] + """ + if not isinstance(config, DemanglerConfig): + raise TypeError("config must be a DemanglerConfig") + + result = core.BNDemanglerResult() + api_config = config._to_core_struct() + if not core.BNDemangle(mangled_name, api_config, result): + return None + + try: + return _demangle_result_from_core_and_free(result) + except UnicodeDecodeError: + return None + + def demangle_generic( archOrPlatform: Union[Architecture, Platform], mangled_name: str, view: Optional['binaryview.BinaryView'] = None, simplify: bool = False -) -> Optional[Tuple[Optional['types.Type'], List[str]]]: +) -> DemangleResult: """ ``demangle_generic`` demangles a mangled symbol name to a Type object. @@ -63,47 +175,25 @@ def demangle_generic( :param str mangled_name: a mangled symbol name :param view: (optional) view of the binary containing the mangled name :param simplify: (optional) Whether to simplify demangled names - :return: returns tuple of (Optional[Type], demangled_name) or None on error - :rtype: Tuple + :return: returns a DemangleResult with type and name fields. DemangleResult can be unpacked as (type, name). + :rtype: DemangleResult :Example: >>> demangle_generic(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - (, ['Foobar', 'testf']) + DemangleResult(type=, name=['Foobar', 'testf']) >>> demangle_generic(Architecture["x86_64"], "__ZN20ArmCallingConvention27GetIntegerArgumentRegistersEv") - (, ['ArmCallingConvention', 'GetIntegerArgumentRegisters']) + DemangleResult(type=, name=['ArmCallingConvention', 'GetIntegerArgumentRegisters']) >>> """ - arch = None - if isinstance(archOrPlatform, Architecture): - arch = archOrPlatform - elif isinstance(archOrPlatform, Platform): - arch = archOrPlatform.arch - else: - raise TypeError("Unexpected arch or platform type") - - out_type = ctypes.POINTER(core.BNType)() - 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 + config = DemanglerConfig(archOrPlatform, view, simplify) + return demangle_any(mangled_name, config) or DemangleResult(None, [mangled_name]) def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = None) -> Optional[List[str]]: """ ``demangle_llvm`` demangles a mangled name using the LLVM demangler. - :param str mangled_name: a mangled (msvc/itanium/rust/dlang) name + :param str mangled_name: a mangled (msvc/gnu3/rust/dlang) name :param options: (optional) Whether to simplify demangled names : None falls back to user settings, a BinaryView uses that BinaryView's settings, or a boolean to set it directly :type options: Optional[Union[bool, BinaryView]] :return: returns demangled name or None on error @@ -114,172 +204,112 @@ def demangle_llvm(mangled_name: str, options: Optional[Union[bool, binaryview.Bi ['public: static enum Foobar::foo __cdecl Foobar::testf(enum Foobar::foo)'] >>> """ - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if ( - isinstance(options, binaryview.BinaryView) and core.BNDemangleLLVMWithOptions( - mangled_name, ctypes.byref(outName), ctypes.byref(outSize), options.handle - ) - ) or ( - isinstance(options, bool) and core.BNDemangleLLVM( - mangled_name, ctypes.byref(outName), ctypes.byref(outSize), options - ) - ) or ( - options is None and core.BNDemangleLLVMWithOptions( - mangled_name, ctypes.byref(outName), ctypes.byref(outSize), None - ) - ): - for i in range(outSize.value): - try: - names.append(outName[i].decode('utf8')) # type: ignore - except UnicodeDecodeError: - names.append(outName[i].decode('charmap')) # type: ignore - core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) - return names - return None + view, simplify_templates = _simplify_from_compat_option(options) + config = DemanglerConfig(view=view, simplify=simplify_templates)._to_core_struct() + binaryninja._init_plugins() + demangler = core.BNGetLLVMDemangler() + if demangler is None: + return None -def demangle_ms(archOrPlatform: Union[Architecture, Platform], mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = False): - """ - ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. + result = core.BNDemanglerResult() + if not core.BNDemangleWithDemangler(demangler, mangled_name, config, result): + return None - :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 Microsoft Visual Studio C++ name - :param options: (optional) Whether to simplify demangled names : None falls back to user settings, a BinaryView uses that BinaryView's settings, or a boolean to set it directly - :type options: Optional[Union[bool, BinaryView]] - :return: returns tuple of (Type, demangled_name) or (None, mangled_name) on error - :rtype: Tuple[Optional[Type], Union[str, List[str]]] - :Example: + return _demangle_result_from_core_and_free(result).name - >>> demangle_ms(Platform["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") - (, ['Foobar', 'testf']) - >>> - """ - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - - demangle = core.BNDemangleMS - demangleWithOptions = core.BNDemangleMSWithOptions - - if isinstance(archOrPlatform, Platform): - demangle = core.BNDemangleMSPlatform - - if ( - isinstance(options, binaryview.BinaryView) and demangleWithOptions( - archOrPlatform.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options.handle - ) - ) or ( - isinstance(options, bool) and demangle( - archOrPlatform.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options - ) - ) or ( - options is None and demangleWithOptions( - archOrPlatform.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), None - ) - ): - for i in range(outSize.value): - names.append(outName[i].decode('utf8')) # type: ignore - core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) - if not handle: - return (None, names) - return (types.Type.create(handle), names) - return (None, mangled_name) +def _simplify_from_compat_option(simplify: Optional[Union[bool, binaryview.BinaryView]]): + if isinstance(simplify, binaryview.BinaryView): + return simplify, Settings().get_bool("analysis.types.templateSimplifier", simplify) + if simplify is None: + return None, Settings().get_bool("analysis.types.templateSimplifier") + if isinstance(simplify, bool): + return None, simplify + raise TypeError("simplify must be a bool") -def demangle_gnu3(arch, mangled_name: str, options: Optional[Union[bool, binaryview.BinaryView]] = None): - """ - ``demangle_gnu3`` demangles a mangled name to a Type object. - :param Architecture arch: Architecture for the symbol. Required for pointer and integer sizes. - :param str mangled_name: a mangled GNU3 name - :param options: (optional) Whether to simplify demangled names : None falls back to user settings, a BinaryView uses that BinaryView's settings, or a boolean to set it directly - :type options: Optional[Union[bool, BinaryView]] - :return: returns tuple of (Type, demangled_name) or (None, mangled_name) on error - :rtype: Tuple[Optional[Type], Union[str, List[str]]] - """ - handle = ctypes.POINTER(core.BNType)() - outName = ctypes.POINTER(ctypes.c_char_p)() - outSize = ctypes.c_ulonglong() - names = [] - if ( - isinstance(options, binaryview.BinaryView) and core.BNDemangleGNU3WithOptions( - arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options.handle - ) - ) or ( - isinstance(options, bool) and core.BNDemangleGNU3( - arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options - ) - ) or ( - options is None and core.BNDemangleGNU3WithOptions( - arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), None - ) - ): - for i in range(outSize.value): - names.append(outName[i].decode('utf8')) # type: ignore - core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) - if not handle: - return (None, names) - return (types.Type.create(handle), names) - return (None, mangled_name) +def _demangle_type_and_name( + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: Optional[Union[bool, binaryview.BinaryView]], + demangler_getter: Callable[[], Any] +) -> DemangleResult: + view, simplify_templates = _simplify_from_compat_option(simplify) + + binaryninja._init_plugins() + demangler = demangler_getter() + if demangler is None: + return DemangleResult(None, mangled_name) + + config = DemanglerConfig(arch_or_platform, view, simplify_templates)._to_core_struct() + result = core.BNDemanglerResult() + if not core.BNDemangleWithDemangler(demangler, mangled_name, config, result): + return DemangleResult(None, mangled_name) + + return _demangle_result_from_core_and_free(result) -def simplify_name_to_string(input_name: Union[str, types.QualifiedName]): +def demangle_ms( + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: bool = False +) -> DemangleResult: """ - ``simplify_name_to_string`` simplifies a templated C++ name with default arguments and returns a string + ``demangle_ms`` demangles a mangled Microsoft Visual Studio C++ name to a Type object. - :param input_name: String or qualified name to be simplified - :type input_name: Union[str, QualifiedName] - :return: simplified name (or original name if simplifier fails/cannot simplify) - :rtype: str + :param Union[Architecture, Platform] arch_or_platform: Architecture or Platform for the symbol. Required for pointer/integer sizes and calling conventions. + :param str mangled_name: a mangled Microsoft Visual Studio C++ name + :param bool simplify: (optional) Whether to simplify demangled names + :return: returns a DemangleResult with type and name fields, or DemangleResult(None, mangled_name) on error + :rtype: DemangleResult :Example: - >>> demangle.simplify_name_to_string("std::__cxx11::basic_string, std::allocator >") - 'std::string' + >>> demangle_ms(Architecture["x86_64"], "?testf@Foobar@@SA?AW4foo@1@W421@@Z") + DemangleResult(type=, name=['Foobar', 'testf']) >>> """ - result = None - if isinstance(input_name, str): - result = core.BNRustSimplifyStrToStr(input_name) - elif isinstance(input_name, types.QualifiedName): - result = core.BNRustSimplifyStrToStr(str(input_name)) - else: - raise TypeError("Parameter must be of type `str` or `types.QualifiedName`") - return result + return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, core.BNGetMSVCDemangler) -def simplify_name_to_qualified_name(input_name: Union[str, types.QualifiedName], simplify: bool = True): +def demangle_gnu3( + arch_or_platform: Union[Architecture, Platform], + mangled_name: str, + simplify: bool = False +) -> DemangleResult: """ - ``simplify_name_to_qualified_name`` simplifies a templated C++ name with default arguments and returns a qualified name. This can also tokenize a string to a qualified name with/without simplifying it + ``demangle_gnu3`` demangles a mangled name to a Type object. - :param input_name: String or qualified name to be simplified - :type input_name: Union[str, QualifiedName] - :param bool simplify: (optional) Whether to simplify input string (no effect if given a qualified name; will always simplify) - :return: simplified name (or one-element array containing the input if simplifier fails/cannot simplify) - :rtype: QualifiedName - :Example: + :param Union[Architecture, Platform] arch_or_platform: Architecture or Platform for the symbol. Required for pointer and integer sizes. + :param str mangled_name: a mangled GNU3 name + :param bool simplify: (optional) Whether to simplify demangled names + :return: returns a DemangleResult with type and name fields, or DemangleResult(None, mangled_name) on error + :rtype: DemangleResult + """ + return _demangle_type_and_name(arch_or_platform, mangled_name, simplify, core.BNGetGNU3Demangler) - >>> demangle.simplify_name_to_qualified_name(QualifiedName(["std", "__cxx11", "basic_string, std::allocator >"]), True) - 'std::wstring' - >>> + +def simplify_demangled_template_name( + name: Union[str, Iterable[str], types.QualifiedName]) -> types.QualifiedName: """ - name = None - if isinstance(input_name, str): - name = core.BNRustSimplifyStrToFQN(input_name, simplify) - assert name is not None, "core.BNRustSimplifyStrToFQN returned None" - elif isinstance(input_name, types.QualifiedName): - name = core.BNRustSimplifyStrToFQN(str(input_name), True) - assert name is not None, "core.BNRustSimplifyStrToFQN returned None" + ``simplify_demangled_template_name`` simplifies standard-library template spelling in + an already demangled qualified name. + """ + if isinstance(name, types.QualifiedName): + qualified_name = name + elif isinstance(name, str): + qualified_name = types.QualifiedName(name) else: - raise TypeError("Parameter must be of type `str` or `types.QualifiedName`") + qualified_name = types.QualifiedName(list(name)) - result = types.QualifiedName._from_core_struct(name) - core.BNFreeQualifiedName(name) - if len(result) == 0: - return None - return result + api_name = qualified_name._to_core_struct() + result = core.BNQualifiedName() + if not core.BNSimplifyDemangledTemplateName(ctypes.byref(api_name), ctypes.byref(result)): + return qualified_name + try: + return types.QualifiedName._from_core_struct(result) + finally: + core.BNFreeQualifiedName(result) class _DemanglerMetaclass(type): @@ -323,10 +353,12 @@ class Demangler(metaclass=_DemanglerMetaclass): Pluggable name demangling interface. See :py:func:`register` and :py:func:`demangle` for details on the process of this interface. + Custom Demangler subclasses can be registered and promoted at runtime. + The list of Demanglers can be queried: >>> list(Demangler) - [, ] + [, , ] """ name = None @@ -345,6 +377,8 @@ def register(cls): """ Register a custom Demangler. Newly registered demanglers will get priority over previously registered demanglers and built-in demanglers. + + :return: True if registration succeeded; False if the demangler was invalid. """ demangler = cls() @@ -352,27 +386,29 @@ def register(cls): assert demangler.handle is None demangler._cb = core.BNDemanglerCallbacks() + demangler._cb.size = ctypes.sizeof(core.BNDemanglerCallbacks) demangler._cb.context = 0 demangler._cb.isMangledString = demangler._cb.isMangledString.__class__(demangler._is_mangled_string) demangler._cb.demangle = demangler._cb.demangle.__class__(demangler._demangle) - demangler._cb.freeVarName = demangler._cb.freeVarName.__class__(demangler._free_var_name) + demangler._cb.freeResult = demangler._cb.freeResult.__class__(demangler._free_result) demangler.handle = core.BNRegisterDemangler(cls.name, demangler._cb) + if not demangler.handle: + return False + cls._registered_demanglers.append(demangler) + return True @classmethod def promote(cls, demangler): """ Promote a demangler to the highest-priority position. - >>> list(Demangler) - [, ] - >>> Demangler.promote(list(Demangler)[0]) - >>> list(Demangler) - [, ] - :param demangler: Demangler to promote + :return: True if promotion succeeded; False if the demangler was invalid or not registered. """ - core.BNPromoteDemangler(demangler.handle) + if demangler is None or demangler.handle is None: + return False + return core.BNPromoteDemangler(demangler.handle) def __eq__(self, other): if not isinstance(other, Demangler): @@ -392,37 +428,37 @@ def _is_mangled_string(self, ctxt, name): log_error_for_exception("Unhandled Python exception in Demangler._is_mangled_string") return False - def _demangle(self, ctxt, arch, name, out_type, out_var_name, view): + def _demangle(self, ctxt, name, config, result): try: - api_arch = CoreArchitecture._from_cache(arch) - api_view = None - if view is not None: - api_view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + api_config = DemanglerConfig._from_core_struct(config) - result = self.demangle(api_arch, core.pyNativeStr(name), api_view) - if result is None: + demangle_result = self.demangle(core.pyNativeStr(name), api_config) + if demangle_result is None: return False - type, var_name = result + type, var_name = demangle_result if not isinstance(var_name, types.QualifiedName): var_name = types.QualifiedName(var_name) - Demangler._cached_name = var_name._to_core_struct() + Demangler._cached_name = core.BNDemanglerResult() + Demangler._cached_name.name = var_name._to_core_struct() if type is not None: - out_type[0] = core.BNNewTypeReference(type.handle) + Demangler._cached_name.type = core.BNNewTypeReference(type.handle) else: - out_type[0] = None - out_var_name[0] = Demangler._cached_name + Demangler._cached_name.type = None + result[0] = Demangler._cached_name return True except Exception: log_error_for_exception("Unhandled Python exception in Demangler._demangle") return False - def _free_var_name(self, ctxt, name): + def _free_result(self, ctxt, result): try: + if result is not None and result.contents.type: + core.BNFreeType(result.contents.type) Demangler._cached_name = None except Exception: - log_error_for_exception("Unhandled Python exception in Demangler._free_var_name") + log_error_for_exception("Unhandled Python exception in Demangler._free_result") def is_mangled_string(self, name: str) -> bool: """ @@ -441,15 +477,15 @@ def is_mangled_string(self, name: str) -> bool: def demangle( self, - arch: Architecture, name: str, - view: Optional['binaryview.BinaryView'] = None - ) -> Optional[Tuple['types.Type', 'types.QualifiedName']]: + config: DemanglerConfig + ) -> Optional[DemangleResult]: """ Demangle a raw name into a Type and QualifiedName. - The result of this function is a (Type, QualifiedName) tuple for the demangled - name's details. + The result of this function is a DemangleResult with Type and QualifiedName + fields for the demangled name's details. DemangleResult can be unpacked as + (type, name). Any unresolved named types referenced by the resulting Type will be created as empty structures or void typedefs in the view, if the result is used on @@ -463,36 +499,33 @@ def demangle( If this call returns None, the next most recently used demangler(s) will be tried instead. If the mangled name has no type information, but a name is still possible to extract, - this function may return a successful (None, ) result, which will be accepted. + this function may return a successful DemangleResult(None, ), which will be accepted. - :param arch: Architecture for context in which the name exists, eg for pointer sizes :param name: Raw mangled name - :param view: (Optional) BinaryView context in which the name exists, eg for type lookup - :return: Tuple of (Type, Name) if successful, None if not. Type may be None if only - a demangled name can be recovered from the raw name. + :param config: Platform/view/options used while demangling + :return: DemangleResult with type and name fields if successful, None if not. + Type may be None if only a demangled name can be recovered from the raw name. """ raise NotImplementedError() + @staticmethod + def demangle_any(name: str, config: DemanglerConfig) -> Optional[DemangleResult]: + """ + Demangle a raw name using a prebuilt DemanglerConfig. + """ + return demangle_any(name, config) + class CoreDemangler(Demangler): def is_mangled_string(self, name: str) -> bool: return core.BNIsDemanglerMangledName(self.handle, name) - def demangle(self, arch: Architecture, name: str, view: Optional['binaryview.BinaryView'] = None) -> 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 + def demangle(self, name: str, config: DemanglerConfig) -> Optional[DemangleResult]: + result = core.BNDemanglerResult() + api_config = config._to_core_struct() - if not core.BNDemanglerDemangle(self.handle, arch.handle, name, out_type, out_var_name, view_handle): + if not core.BNDemangleWithDemangler(self.handle, name, api_config, result): return None - result_type = None - if out_type: - result_type = types.Type.create(handle=out_type) - result_var_name = types.QualifiedName._from_core_struct(out_var_name) - core.BNFreeQualifiedName(out_var_name) - return result_type, result_var_name + return _demangle_result_from_core_and_free(result, qualified_name=True) diff --git a/python/types.py b/python/types.py index 04ec5e780c..a4e36fc22f 100644 --- a/python/types.py +++ b/python/types.py @@ -202,7 +202,6 @@ def escape(name: QualifiedNameType, escaping: TokenEscapingType) -> str: def unescape(name: QualifiedNameType, escaping: TokenEscapingType) -> str: return core.BNUnescapeTypeName(str(QualifiedName(name)), escaping) - @dataclass(frozen=True) class TypeReferenceSource: name: QualifiedName diff --git a/rust/examples/demangler.rs b/rust/examples/demangler.rs index edefaec19b..15a970c7df 100644 --- a/rust/examples/demangler.rs +++ b/rust/examples/demangler.rs @@ -1,7 +1,5 @@ -use binaryninja::architecture::CoreArchitecture; -use binaryninja::binary_view::BinaryView; -use binaryninja::demangle::{CustomDemangler, Demangler}; -use binaryninja::rc::Ref; +use binaryninja::architecture::{ArchitectureExt, CoreArchitecture}; +use binaryninja::demangle::{CustomDemangler, Demangler, DemanglerConfig, DemanglerResult}; use binaryninja::tracing::TracingLogListener; use binaryninja::types::{QualifiedName, Type}; @@ -12,15 +10,16 @@ impl CustomDemangler for TestDemangler { name == "test_name" || name == "test_name2" } - fn demangle( - &self, - _arch: &CoreArchitecture, - name: &str, - _view: Option>, - ) -> Option<(QualifiedName, Option>)> { + fn demangle(&self, name: &str, _config: &DemanglerConfig) -> Option { match name { - "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), - "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), + "test_name" => Some(DemanglerResult::new( + QualifiedName::from(vec!["test_name"]), + Some(Type::bool()), + )), + "test_name2" => Some(DemanglerResult::new( + QualifiedName::from(vec!["test_name2", "aaa"]), + None, + )), _ => None, } } @@ -30,14 +29,18 @@ fn main() { tracing_subscriber::fmt::init(); let _listener = TracingLogListener::new().register(); + tracing::info!("Registering demangler..."); + assert!(Demangler::register("Test", TestDemangler)); + // This loads all the core architecture, platform, etc plugins let _headless_session = binaryninja::headless::Session::new().expect("Failed to initialize session"); - tracing::info!("Registering demangler..."); - Demangler::register("Test", TestDemangler); - let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86 exists"); + let platform = placeholder_arch + .standalone_platform() + .expect("x86 standalone platform exists"); + let config = DemanglerConfig::for_platform(&platform, false); for d in Demangler::list().iter() { tracing::info!("{}", d.name()); @@ -46,17 +49,8 @@ fn main() { " \"__ZN1AC2Ei\" is mangled? {}", d.is_mangled_string("__ZN1AC2Ei") ); - tracing::info!( - " \"__ZN1AC2Ei\" : {:?}", - d.demangle(&placeholder_arch, "__ZN1AC2Ei", None) - ); - tracing::info!( - " \"test_name\" : {:?}", - d.demangle(&placeholder_arch, "test_name", None) - ); - tracing::info!( - " \"test_name2\" : {:?}", - d.demangle(&placeholder_arch, "test_name2", None) - ); + tracing::info!(" \"__ZN1AC2Ei\" : {:?}", d.demangle("__ZN1AC2Ei", &config)); + tracing::info!(" \"test_name\" : {:?}", d.demangle("test_name", &config)); + tracing::info!(" \"test_name2\" : {:?}", d.demangle("test_name2", &config)); } } diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 3d1c243d57..94ebaf2157 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -1468,7 +1468,10 @@ impl BinaryView { let name_handle = unsafe { let id_str = BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name); - BNDefineAnalysisType(self.handle, id_str, &mut raw_name, type_obj.handle) + let name_handle = + BNDefineAnalysisType(self.handle, id_str, &mut raw_name, type_obj.handle); + BNFreeString(id_str); + name_handle }; QualifiedName::free_raw(raw_name); QualifiedName::from_owned_raw(name_handle) diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index d6aad25cee..f28736b20f 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -16,192 +16,275 @@ use binaryninjacore_sys::*; use std::ffi::{c_char, c_void}; +use std::ptr; -use crate::architecture::CoreArchitecture; +use crate::architecture::{ArchitectureExt, CoreArchitecture}; use crate::binary_view::BinaryView; -use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::types::{QualifiedName, Type}; - +use crate::platform::Platform; +use crate::qualified_name::QualifiedName; use crate::rc::*; +use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::types::Type; pub type Result = std::result::Result; -pub fn demangle_generic( - arch: &CoreArchitecture, - mangled_name: &str, - view: Option<&BinaryView>, - simplify: bool, -) -> Option<(QualifiedName, Option>)> { - let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name = BNQualifiedName::default(); - let res = unsafe { - BNDemangleGeneric( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()), - simplify, - ) - }; +fn msvc_demangler() -> *mut BNDemangler { + unsafe { BNGetMSVCDemangler() } +} + +fn gnu3_demangler() -> *mut BNDemangler { + unsafe { BNGetGNU3Demangler() } +} + +fn llvm_demangler() -> *mut BNDemangler { + unsafe { BNGetLLVMDemangler() } +} + +/// Platform, view, and simplification options used by demangler APIs. +#[derive(Clone, Debug, Default)] +pub struct DemanglerConfig { + pub platform: Option>, + pub view: Option>, + pub simplify_templates: bool, +} + +impl DemanglerConfig { + pub fn new( + platform: Option<&Platform>, + view: Option<&BinaryView>, + simplify_templates: bool, + ) -> Self { + Self { + platform: platform.map(|platform| platform.to_owned()), + view: view.map(|view| view.to_owned()), + simplify_templates, + } + } + + fn for_architecture(arch: &CoreArchitecture, simplify_templates: bool) -> Self { + Self { + platform: arch.standalone_platform(), + view: None, + simplify_templates, + } + } + + fn for_architecture_with_view( + arch: &CoreArchitecture, + view: Option<&BinaryView>, + simplify_templates: bool, + ) -> Self { + Self { + platform: arch.standalone_platform(), + view: view.map(|view| view.to_owned()), + simplify_templates, + } + } + + pub fn for_platform(platform: &Platform, simplify_templates: bool) -> Self { + Self { + platform: Some(platform.to_owned()), + view: None, + simplify_templates, + } + } + + pub fn for_binary_view(view: &BinaryView, simplify_templates: bool) -> Self { + let platform = view.default_platform().or_else(|| { + view.default_arch() + .and_then(|arch| arch.standalone_platform()) + }); + Self { + platform, + view: Some(view.to_owned()), + simplify_templates, + } + } - if res { - let out_type = match out_type.is_null() { + pub fn from_api_object(config: &BNDemanglerConfig) -> Self { + let platform = match config.platform.is_null() { true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), + false => { + Some(unsafe { Platform::ref_from_raw(BNNewPlatformReference(config.platform)) }) + } }; - Some((QualifiedName::from_owned_raw(out_name), out_type)) - } else { - None + let view = match config.view.is_null() { + true => None, + false => Some(unsafe { BinaryView::ref_from_raw(BNNewViewReference(config.view)) }), + }; + Self { + platform, + view, + simplify_templates: config.simplifyTemplates, + } + } + + unsafe fn from_api_object_ptr(config: *const BNDemanglerConfig) -> Self { + match config.is_null() { + true => Self::default(), + false => Self::from_api_object(unsafe { &*config }), + } + } + + pub fn to_api_object(&self) -> BNDemanglerConfig { + BNDemanglerConfig { + platform: self + .platform + .as_ref() + .map(|platform| platform.handle) + .unwrap_or(ptr::null_mut()), + view: self + .view + .as_ref() + .map(|view| view.handle) + .unwrap_or(ptr::null_mut()), + simplifyTemplates: self.simplify_templates, + } } } -pub fn demangle_llvm(mangled_name: &str, simplify: bool) -> Option { - let mangled_name = mangled_name.to_cstr(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; - let res = unsafe { - BNDemangleLLVM( - mangled_name.as_ptr(), - &mut out_name, - &mut out_size, - simplify, - ) - }; +/// Demangled name and optional type recovered from a mangled name. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DemanglerResult { + pub name: QualifiedName, + pub ty: Option>, +} - match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - Some(names.into()) +/// Compatibility alias matching the Python API name. +pub type DemangleResult = DemanglerResult; + +impl DemanglerResult { + pub fn new(name: impl Into, ty: Option>) -> Self { + Self { + name: name.into(), + ty, } - false => None, + } + + pub fn from_api_object(result: &BNDemanglerResult) -> Self { + let ty = match result.type_.is_null() { + true => None, + false => Some(unsafe { Type::ref_from_raw(BNNewTypeReference(result.type_)) }), + }; + Self { + name: QualifiedName::from_raw(&result.name), + ty, + } + } + + pub fn from_api_object_and_free(result: &mut BNDemanglerResult) -> Self { + let demangler_result = Self::from_api_object(result); + unsafe { BNFreeDemanglerResult(result) }; + demangler_result + } + + pub fn to_api_object(&self) -> BNDemanglerResult { + BNDemanglerResult { + name: QualifiedName::into_raw(self.name.clone()), + type_: self + .ty + .as_ref() + .map(|ty| unsafe { BNNewTypeReference(ty.handle) }) + .unwrap_or(ptr::null_mut()), + } + } + + pub fn into_tuple(self) -> (QualifiedName, Option>) { + (self.name, self.ty) } } -pub fn demangle_gnu3( - arch: &CoreArchitecture, - mangled_name: &str, - simplify: bool, -) -> Option<(QualifiedName, Option>)> { - let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; - let res = unsafe { - BNDemangleGNU3( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - &mut out_size, - simplify, - ) - }; +impl From<(QualifiedName, Option>)> for DemanglerResult { + fn from(value: (QualifiedName, Option>)) -> Self { + Self { + name: value.0, + ty: value.1, + } + } +} + +impl From for (QualifiedName, Option>) { + fn from(value: DemanglerResult) -> Self { + value.into_tuple() + } +} +fn collect_demangler_result(res: bool, result: &mut BNDemanglerResult) -> Option { match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((names.into(), out_type)) - } + true => Some(DemanglerResult::from_api_object_and_free(result)), false => None, } } -pub fn demangle_ms( - arch: &CoreArchitecture, +fn demangle_with_demangler( + demangler: *mut BNDemangler, mangled_name: &str, - simplify: bool, -) -> Option<(QualifiedName, Option>)> { + config: &DemanglerConfig, +) -> Option { + if demangler.is_null() { + return None; + } + let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; + let api_config = config.to_api_object(); + let mut result = BNDemanglerResult::default(); let res = unsafe { - BNDemangleMS( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - &mut out_size, - simplify, - ) + BNDemangleWithDemangler(demangler, mangled_name.as_ptr(), &api_config, &mut result) }; + collect_demangler_result(res, &mut result) +} - match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((names.into(), out_type)) - } - false => None, - } +pub fn demangle_any(mangled_name: &str, config: &DemanglerConfig) -> Option { + let mangled_name = mangled_name.to_cstr(); + let api_config = config.to_api_object(); + let mut result = BNDemanglerResult::default(); + let res = unsafe { BNDemangle(mangled_name.as_ptr(), &api_config, &mut result) }; + collect_demangler_result(res, &mut result) } -pub fn demangle_ms_with_view( +pub fn demangle_generic( arch: &CoreArchitecture, mangled_name: &str, view: Option<&BinaryView>, -) -> Option<(QualifiedName, Option>)> { - let mangled_name = mangled_name.to_cstr(); - let mut out_type: *mut BNType = std::ptr::null_mut(); - let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); - let mut out_size: usize = 0; - let res = unsafe { - BNDemangleMSWithOptions( - arch.handle, - mangled_name.as_ptr(), - &mut out_type, - &mut out_name, - &mut out_size, - view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()), - ) - }; + simplify: bool, +) -> Option { + let config = DemanglerConfig::for_architecture_with_view(arch, view, simplify); + demangle_any(mangled_name, &config) +} - match res { - true => { - assert!(!out_name.is_null()); - let names: Vec<_> = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - let out_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((names.into(), out_type)) - } - false => None, +pub fn demangle_llvm(mangled_name: &str, simplify: bool) -> Option { + let config = DemanglerConfig::new(None, None, simplify); + demangle_with_demangler(llvm_demangler(), mangled_name, &config).map(|result| result.name) +} + +pub fn demangle_gnu3( + arch: &CoreArchitecture, + mangled_name: &str, + simplify: bool, +) -> Option { + let config = DemanglerConfig::for_architecture(arch, simplify); + demangle_with_demangler(gnu3_demangler(), mangled_name, &config) +} + +pub fn demangle_ms( + arch: &CoreArchitecture, + mangled_name: &str, + simplify: bool, +) -> Option { + let config = DemanglerConfig::for_architecture(arch, simplify); + demangle_with_demangler(msvc_demangler(), mangled_name, &config) +} + +pub fn simplify_demangled_template_name(name: impl Into) -> QualifiedName { + let name = name.into(); + let raw_name = QualifiedName::into_raw(name.clone()); + let mut result = BNQualifiedName::default(); + let ok = unsafe { BNSimplifyDemangledTemplateName(&raw_name, &mut result) }; + QualifiedName::free_raw(raw_name); + if ok { + QualifiedName::from_owned_raw(result) + } else { + name } } @@ -227,44 +310,8 @@ impl Demangler { unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) } } - pub fn demangle( - &self, - arch: &CoreArchitecture, - name: &str, - view: Option<&BinaryView>, - ) -> Option<(QualifiedName, Option>)> { - let name_bytes = name.to_cstr(); - - let mut out_type = std::ptr::null_mut(); - let mut out_var_name = BNQualifiedName::default(); - - let view_ptr = match view { - Some(v) => v.handle, - None => std::ptr::null_mut(), - }; - - let res = unsafe { - BNDemanglerDemangle( - self.handle, - arch.handle, - name_bytes.as_ref().as_ptr() as *const _, - &mut out_type, - &mut out_var_name, - view_ptr, - ) - }; - - match res { - true => { - let var_type = match out_type.is_null() { - true => None, - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - Some((QualifiedName::from_owned_raw(out_var_name), var_type)) - } - false => None, - } + pub fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option { + demangle_with_demangler(self.handle, name, config) } pub fn name(&self) -> String { @@ -281,7 +328,7 @@ impl Demangler { } } - pub fn register(name: &str, demangler: C) -> Self { + pub fn register(name: &str, demangler: C) -> bool { extern "C" fn cb_is_mangled_string(ctxt: *mut c_void, name: *const c_char) -> bool where C: CustomDemangler, @@ -296,35 +343,27 @@ impl Demangler { } extern "C" fn cb_demangle( ctxt: *mut c_void, - arch: *mut BNArchitecture, name: *const c_char, - out_type: *mut *mut BNType, - out_var_name: *mut BNQualifiedName, - view: *mut BNBinaryView, + config: *const BNDemanglerConfig, + result: *mut BNDemanglerResult, ) -> bool where C: CustomDemangler, { ffi_wrap!("CustomDemangler::cb_demangle", unsafe { + if result.is_null() { + return false; + } + let cmd = &*(ctxt as *const C); - let arch = CoreArchitecture::from_raw(arch); let Some(name) = raw_to_string(name) else { return false; }; - let view = match view.is_null() { - false => Some(BinaryView::from_raw(view).to_owned()), - true => None, - }; + let config = DemanglerConfig::from_api_object_ptr(config); - match cmd.demangle(&arch, &name, view) { - Some((name, ty)) => { - // NOTE: Leaked to the caller, who must pick the ref up. - *out_type = match ty { - Some(t) => Ref::into_raw(t).handle, - None => std::ptr::null_mut(), - }; - // NOTE: Leaked to be freed with `cb_free_var_name`. - *out_var_name = QualifiedName::into_raw(name); + match cmd.demangle(&name, &config) { + Some(demangle_result) => { + *result = demangle_result.to_api_object(); true } None => false, @@ -332,10 +371,11 @@ impl Demangler { }) } - extern "C" fn cb_free_var_name(_ctxt: *mut c_void, name: *mut BNQualifiedName) { - ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe { - // TODO: What is the point of this free callback? - QualifiedName::free_raw(*name) + extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNDemanglerResult) { + ffi_wrap!("CustomDemangler::cb_free_result", unsafe { + if !result.is_null() { + BNFreeDemanglerResult(result); + } }) } @@ -343,25 +383,35 @@ impl Demangler { let name_ptr = name.as_ptr(); let ctxt = Box::into_raw(Box::new(demangler)); - let callbacks = BNDemanglerCallbacks { + let callbacks = Box::into_raw(Box::new(BNDemanglerCallbacks { + size: std::mem::size_of::(), context: ctxt as *mut c_void, isMangledString: Some(cb_is_mangled_string::), demangle: Some(cb_demangle::), - freeVarName: Some(cb_free_var_name), - }; - - unsafe { - Demangler::from_raw(BNRegisterDemangler( - name_ptr, - Box::leak(Box::new(callbacks)), - )) + freeResult: Some(cb_free_result), + })); + + let handle = unsafe { BNRegisterDemangler(name_ptr, callbacks) }; + if handle.is_null() { + unsafe { + drop(Box::from_raw(ctxt)); + drop(Box::from_raw(callbacks)); + } + false + } else { + true } } - pub fn promote(demangler: &Demangler) { - unsafe { - BNPromoteDemangler(demangler.handle); + pub fn demangle_any(name: &str, config: &DemanglerConfig) -> Option { + demangle_any(name, config) + } + + pub fn promote(demangler: &Demangler) -> bool { + if demangler.handle.is_null() { + return false; } + unsafe { BNPromoteDemangler(demangler.handle) } } } @@ -388,10 +438,5 @@ unsafe impl CoreArrayProviderInner for Demangler { pub trait CustomDemangler: 'static + Sync { fn is_mangled_string(&self, name: &str) -> bool; - fn demangle( - &self, - arch: &CoreArchitecture, - name: &str, - view: Option>, - ) -> Option<(QualifiedName, Option>)>; + fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option; } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 854bbbcb1f..e41ad92733 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -82,7 +82,6 @@ pub mod string; pub mod string_detection; pub mod symbol; pub mod tags; -pub mod template_simplifier; pub mod tracing; pub mod types; pub mod update; diff --git a/rust/src/rc.rs b/rust/src/rc.rs index 7635455ac4..327560d6b0 100644 --- a/rust/src/rc.rs +++ b/rust/src/rc.rs @@ -318,14 +318,6 @@ where #[allow(private_bounds)] impl ArrayGuard

{ - pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self { - Self { - contents: raw, - count, - context, - } - } - #[inline] pub fn len(&self) -> usize { self.count diff --git a/rust/src/template_simplifier.rs b/rust/src/template_simplifier.rs deleted file mode 100644 index 81f8deb403..0000000000 --- a/rust/src/template_simplifier.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{ - string::{BnString, IntoCStr}, - types::QualifiedName, -}; -use binaryninjacore_sys::{BNRustSimplifyStrToFQN, BNRustSimplifyStrToStr}; - -pub fn simplify_str_to_str(input: S) -> BnString { - let name = input.to_cstr(); - unsafe { BnString::from_raw(BNRustSimplifyStrToStr(name.as_ptr())) } -} - -pub fn simplify_str_to_fqn(input: S, simplify: bool) -> QualifiedName { - let name = input.to_cstr(); - unsafe { QualifiedName::from_owned_raw(BNRustSimplifyStrToFQN(name.as_ptr(), simplify)) } -} diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index 3c1d021600..6ce2f59d07 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -1,16 +1,57 @@ -use binaryninja::architecture::CoreArchitecture; -use binaryninja::binary_view::BinaryView; +use binaryninja::architecture::{ArchitectureExt, CoreArchitecture}; use binaryninja::demangle::{ - demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, CustomDemangler, Demangler, + demangle_any, demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, + simplify_demangled_template_name, CustomDemangler, Demangler, DemanglerConfig, DemanglerResult, }; use binaryninja::headless::Session; -use binaryninja::rc::Ref; use binaryninja::types::{QualifiedName, Type}; +use std::sync::OnceLock; + +struct TestDemangler; + +impl CustomDemangler for TestDemangler { + fn is_mangled_string(&self, name: &str) -> bool { + name == "test_name" || name == "test_name2" + } + + fn demangle(&self, name: &str, config: &DemanglerConfig) -> Option { + match name { + "test_name" => Some(DemanglerResult::new( + QualifiedName::from(vec![if config.simplify_templates { + "test_name_simplified" + } else { + "test_name" + }]), + Some(Type::bool()), + )), + "test_name2" => Some(DemanglerResult::new( + QualifiedName::from(vec!["test_name2", "aaa"]), + None, + )), + _ => None, + } + } +} + +fn register_custom_demangler() { + static CUSTOM_DEMANGLER: OnceLock<()> = OnceLock::new(); + CUSTOM_DEMANGLER.get_or_init(|| { + assert!(Demangler::register("Test", TestDemangler)); + }); +} + +fn session() -> &'static Session { + static SESSION: OnceLock = OnceLock::new(); + SESSION.get_or_init(|| { + register_custom_demangler(); + Session::new().expect("Failed to initialize session") + }) +} #[test] fn test_demangler_simple() { - let _session = Session::new().expect("Failed to initialize session"); - let placeholder_arch = CoreArchitecture::by_name("x86").expect("x86 exists"); + let _session = session(); + let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86_64 exists"); // Example LLVM-style mangled name let llvm_mangled = "_Z3fooi"; // "foo(int)" in LLVM mangling let llvm_demangled = demangle_llvm(llvm_mangled, true).unwrap(); @@ -18,63 +59,61 @@ fn test_demangler_simple() { // Example GNU-style mangled name let gnu_mangled = "_Z3bari"; // "bar(int)" in GNU mangling - let (gnu_demangled_name, gnu_demangled_type) = - demangle_gnu3(&placeholder_arch, gnu_mangled, true).unwrap(); - assert_eq!(gnu_demangled_name, "bar".into()); + let gnu_demangled = demangle_gnu3(&placeholder_arch, gnu_mangled, true).unwrap(); + assert_eq!(gnu_demangled.name, "bar".into()); // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( - gnu_demangled_type.unwrap().to_string(), - "int32_t(int32_t)".to_string() + gnu_demangled.ty.unwrap().to_string(), + "int64_t(int32_t)".to_string() ); // Example MSVC-style mangled name let msvc_mangled = "?baz@@YAHH@Z"; // "int __cdecl baz(int)" in MSVC mangling - let (msvc_demangled_name, msvc_demangled_type) = - demangle_ms(&placeholder_arch, msvc_mangled, true).unwrap(); - assert_eq!(msvc_demangled_name, "baz".into()); + let msvc_demangled = demangle_ms(&placeholder_arch, msvc_mangled, true).unwrap(); + assert_eq!(msvc_demangled.name, "baz".into()); // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( - msvc_demangled_type.unwrap().to_string(), + msvc_demangled.ty.unwrap().to_string(), "int32_t __cdecl(int32_t)".to_string() ); } #[test] -fn test_custom_demangler() { - let _session = Session::new().expect("Failed to initialize session"); - struct TestDemangler; - - impl CustomDemangler for TestDemangler { - fn is_mangled_string(&self, name: &str) -> bool { - name == "test_name" || name == "test_name2" - } - - fn demangle( - &self, - _arch: &CoreArchitecture, - name: &str, - _view: Option>, - ) -> Option<(QualifiedName, Option>)> { - match name { - "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), - "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), - _ => None, - } - } - } +fn test_simplify_demangled_template_name() { + let _session = session(); - Demangler::register("Test", TestDemangler); + let simplified = simplify_demangled_template_name( + "std::basic_string, std::allocator >", + ); + assert_eq!(simplified, QualifiedName::from(vec!["std", "string"])); + assert_eq!(simplified.to_string(), "std::string"); +} +#[test] +fn test_custom_demangler() { + let _session = session(); let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86_64 exists"); + let platform = placeholder_arch + .standalone_platform() + .expect("x86_64 standalone platform exists"); + let config = DemanglerConfig::for_platform(&platform, true); - let demangled = demangle_generic(&placeholder_arch, "test_name", None, true).unwrap(); + let demangled = demangle_any("test_name", &config).unwrap(); assert_eq!( demangled, - (QualifiedName::from(vec!["test_name"]), Some(Type::bool())) + DemanglerResult::new( + QualifiedName::from(vec!["test_name_simplified"]), + Some(Type::bool()) + ) + ); + let unsimplified = demangle_generic(&placeholder_arch, "test_name", None, false).unwrap(); + assert_eq!( + unsimplified, + DemanglerResult::new(QualifiedName::from(vec!["test_name"]), Some(Type::bool())) ); let demangled2 = demangle_generic(&placeholder_arch, "test_name2", None, true).unwrap(); assert_eq!( demangled2, - (QualifiedName::from(vec!["test_name2", "aaa"]), None) + DemanglerResult::new(QualifiedName::from(vec!["test_name2", "aaa"]), None) ); } diff --git a/type.cpp b/type.cpp index ff4f8eb02d..f24c3b43f4 100644 --- a/type.cpp +++ b/type.cpp @@ -20,6 +20,7 @@ #include "binaryninjaapi.h" #include +#include using namespace BinaryNinja; using namespace std; @@ -349,21 +350,21 @@ QualifiedName QualifiedName::operator+(const QualifiedName& other) const BNQualifiedName QualifiedName::GetAPIObject() const { BNQualifiedName result; - result.nameCount = m_name.size(); result.join = BNAllocString(m_join.c_str()); - result.name = new char*[m_name.size()]; - for (size_t i = 0; i < m_name.size(); i++) - result.name[i] = BNAllocString(m_name[i].c_str()); + std::vector namePtrs; + namePtrs.reserve(m_name.size()); + for (const auto& name : m_name) + namePtrs.push_back(name.c_str()); + result.nameCount = namePtrs.size(); + result.name = BNAllocStringList(namePtrs.data(), namePtrs.size()); return result; } void QualifiedName::FreeAPIObject(BNQualifiedName* name) { - for (size_t i = 0; i < name->nameCount; i++) - BNFreeString(name->name[i]); + BNFreeStringList(name->name, name->nameCount); BNFreeString(name->join); - delete[] name->name; } diff --git a/ui/linearview.h b/ui/linearview.h index d0036aaf3e..4addf93e6c 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -233,6 +233,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub BinaryNinja::FunctionViewType m_ilViewType, m_prevILViewType = InvalidILViewType; HexEditorHighlightState m_highlightState; bool m_singleFunctionView = false; + bool m_simplifyTemplates = false; InstructionEdit* m_instrEdit = nullptr; @@ -311,7 +312,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub StructureRef defineInnerName(TypeRef type, uint64_t offset, uint64_t size, std::set& seen); StructureRef defineInnerIntegerSize(TypeRef type, uint64_t offset, uint64_t size, std::set& seen); StructureRef defineInnerSign(TypeRef type, uint64_t offset, uint64_t size, std::set& seen); - TypeRef getPointerTypeAndName(ArchitectureRef arch, uint64_t addr, std::string& name); + TypeRef getPointerTypeAndName(uint64_t addr, std::string& name); std::string getVariableName(uint64_t addr); BinaryNinja::Ref createLinearViewObject(); diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index bfb5c68b2c..58cb458963 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -808,6 +808,7 @@ bool ElfView::Init() if (!platform) platform = entryPointArch->GetStandalonePlatform(); + m_plat = platform; SetDefaultPlatform(platform); GetParentView()->SetDefaultPlatform(platform); @@ -2617,19 +2618,17 @@ void ElfView::DefineElfSymbol(BNSymbolType type, const string& incomingName, uin string shortName = rawName; string fullName = rawName; Confidence> typeRef = symbolTypeRef; - if (m_arch) + + DemanglerConfig config {m_plat, this, m_simplifyTemplates}; + if (auto demangled = Demangler::DemangleAny(rawName, config)) { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, m_simplifyTemplates)) - { - shortName = demangledName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !m_plat->GetFunctionByName(rawName)) + typeRef = demangledType; } if (!typeRef && m_arch && (m_arch->GetName() == "hexagon" || m_arch->GetName() == "tms320c6x")) diff --git a/view/kernelcache/api/kernelcache.cpp b/view/kernelcache/api/kernelcache.cpp index 30f4065695..8fc3a3e0f6 100644 --- a/view/kernelcache/api/kernelcache.cpp +++ b/view/kernelcache/api/kernelcache.cpp @@ -34,22 +34,6 @@ CacheSymbol SymbolFromApi(BNKernelCacheSymbol apiSymbol) return symbol; } -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType = nullptr; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return {shortName, outType}; -} - -Ref CacheSymbol::GetBNSymbol(BinaryView &view) const -{ - auto [shortName, _] = DemangledName(view); - return new Symbol(type, shortName, shortName, name, address, nullptr); -} - std::string KernelCacheAPI::GetSymbolTypeAsString(const BNSymbolType &type) { // NOTE: We currently only use the function and data symbol for cache symbols. diff --git a/view/kernelcache/api/kernelcacheapi.h b/view/kernelcache/api/kernelcacheapi.h index aceb30a5d8..ed43b8d316 100644 --- a/view/kernelcache/api/kernelcacheapi.h +++ b/view/kernelcache/api/kernelcacheapi.h @@ -275,9 +275,6 @@ namespace KernelCacheAPI { BNSymbolType type; uint64_t address; std::string name; - - std::pair> DemangledName(BinaryNinja::BinaryView &view) const; - BinaryNinja::Ref GetBNSymbol(BinaryNinja::BinaryView& view) const; }; std::string GetSymbolTypeAsString(const BNSymbolType& type); diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp index 6ff88ad1fa..0d7513e5ef 100644 --- a/view/kernelcache/core/KernelCache.cpp +++ b/view/kernelcache/core/KernelCache.cpp @@ -5,19 +5,16 @@ using namespace BinaryNinja; -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return { shortName, outType }; -} - std::pair, Ref> CacheSymbol::GetBNSymbolAndType(BinaryView& view) const { - auto [shortName, demangledType] = DemangledName(view); + Ref demangledType; + std::string shortName = name; + DemanglerConfig config {view.GetDefaultPlatform(), &view, true}; + if (auto demangled = Demangler::DemangleAny(name, config)) + { + shortName = demangled->name.GetString(); + demangledType = demangled->type; + } auto symbol = new Symbol(type, shortName, shortName, name, address, nullptr); return {symbol, demangledType}; } diff --git a/view/kernelcache/core/KernelCache.h b/view/kernelcache/core/KernelCache.h index 3897422597..38e2da558f 100644 --- a/view/kernelcache/core/KernelCache.h +++ b/view/kernelcache/core/KernelCache.h @@ -27,8 +27,6 @@ struct CacheSymbol CacheSymbol(CacheSymbol&& other) noexcept = default; CacheSymbol& operator=(CacheSymbol&& other) noexcept = default; - std::pair> DemangledName(BinaryNinja::BinaryView& view) const; - // NOTE: you should really only call this when adding the symbol to the view. std::pair, BinaryNinja::Ref> GetBNSymbolAndType(BinaryNinja::BinaryView& view) const; }; diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index 7613918806..d47ff60298 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -2061,6 +2061,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ if (header.m_entryPoints.size() > 0 && !platformSetByUser) platform = platform->GetAssociatedPlatformByAddress(header.m_entryPoints[0]); + m_plat = platform; SetDefaultPlatform(platform); SetDefaultArchitecture(platform->GetArchitecture()); @@ -2721,19 +2722,17 @@ Ref MachoView::DefineMachoSymbol( string fullName = rawName; Ref typeRef = symbolTypeRef; - if (m_arch) - { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, nullptr, m_simplifyTemplates)) - { - shortName = demangledName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } + DemanglerConfig config {m_plat, this, m_simplifyTemplates}; + auto demangled = Demangler::DemangleAny(rawName, config); + if (demangled) + { + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !m_plat->GetFunctionByName(rawName)) + typeRef = demangledType; } if ((type == ExternalSymbol || type == ImportAddressSymbol) diff --git a/view/pe/coffview.cpp b/view/pe/coffview.cpp index a36aff64a3..3c6c34a997 100644 --- a/view/pe/coffview.cpp +++ b/view/pe/coffview.cpp @@ -1529,11 +1529,11 @@ void COFFView::AddCOFFSymbol(BNSymbolType type, const string& dll, const string& if (m_arch && name.size() > 0) { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, m_simplifyTemplates)) + DemanglerConfig config {GetDefaultPlatform(), this, m_simplifyTemplates}; + if (auto demangled = Demangler::DemangleAny(rawName, config)) { - shortName = demangledName.GetString(); + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); fullName = shortName; if (demangledType) fullName += demangledType->GetStringAfterName(); diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp index 6af6283eaf..dd245699cb 100644 --- a/view/pe/peview.cpp +++ b/view/pe/peview.cpp @@ -3565,11 +3565,11 @@ void PEView::AddPESymbol(BNSymbolType type, const string& dll, const string& nam if (m_arch && name.size() > 0) { - QualifiedName demangledName; - Ref demangledType; - if (DemangleGeneric(m_arch, rawName, demangledType, demangledName, this, m_simplifyTemplates)) + DemanglerConfig config {GetDefaultPlatform(), this, m_simplifyTemplates}; + if (auto demangled = Demangler::DemangleAny(rawName, config)) { - shortName = demangledName.GetString(); + auto demangledType = demangled->type; + shortName = demangled->name.GetString(); fullName = shortName; if (demangledType) fullName += demangledType->GetStringAfterName(); diff --git a/view/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp index ba4d85aa69..423b8a776f 100644 --- a/view/sharedcache/api/sharedcache.cpp +++ b/view/sharedcache/api/sharedcache.cpp @@ -143,22 +143,6 @@ std::string SharedCacheAPI::GetRegionTypeAsString(const BNSharedCacheRegionType } } -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType = nullptr; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return {shortName, outType}; -} - -Ref CacheSymbol::GetBNSymbol(BinaryView &view) const -{ - auto [shortName, _] = DemangledName(view); - return new Symbol(type, shortName, shortName, name, address, binding); -} - std::string SharedCacheAPI::GetSymbolTypeAsString(const BNSymbolType &type) { // NOTE: We currently only use the function and data symbol for cache symbols. diff --git a/view/sharedcache/api/sharedcacheapi.h b/view/sharedcache/api/sharedcacheapi.h index dc80b98832..e77ea87a6f 100644 --- a/view/sharedcache/api/sharedcacheapi.h +++ b/view/sharedcache/api/sharedcacheapi.h @@ -291,9 +291,6 @@ namespace SharedCacheAPI { BNSymbolBinding binding = NoBinding; uint64_t address; std::string name; - - std::pair> DemangledName(BinaryNinja::BinaryView &view) const; - BinaryNinja::Ref GetBNSymbol(BinaryNinja::BinaryView& view) const; }; std::string GetSymbolTypeAsString(const BNSymbolType& type); diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index f9761968b9..df85b980ab 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -8,19 +8,16 @@ using namespace BinaryNinja; -std::pair> CacheSymbol::DemangledName(BinaryView &view) const -{ - QualifiedName qname; - Ref outType; - std::string shortName = name; - if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) - shortName = qname.GetString(); - return { shortName, outType }; -} - std::pair, Ref> CacheSymbol::GetBNSymbolAndType(BinaryView& view) const { - auto [shortName, demangledType] = DemangledName(view); + Ref demangledType; + std::string shortName = name; + DemanglerConfig config {view.GetDefaultPlatform(), &view, true}; + if (auto demangled = Demangler::DemangleAny(name, config)) + { + shortName = demangled->name.GetString(); + demangledType = demangled->type; + } auto symbol = new Symbol(type, shortName, shortName, name, address, binding); return {symbol, demangledType}; } diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 8ecd8f1de7..88ef8f57d4 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -27,8 +27,6 @@ struct CacheSymbol CacheSymbol(CacheSymbol&& other) noexcept = default; CacheSymbol& operator=(CacheSymbol&& other) noexcept = default; - std::pair> DemangledName(BinaryNinja::BinaryView& view) const; - // NOTE: you should really only call this when adding the symbol to the view. std::pair, BinaryNinja::Ref> GetBNSymbolAndType(BinaryNinja::BinaryView& view) const; }; diff --git a/view/sharedcache/workflow/SharedCacheWorkflow.cpp b/view/sharedcache/workflow/SharedCacheWorkflow.cpp index ff42e0c5bd..7d06640264 100644 --- a/view/sharedcache/workflow/SharedCacheWorkflow.cpp +++ b/view/sharedcache/workflow/SharedCacheWorkflow.cpp @@ -85,8 +85,10 @@ void IdentifyStub(BinaryView& view, const SharedCacheController& controller, uin if (!symbol.has_value()) return; - // TODO: The demangled type here is almost always wrong so we omit it for now. - auto [demangledName, demangledType] = symbol->DemangledName(view); + std::string demangledName = symbol->name; + DemanglerConfig config {view.GetDefaultPlatform(), &view, true}; + if (auto demangled = Demangler::DemangleAny(symbol->name, config)) + demangledName = demangled->name.GetString(); auto rawName = STUB_PREFIX + symbol->name; auto shortName = STUB_PREFIX + demangledName; @@ -95,8 +97,6 @@ void IdentifyStub(BinaryView& view, const SharedCacheController& controller, uin { // NOTE: The type library name is expected to be the image name currently. // Try and pull the type from the associated type library (if there is one) - // TODO: The demangled type here is missing a param - // Ref selectedType = demangledType; Ref selectedType = nullptr; if (const auto image = controller.GetImageContaining(symbolAddr)) { @@ -270,7 +270,12 @@ void AnalyzeStandardFunction(Ref func, Ref mlil const auto symbol = controller.GetSymbolAt(symbolAddr); if (!symbol.has_value()) return false; - view->DefineAutoSymbol(symbol->GetBNSymbol(*view)); + std::string shortName = symbol->name; + DemanglerConfig config {view->GetDefaultPlatform(), view, true}; + if (auto demangled = Demangler::DemangleAny(symbol->name, config)) + shortName = demangled->name.GetString(); + view->DefineAutoSymbol( + new Symbol(symbol->type, shortName, shortName, symbol->name, symbol->address, symbol->binding)); return true; };