diff --git a/include/llvm/Demangle/Demangle.h b/include/llvm/Demangle/Demangle.h index fde77d224f7..df7753f23b8 100644 --- a/include/llvm/Demangle/Demangle.h +++ b/include/llvm/Demangle/Demangle.h @@ -16,7 +16,7 @@ namespace llvm { /// The mangled_name is demangled into buf and returned. If the buffer is not /// large enough, realloc is used to expand it. /// -/// The *status will be set to a value from the enumeration +/// The *status will be set to a value from the following enumeration enum : int { demangle_unknown_error = -4, demangle_invalid_args = -3, @@ -27,6 +27,8 @@ enum : int { char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status); +char *microsoftDemangle(const char *mangled_name, char *buf, size_t *n, + int *status); /// "Partial" demangler. This supports demangling a string into an AST /// (typically an intermediate stage in itaniumDemangle) and querying certain diff --git a/lib/Demangle/CMakeLists.txt b/lib/Demangle/CMakeLists.txt index 825df88dddc..bef1aa02155 100644 --- a/lib/Demangle/CMakeLists.txt +++ b/lib/Demangle/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMDemangle ItaniumDemangle.cpp - + MicrosoftDemangle.cpp + ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/Demangle" ) diff --git a/lib/Demangle/MicrosoftDemangle.cpp b/lib/Demangle/MicrosoftDemangle.cpp new file mode 100644 index 00000000000..eb27e612ae7 --- /dev/null +++ b/lib/Demangle/MicrosoftDemangle.cpp @@ -0,0 +1,1533 @@ +//===- MicrosoftDemangle.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a demangler for MSVC-style mangled symbols. +// +// This file has no dependencies on the rest of LLVM so that it can be +// easily reused in other programs such as libcxxabi. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/Demangle.h" + +#include "Compiler.h" +#include "StringView.h" +#include "Utility.h" + +#include + +// This memory allocator is extremely fast, but it doesn't call dtors +// for allocated objects. That means you can't use STL containers +// (such as std::vector) with this allocator. But it pays off -- +// the demangler is 3x faster with this allocator compared to one with +// STL containers. +namespace { +class ArenaAllocator { + struct AllocatorNode { + uint8_t *Buf = nullptr; + size_t Used = 0; + AllocatorNode *Next = nullptr; + }; + +public: + ArenaAllocator() : Head(new AllocatorNode) { Head->Buf = new uint8_t[Unit]; } + + ~ArenaAllocator() { + while (Head) { + assert(Head->Buf); + delete[] Head->Buf; + Head = Head->Next; + } + } + + void *alloc(size_t Size) { + assert(Size < Unit); + assert(Head && Head->Buf); + + uint8_t *P = Head->Buf + Head->Used; + Head->Used += Size; + if (Head->Used < Unit) + return P; + + AllocatorNode *NewHead = new AllocatorNode; + NewHead->Buf = new uint8_t[ArenaAllocator::Unit]; + NewHead->Next = Head; + Head = NewHead; + NewHead->Used = Size; + return NewHead->Buf; + } + +private: + static constexpr size_t Unit = 4096; + + AllocatorNode *Head = nullptr; +}; +} // namespace + +static bool startsWithDigit(StringView S) { + return !S.empty() && std::isdigit(S.front()); +} + +// Writes a space if the last token does not end with a punctuation. +static void outputSpaceIfNecessary(OutputStream &OS) { + if (OS.empty()) + return; + + char C = OS.back(); + if (isalnum(C) || C == '>') + OS << " "; +} + +void *operator new(size_t Size, ArenaAllocator &A) { return A.alloc(Size); } + +// Storage classes +enum Qualifiers : uint8_t { + Q_None = 0, + Q_Const = 1 << 0, + Q_Volatile = 1 << 1, + Q_Far = 1 << 2, + Q_Huge = 1 << 3, + Q_Unaligned = 1 << 4, + Q_Restrict = 1 << 5, + Q_Pointer64 = 1 << 6 +}; + +enum class StorageClass : uint8_t { + None, + PrivateStatic, + ProtectedStatic, + PublicStatic, + Global, + FunctionLocalStatic +}; + +enum class QualifierMangleMode { Drop, Mangle, Result }; +enum class QualifierMangleLocation { Member, NonMember, Detect }; + +// Calling conventions +enum class CallingConv : uint8_t { + None, + Cdecl, + Pascal, + Thiscall, + Stdcall, + Fastcall, + Clrcall, + Eabi, + Vectorcall, + Regcall, +}; + +enum class ReferenceKind : uint8_t { None, LValueRef, RValueRef }; + +// Types +enum class PrimTy : uint8_t { + Unknown, + None, + Function, + Ptr, + Ref, + Array, + + Struct, + Union, + Class, + Enum, + + Void, + Bool, + Char, + Schar, + Uchar, + Short, + Ushort, + Int, + Uint, + Long, + Ulong, + Int64, + Uint64, + Wchar, + Float, + Double, + Ldouble, +}; + +// Function classes +enum FuncClass : uint8_t { + Public = 1 << 0, + Protected = 1 << 1, + Private = 1 << 2, + Global = 1 << 3, + Static = 1 << 4, + Virtual = 1 << 5, + FFar = 1 << 6, +}; + +namespace { + +struct Type; + +// Represents a list of parameters (template params or function arguments. +// It's represented as a linked list. +struct ParamList { + Type *Current = nullptr; + + ParamList *Next = nullptr; +}; + +// The type class. Mangled symbols are first parsed and converted to +// this type and then converted to string. +struct Type { + virtual ~Type() {} + + virtual Type *clone(ArenaAllocator &Arena) const; + + // Write the "first half" of a given type. This is a static functions to + // give the code a chance to do processing that is common to a subset of + // subclasses + static void outputPre(OutputStream &OS, Type &Ty); + + // Write the "second half" of a given type. This is a static functions to + // give the code a chance to do processing that is common to a subset of + // subclasses + static void outputPost(OutputStream &OS, Type &Ty); + + virtual void outputPre(OutputStream &OS); + virtual void outputPost(OutputStream &OS); + + // Primitive type such as Int. + PrimTy Prim = PrimTy::Unknown; + + Qualifiers Quals = Q_None; + StorageClass Storage = StorageClass::None; // storage class +}; + +// Represents an identifier which may be a template. +struct Name { + // Name read from an MangledName string. + StringView Str; + + // Overloaded operators are represented as special BackReferences in mangled + // symbols. If this is an operator name, "op" has an operator name (e.g. + // ">>"). Otherwise, empty. + StringView Operator; + + // Template parameters. Null if not a template. + ParamList TemplateParams; + + // Nested BackReferences (e.g. "A::B::C") are represented as a linked list. + Name *Next = nullptr; +}; + +struct PointerType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS); + void outputPost(OutputStream &OS) override; + + bool isMemberPointer() const { return false; } + + // Represents a type X in "a pointer to X", "a reference to X", + // "an array of X", or "a function returning X". + Type *Pointee = nullptr; +}; + +struct FunctionType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS); + void outputPost(OutputStream &OS); + + Type *ReturnType = nullptr; + // If this is a reference, the type of reference. + ReferenceKind RefKind; + + CallingConv CallConvention; + FuncClass FunctionClass; + + ParamList Params; +}; + +struct UdtType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + + Name *UdtName = nullptr; +}; + +struct ArrayType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + void outputPost(OutputStream &OS) override; + + // Either NextDimension or ElementType will be valid. + ArrayType *NextDimension = nullptr; + uint32_t ArrayDimension = 0; + + Type *ElementType = nullptr; +}; + +} // namespace + +static void outputCallingConvention(OutputStream &OS, CallingConv CC) { + outputSpaceIfNecessary(OS); + + switch (CC) { + case CallingConv::Cdecl: + OS << "__cdecl"; + break; + case CallingConv::Fastcall: + OS << "__fastcall"; + break; + case CallingConv::Pascal: + OS << "__pascal"; + break; + case CallingConv::Regcall: + OS << "__regcall"; + break; + case CallingConv::Stdcall: + OS << "__stdcall"; + break; + case CallingConv::Thiscall: + OS << "__thiscall"; + break; + case CallingConv::Eabi: + OS << "__eabi"; + break; + case CallingConv::Vectorcall: + OS << "__vectorcall"; + break; + case CallingConv::Clrcall: + OS << "__clrcall"; + break; + default: + break; + } +} + +// Write a function or template parameter list. +static void outputParameterList(OutputStream &OS, const ParamList &Params) { + const ParamList *Head = &Params; + while (Head) { + Type::outputPre(OS, *Head->Current); + Type::outputPost(OS, *Head->Current); + + Head = Head->Next; + + if (Head) + OS << ", "; + } +} + +static void outputTemplateParams(OutputStream &OS, const Name &TheName) { + if (!TheName.TemplateParams.Current) + return; + + OS << "<"; + outputParameterList(OS, TheName.TemplateParams); + OS << ">"; +} + +static void outputName(OutputStream &OS, const Name *TheName) { + if (!TheName) + return; + + outputSpaceIfNecessary(OS); + + // Print out namespaces or outer class BackReferences. + for (; TheName->Next; TheName = TheName->Next) { + OS << TheName->Str; + outputTemplateParams(OS, *TheName); + OS << "::"; + } + + // Print out a regular name. + if (TheName->Operator.empty()) { + OS << TheName->Str; + outputTemplateParams(OS, *TheName); + return; + } + + // Print out ctor or dtor. + if (TheName->Operator == "ctor" || TheName->Operator == "dtor") { + OS << TheName->Str; + outputTemplateParams(OS, *TheName); + OS << "::"; + if (TheName->Operator == "dtor") + OS << "~"; + OS << TheName->Str; + outputTemplateParams(OS, *TheName); + return; + } + + // Print out an overloaded operator. + if (!TheName->Str.empty()) + OS << TheName->Str << "::"; + OS << "operator" << TheName->Operator; +} + +namespace { + +Type *Type::clone(ArenaAllocator &Arena) const { + return new (Arena) Type(*this); +} + +// Write the "first half" of a given type. +void Type::outputPre(OutputStream &OS, Type &Ty) { + // Function types require custom handling of const and static so we + // handle them separately. All other types use the same decoration + // for these modifiers, so handle them here in common code. + if (Ty.Prim == PrimTy::Function) { + Ty.outputPre(OS); + return; + } + + switch (Ty.Storage) { + case StorageClass::PrivateStatic: + case StorageClass::PublicStatic: + case StorageClass::ProtectedStatic: + OS << "static "; + default: + break; + } + Ty.outputPre(OS); + + if (Ty.Quals & Q_Const) { + outputSpaceIfNecessary(OS); + OS << "const"; + } + + if (Ty.Quals & Q_Volatile) { + outputSpaceIfNecessary(OS); + OS << "volatile"; + } +} + +// Write the "second half" of a given type. +void Type::outputPost(OutputStream &OS, Type &Ty) { Ty.outputPost(OS); } + +void Type::outputPre(OutputStream &OS) { + switch (Prim) { + case PrimTy::Void: + OS << "void"; + break; + case PrimTy::Bool: + OS << "bool"; + break; + case PrimTy::Char: + OS << "char"; + break; + case PrimTy::Schar: + OS << "signed char"; + break; + case PrimTy::Uchar: + OS << "unsigned char"; + break; + case PrimTy::Short: + OS << "short"; + break; + case PrimTy::Ushort: + OS << "unsigned short"; + break; + case PrimTy::Int: + OS << "int"; + break; + case PrimTy::Uint: + OS << "unsigned int"; + break; + case PrimTy::Long: + OS << "long"; + break; + case PrimTy::Ulong: + OS << "unsigned long"; + break; + case PrimTy::Int64: + OS << "__int64"; + break; + case PrimTy::Uint64: + OS << "unsigned __int64"; + break; + case PrimTy::Wchar: + OS << "wchar_t"; + break; + case PrimTy::Float: + OS << "float"; + break; + case PrimTy::Double: + OS << "double"; + break; + case PrimTy::Ldouble: + OS << "long double"; + break; + default: + assert(false && "Invalid primitive type!"); + } +} +void Type::outputPost(OutputStream &OS) {} + +Type *PointerType::clone(ArenaAllocator &Arena) const { + return new (Arena) PointerType(*this); +} + +void PointerType::outputPre(OutputStream &OS) { + Type::outputPre(OS, *Pointee); + + outputSpaceIfNecessary(OS); + + if (Quals & Q_Unaligned) + OS << "__unaligned "; + + // "[]" and "()" (for function parameters) take precedence over "*", + // so "int *x(int)" means "x is a function returning int *". We need + // parentheses to supercede the default precedence. (e.g. we want to + // emit something like "int (*x)(int)".) + if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array) + OS << "("; + + if (Prim == PrimTy::Ptr) + OS << "*"; + else + OS << "&"; + + // if (Ty.Quals & Q_Pointer64) + // OS << " __ptr64"; + if (Quals & Q_Restrict) + OS << " __restrict"; +} + +void PointerType::outputPost(OutputStream &OS) { + if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array) + OS << ")"; + + Type::outputPost(OS, *Pointee); +} + +Type *FunctionType::clone(ArenaAllocator &Arena) const { + return new (Arena) FunctionType(*this); +} + +void FunctionType::outputPre(OutputStream &OS) { + if (!(FunctionClass & Global)) { + if (FunctionClass & Static) + OS << "static "; + } + + if (ReturnType) + Type::outputPre(OS, *ReturnType); + + outputCallingConvention(OS, CallConvention); +} + +void FunctionType::outputPost(OutputStream &OS) { + OS << "("; + outputParameterList(OS, Params); + OS << ")"; + if (Quals & Q_Const) + OS << " const"; + if (Quals & Q_Volatile) + OS << " volatile"; + return; +} + +Type *UdtType::clone(ArenaAllocator &Arena) const { + return new (Arena) UdtType(*this); +} + +void UdtType::outputPre(OutputStream &OS) { + switch (Prim) { + case PrimTy::Class: + OS << "class "; + break; + case PrimTy::Struct: + OS << "struct "; + break; + case PrimTy::Union: + OS << "union "; + break; + case PrimTy::Enum: + OS << "enum "; + break; + default: + assert(false && "Not a udt type!"); + } + + outputName(OS, UdtName); +} + +Type *ArrayType::clone(ArenaAllocator &Arena) const { + return new (Arena) ArrayType(*this); +} + +void ArrayType::outputPre(OutputStream &OS) { + Type::outputPre(OS, *ElementType); +} + +void ArrayType::outputPost(OutputStream &OS) { + if (ArrayDimension > 0) + OS << "[" << ArrayDimension << "]"; + if (NextDimension) + Type::outputPost(OS, *NextDimension); + else if (ElementType) + Type::outputPost(OS, *ElementType); +} + +} // namespace + +namespace { + +// Demangler class takes the main role in demangling symbols. +// It has a set of functions to parse mangled symbols into Type instances. +// It also has a set of functions to cnovert Type instances to strings. +class Demangler { +public: + Demangler(OutputStream &OS, StringView s) : OS(OS), MangledName(s) {} + + // You are supposed to call parse() first and then check if error is true. If + // it is false, call output() to write the formatted name to the given stream. + void parse(); + void output(); + + // True if an error occurred. + bool Error = false; + +private: + Type *demangleVariableEncoding(); + Type *demangleFunctionEncoding(); + + Qualifiers demanglePointerExtQualifiers(); + + // Parser functions. This is a recursive-descent parser. + Type *demangleType(QualifierMangleMode QMM); + Type *demangleBasicType(); + UdtType *demangleClassType(); + PointerType *demanglePointerType(); + + ArrayType *demangleArrayType(); + + ParamList demangleParameterList(); + + int demangleNumber(); + void demangleNamePiece(Name &Node, bool IsHead); + + StringView demangleString(bool memorize); + void memorizeString(StringView s); + Name *demangleName(); + void demangleOperator(Name *); + StringView demangleOperatorName(); + int demangleFunctionClass(); + CallingConv demangleCallingConvention(); + StorageClass demangleVariableStorageClass(); + ReferenceKind demangleReferenceKind(); + + Qualifiers demangleFunctionQualifiers(); + Qualifiers demangleVariablQ_ifiers(); + Qualifiers demangleReturnTypQ_ifiers(); + + Qualifiers demangleQualifiers( + QualifierMangleLocation Location = QualifierMangleLocation::Detect); + + // Mangled symbol. demangle* functions shorten this string + // as they parse it. + StringView MangledName; + + // A parsed mangled symbol. + Type *SymbolType; + + // The main symbol name. (e.g. "ns::foo" in "int ns::foo()".) + Name *SymbolName = nullptr; + + // Memory allocator. + ArenaAllocator Arena; + + // The first 10 BackReferences in a mangled name can be back-referenced by + // special name @[0-9]. This is a storage for the first 10 BackReferences. + StringView BackReferences[10]; + size_t BackRefCount = 0; + + // The result is written to this stream. + OutputStream OS; +}; +} // namespace + +// Parser entry point. +void Demangler::parse() { + // MSVC-style mangled symbols must start with '?'. + if (!MangledName.consumeFront("?")) { + SymbolName = new (Arena) Name; + SymbolName->Str = MangledName; + SymbolType = new (Arena) Type; + SymbolType->Prim = PrimTy::Unknown; + } + + // What follows is a main symbol name. This may include + // namespaces or class BackReferences. + SymbolName = demangleName(); + + // Read a variable. + if (startsWithDigit(MangledName)) { + SymbolType = demangleVariableEncoding(); + return; + } + + // Read a function. + SymbolType = demangleFunctionEncoding(); +} + +// ::= +// ::= 0 # private static member +// ::= 1 # protected static member +// ::= 2 # public static member +// ::= 3 # global +// ::= 4 # static local + +Type *Demangler::demangleVariableEncoding() { + StorageClass SC = demangleVariableStorageClass(); + + Type *Ty = demangleType(QualifierMangleMode::Drop); + + Ty->Storage = SC; + + // ::= + // ::= # pointers, references + switch (Ty->Prim) { + case PrimTy::Ptr: + case PrimTy::Ref: { + Qualifiers ExtraChildQuals = Q_None; + Ty->Quals = Qualifiers(Ty->Quals | demanglePointerExtQualifiers()); + + PointerType *PTy = static_cast(Ty); + QualifierMangleLocation Location = PTy->isMemberPointer() + ? QualifierMangleLocation::Member + : QualifierMangleLocation::NonMember; + + ExtraChildQuals = demangleQualifiers(Location); + + if (PTy->isMemberPointer()) { + Name *BackRefName = demangleName(); + (void)BackRefName; + } + + PTy->Pointee->Quals = Qualifiers(PTy->Pointee->Quals | ExtraChildQuals); + break; + } + default: + Ty->Quals = demangleQualifiers(); + break; + } + + return Ty; +} + +// Sometimes numbers are encoded in mangled symbols. For example, +// "int (*x)[20]" is a valid C type (x is a pointer to an array of +// length 20), so we need some way to embed numbers as part of symbols. +// This function parses it. +// +// ::= [?] +// +// ::= # when 1 <= Number <= 10 +// ::= + @ # when Numbrer == 0 or >= 10 +// +// ::= [A-P] # A = 0, B = 1, ... +int Demangler::demangleNumber() { + bool neg = MangledName.consumeFront("?"); + + if (startsWithDigit(MangledName)) { + int32_t Ret = MangledName[0] - '0' + 1; + MangledName = MangledName.dropFront(1); + return neg ? -Ret : Ret; + } + + int Ret = 0; + for (size_t i = 0; i < MangledName.size(); ++i) { + char C = MangledName[i]; + if (C == '@') { + MangledName = MangledName.dropFront(i + 1); + return neg ? -Ret : Ret; + } + if ('A' <= C && C <= 'P') { + Ret = (Ret << 4) + (C - 'A'); + continue; + } + break; + } + + Error = true; + return 0; +} + +// Read until the next '@'. +StringView Demangler::demangleString(bool Memorize) { + for (size_t i = 0; i < MangledName.size(); ++i) { + if (MangledName[i] != '@') + continue; + StringView ret = MangledName.substr(0, i); + MangledName = MangledName.dropFront(i + 1); + + if (Memorize) + memorizeString(ret); + return ret; + } + + Error = true; + return ""; +} + +// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9. +// Memorize it. +void Demangler::memorizeString(StringView S) { + if (BackRefCount >= sizeof(BackReferences) / sizeof(*BackReferences)) + return; + for (size_t i = 0; i < BackRefCount; ++i) + if (S == BackReferences[i]) + return; + BackReferences[BackRefCount++] = S; +} + +void Demangler::demangleNamePiece(Name &Node, bool IsHead) { + if (startsWithDigit(MangledName)) { + size_t I = MangledName[0] - '0'; + if (I >= BackRefCount) { + Error = true; + return; + } + MangledName = MangledName.dropFront(); + Node.Str = BackReferences[I]; + } else if (MangledName.consumeFront("?$")) { + // Class template. + Node.Str = demangleString(false); + Node.TemplateParams = demangleParameterList(); + if (!MangledName.consumeFront('@')) { + Error = true; + return; + } + } else if (!IsHead && MangledName.consumeFront("?A")) { + // Anonymous namespace starts with ?A. So does overloaded operator[], + // but the distinguishing factor is that namespace themselves are not + // mangled, only the variables and functions inside of them are. So + // an anonymous namespace will never occur as the first item in the + // name. + Node.Str = "`anonymous namespace'"; + if (!MangledName.consumeFront('@')) { + Error = true; + return; + } + } else if (MangledName.consumeFront("?")) { + // Overloaded operator. + demangleOperator(&Node); + } else { + // Non-template functions or classes. + Node.Str = demangleString(true); + } +} + +// Parses a name in the form of A@B@C@@ which represents C::B::A. +Name *Demangler::demangleName() { + Name *Head = nullptr; + + while (!MangledName.consumeFront("@")) { + Name *Elem = new (Arena) Name; + + assert(!Error); + demangleNamePiece(*Elem, Head == nullptr); + if (Error) + return nullptr; + + Elem->Next = Head; + Head = Elem; + } + + return Head; +} + +void Demangler::demangleOperator(Name *OpName) { + OpName->Operator = demangleOperatorName(); + if (!Error && !MangledName.empty() && MangledName.front() != '@') + demangleNamePiece(*OpName, false); +} + +StringView Demangler::demangleOperatorName() { + SwapAndRestore RestoreOnError(MangledName, MangledName); + RestoreOnError.shouldRestore(false); + + switch (MangledName.popFront()) { + case '0': + return "ctor"; + case '1': + return "dtor"; + case '2': + return " new"; + case '3': + return " delete"; + case '4': + return "="; + case '5': + return ">>"; + case '6': + return "<<"; + case '7': + return "!"; + case '8': + return "=="; + case '9': + return "!="; + case 'A': + return "[]"; + case 'C': + return "->"; + case 'D': + return "*"; + case 'E': + return "++"; + case 'F': + return "--"; + case 'G': + return "-"; + case 'H': + return "+"; + case 'I': + return "&"; + case 'J': + return "->*"; + case 'K': + return "/"; + case 'L': + return "%"; + case 'M': + return "<"; + case 'N': + return "<="; + case 'O': + return ">"; + case 'P': + return ">="; + case 'Q': + return ","; + case 'R': + return "()"; + case 'S': + return "~"; + case 'T': + return "^"; + case 'U': + return "|"; + case 'V': + return "&&"; + case 'W': + return "||"; + case 'X': + return "*="; + case 'Y': + return "+="; + case 'Z': + return "-="; + case '_': { + if (MangledName.empty()) + break; + + switch (MangledName.popFront()) { + case '0': + return "/="; + case '1': + return "%="; + case '2': + return ">>="; + case '3': + return "<<="; + case '4': + return "&="; + case '5': + return "|="; + case '6': + return "^="; + case 'U': + return " new[]"; + case 'V': + return " delete[]"; + case '_': + if (MangledName.consumeFront("L")) + return " co_await"; + } + } + } + + Error = true; + RestoreOnError.shouldRestore(true); + return ""; +} + +int Demangler::demangleFunctionClass() { + SwapAndRestore RestoreOnError(MangledName, MangledName); + RestoreOnError.shouldRestore(false); + + switch (MangledName.popFront()) { + case 'A': + return Private; + case 'B': + return Private | FFar; + case 'C': + return Private | Static; + case 'D': + return Private | Static; + case 'E': + return Private | Virtual; + case 'F': + return Private | Virtual; + case 'I': + return Protected; + case 'J': + return Protected | FFar; + case 'K': + return Protected | Static; + case 'L': + return Protected | Static | FFar; + case 'M': + return Protected | Virtual; + case 'N': + return Protected | Virtual | FFar; + case 'Q': + return Public; + case 'R': + return Public | FFar; + case 'S': + return Public | Static; + case 'T': + return Public | Static | FFar; + case 'U': + return Public | Virtual; + case 'V': + return Public | Virtual | FFar; + case 'Y': + return Global; + case 'Z': + return Global | FFar; + } + + Error = true; + RestoreOnError.shouldRestore(true); + return 0; +} + +Qualifiers Demangler::demangleFunctionQualifiers() { + SwapAndRestore RestoreOnError(MangledName, MangledName); + RestoreOnError.shouldRestore(false); + + switch (MangledName.popFront()) { + case 'A': + return Q_None; + case 'B': + return Q_Const; + case 'C': + return Q_Volatile; + case 'D': + return Qualifiers(Q_Const | Q_Volatile); + } + + Error = true; + RestoreOnError.shouldRestore(true); + return Q_None; +} + +CallingConv Demangler::demangleCallingConvention() { + switch (MangledName.popFront()) { + case 'A': + case 'B': + return CallingConv::Cdecl; + case 'C': + case 'D': + return CallingConv::Pascal; + case 'E': + case 'F': + return CallingConv::Thiscall; + case 'G': + case 'H': + return CallingConv::Stdcall; + case 'I': + case 'J': + return CallingConv::Fastcall; + case 'M': + case 'N': + return CallingConv::Clrcall; + case 'O': + case 'P': + return CallingConv::Eabi; + case 'Q': + return CallingConv::Vectorcall; + } + + return CallingConv::None; +}; + +StorageClass Demangler::demangleVariableStorageClass() { + assert(std::isdigit(MangledName.front())); + + switch (MangledName.popFront()) { + case '0': + return StorageClass::PrivateStatic; + case '1': + return StorageClass::ProtectedStatic; + case '2': + return StorageClass::PublicStatic; + case '3': + return StorageClass::Global; + case '4': + return StorageClass::FunctionLocalStatic; + } + Error = true; + return StorageClass::None; +} + +Qualifiers Demangler::demangleVariablQ_ifiers() { + SwapAndRestore RestoreOnError(MangledName, MangledName); + RestoreOnError.shouldRestore(false); + + switch (MangledName.popFront()) { + case 'A': + return Q_None; + case 'B': + return Q_Const; + case 'C': + return Q_Volatile; + case 'D': + return Qualifiers(Q_Const | Q_Volatile); + case 'E': + return Q_Far; + case 'F': + return Qualifiers(Q_Const | Q_Far); + case 'G': + return Qualifiers(Q_Volatile | Q_Far); + case 'H': + return Qualifiers(Q_Const | Q_Volatile | Q_Far); + } + + Error = true; + RestoreOnError.shouldRestore(true); + return Q_None; +} + +Qualifiers Demangler::demangleReturnTypQ_ifiers() { + if (!MangledName.consumeFront("?")) + return Q_None; + + SwapAndRestore RestoreOnError(MangledName, MangledName); + RestoreOnError.shouldRestore(false); + + switch (MangledName.popFront()) { + case 'A': + return Q_None; + case 'B': + return Q_Const; + case 'C': + return Q_Volatile; + case 'D': + return Qualifiers(Q_Const | Q_Volatile); + } + + Error = true; + RestoreOnError.shouldRestore(true); + return Q_None; +} + +Qualifiers Demangler::demangleQualifiers(QualifierMangleLocation Location) { + if (Location == QualifierMangleLocation::Detect) { + switch (MangledName.front()) { + case 'Q': + case 'R': + case 'S': + case 'T': + Location = QualifierMangleLocation::Member; + break; + case 'A': + case 'B': + case 'C': + case 'D': + Location = QualifierMangleLocation::NonMember; + break; + default: + Error = true; + return Q_None; + } + } + + if (Location == QualifierMangleLocation::Member) { + switch (MangledName.popFront()) { + // Member qualifiers + case 'Q': + return Q_None; + case 'R': + return Q_Const; + case 'S': + return Q_Volatile; + case 'T': + return Qualifiers(Q_Const | Q_Volatile); + } + } else { + switch (MangledName.popFront()) { + // Non-Member qualifiers + case 'A': + return Q_None; + case 'B': + return Q_Const; + case 'C': + return Q_Volatile; + case 'D': + return Qualifiers(Q_Const | Q_Volatile); + } + } + Error = true; + return Q_None; +} + +// ::= +// ::= # pointers, references +Type *Demangler::demangleType(QualifierMangleMode QMM) { + Qualifiers Quals = Q_None; + if (QMM == QualifierMangleMode::Mangle) + Quals = Qualifiers(Quals | demangleQualifiers()); + else if (QMM == QualifierMangleMode::Result) { + if (MangledName.consumeFront('?')) + Quals = Qualifiers(Quals | demangleQualifiers()); + } + + Type *Ty = nullptr; + switch (MangledName.front()) { + case 'T': // union + case 'U': // struct + case 'V': // class + case 'W': // enum + Ty = demangleClassType(); + break; + case 'A': // foo & + case 'P': // foo * + case 'Q': // foo *const + case 'R': // foo *volatile + case 'S': // foo *const volatile + Ty = demanglePointerType(); + break; + case 'Y': + Ty = demangleArrayType(); + break; + default: + Ty = demangleBasicType(); + break; + } + Ty->Quals = Qualifiers(Ty->Quals | Quals); + return Ty; +} + +static bool functionHasThisPtr(const FunctionType &Ty) { + assert(Ty.Prim == PrimTy::Function); + if (Ty.FunctionClass & Global) + return false; + if (Ty.FunctionClass & Static) + return false; + return true; +} + +ReferenceKind Demangler::demangleReferenceKind() { + if (MangledName.consumeFront('G')) + return ReferenceKind::LValueRef; + else if (MangledName.consumeFront('H')) + return ReferenceKind::RValueRef; + return ReferenceKind::None; +} + +Type *Demangler::demangleFunctionEncoding() { + FunctionType *FTy = new (Arena) FunctionType; + + FTy->Prim = PrimTy::Function; + FTy->FunctionClass = (FuncClass)demangleFunctionClass(); + if (functionHasThisPtr(*FTy)) { + FTy->Quals = demanglePointerExtQualifiers(); + FTy->RefKind = demangleReferenceKind(); + FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers()); + } + + // Fields that appear on both member and non-member functions. + FTy->CallConvention = demangleCallingConvention(); + + // ::= + // ::= @ # structors (they have no declared return type) + bool IsStructor = MangledName.consumeFront('@'); + if (!IsStructor) + FTy->ReturnType = demangleType(QualifierMangleMode::Result); + + FTy->Params = demangleParameterList(); + + return FTy; +} + +// Reads a primitive type. +Type *Demangler::demangleBasicType() { + Type *Ty = new (Arena) Type; + + switch (MangledName.popFront()) { + case 'X': + Ty->Prim = PrimTy::Void; + break; + case 'D': + Ty->Prim = PrimTy::Char; + break; + case 'C': + Ty->Prim = PrimTy::Schar; + break; + case 'E': + Ty->Prim = PrimTy::Uchar; + break; + case 'F': + Ty->Prim = PrimTy::Short; + break; + case 'G': + Ty->Prim = PrimTy::Ushort; + break; + case 'H': + Ty->Prim = PrimTy::Int; + break; + case 'I': + Ty->Prim = PrimTy::Uint; + break; + case 'J': + Ty->Prim = PrimTy::Long; + break; + case 'K': + Ty->Prim = PrimTy::Ulong; + break; + case 'M': + Ty->Prim = PrimTy::Float; + break; + case 'N': + Ty->Prim = PrimTy::Double; + break; + case 'O': + Ty->Prim = PrimTy::Ldouble; + break; + case '_': { + switch (MangledName.popFront()) { + case 'N': + Ty->Prim = PrimTy::Bool; + break; + case 'J': + Ty->Prim = PrimTy::Int64; + break; + case 'K': + Ty->Prim = PrimTy::Uint64; + break; + case 'W': + Ty->Prim = PrimTy::Wchar; + break; + } + break; + } + } + return Ty; +} + +UdtType *Demangler::demangleClassType() { + UdtType *UTy = new (Arena) UdtType; + + switch (MangledName.popFront()) { + case 'T': + UTy->Prim = PrimTy::Union; + break; + case 'U': + UTy->Prim = PrimTy::Struct; + break; + case 'V': + UTy->Prim = PrimTy::Class; + break; + case 'W': + if (MangledName.popFront() != '4') { + Error = true; + return nullptr; + } + UTy->Prim = PrimTy::Enum; + break; + default: + assert(false); + } + + UTy->UdtName = demangleName(); + return UTy; +} + +// ::= E? +// # the E is required for 64-bit non-static pointers +PointerType *Demangler::demanglePointerType() { + PointerType *Pointer = new (Arena) PointerType; + + Pointer->Quals = Q_None; + switch (MangledName.popFront()) { + case 'A': + Pointer->Prim = PrimTy::Ref; + break; + case 'P': + Pointer->Prim = PrimTy::Ptr; + break; + case 'Q': + Pointer->Prim = PrimTy::Ptr; + Pointer->Quals = Q_Const; + break; + case 'R': + Pointer->Quals = Q_Volatile; + Pointer->Prim = PrimTy::Ptr; + break; + case 'S': + Pointer->Quals = Qualifiers(Q_Const | Q_Volatile); + Pointer->Prim = PrimTy::Ptr; + break; + default: + assert(false && "Ty is not a pointer type!"); + } + + if (MangledName.consumeFront("6")) { + FunctionType *FTy = new (Arena) FunctionType; + FTy->Prim = PrimTy::Function; + FTy->CallConvention = demangleCallingConvention(); + + FTy->ReturnType = demangleType(QualifierMangleMode::Drop); + FTy->Params = demangleParameterList(); + + if (!MangledName.consumeFront("@Z")) + MangledName.consumeFront("Z"); + + Pointer->Pointee = FTy; + return Pointer; + } + + Qualifiers ExtQuals = demanglePointerExtQualifiers(); + Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); + + Pointer->Pointee = demangleType(QualifierMangleMode::Mangle); + return Pointer; +} + +Qualifiers Demangler::demanglePointerExtQualifiers() { + Qualifiers Quals = Q_None; + if (MangledName.consumeFront('E')) + Quals = Qualifiers(Quals | Q_Pointer64); + if (MangledName.consumeFront('I')) + Quals = Qualifiers(Quals | Q_Restrict); + if (MangledName.consumeFront('F')) + Quals = Qualifiers(Quals | Q_Unaligned); + + return Quals; +} + +ArrayType *Demangler::demangleArrayType() { + assert(MangledName.front() == 'Y'); + MangledName.popFront(); + + int Dimension = demangleNumber(); + if (Dimension <= 0) { + Error = true; + return nullptr; + } + + ArrayType *ATy = new (Arena) ArrayType; + ArrayType *Dim = ATy; + for (int I = 0; I < Dimension; ++I) { + Dim->Prim = PrimTy::Array; + Dim->ArrayDimension = demangleNumber(); + Dim->NextDimension = new (Arena) ArrayType; + Dim = Dim->NextDimension; + } + + if (MangledName.consumeFront("$$C")) { + if (MangledName.consumeFront("B")) + ATy->Quals = Q_Const; + else if (MangledName.consumeFront("C") || MangledName.consumeFront("D")) + ATy->Quals = Qualifiers(Q_Const | Q_Volatile); + else if (!MangledName.consumeFront("A")) + Error = true; + } + + ATy->ElementType = demangleType(QualifierMangleMode::Drop); + Dim->ElementType = ATy->ElementType; + return ATy; +} + +// Reads a function or a template parameters. +ParamList Demangler::demangleParameterList() { + // Within the same parameter list, you can backreference the first 10 types. + Type *BackRef[10]; + int Idx = 0; + + ParamList *Head; + ParamList **Current = &Head; + while (!Error && !MangledName.startsWith('@') && + !MangledName.startsWith('Z')) { + if (startsWithDigit(MangledName)) { + int N = MangledName[0] - '0'; + if (N >= Idx) { + Error = true; + return {}; + } + MangledName = MangledName.dropFront(); + + *Current = new (Arena) ParamList; + (*Current)->Current = BackRef[N]->clone(Arena); + Current = &(*Current)->Next; + continue; + } + + size_t ArrayDimension = MangledName.size(); + + *Current = new (Arena) ParamList; + (*Current)->Current = demangleType(QualifierMangleMode::Drop); + + // Single-letter types are ignored for backreferences because + // memorizing them doesn't save anything. + if (Idx <= 9 && ArrayDimension - MangledName.size() > 1) + BackRef[Idx++] = (*Current)->Current; + Current = &(*Current)->Next; + } + + return *Head; +} + +void Demangler::output() { + // Converts an AST to a string. + // + // Converting an AST representing a C++ type to a string is tricky due + // to the bad grammar of the C++ declaration inherited from C. You have + // to construct a string from inside to outside. For example, if a type + // X is a pointer to a function returning int, the order you create a + // string becomes something like this: + // + // (1) X is a pointer: *X + // (2) (1) is a function returning int: int (*X)() + // + // So you cannot construct a result just by appending strings to a result. + // + // To deal with this, we split the function into two. outputPre() writes + // the "first half" of type declaration, and outputPost() writes the + // "second half". For example, outputPre() writes a return type for a + // function and outputPost() writes an parameter list. + Type::outputPre(OS, *SymbolType); + outputName(OS, SymbolName); + Type::outputPost(OS, *SymbolType); + + // Null terminate the buffer. + OS << '\0'; +} + +char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N, + int *Status) { + OutputStream OS = OutputStream::create(Buf, N, 1024); + + Demangler D(OS, StringView(MangledName)); + D.parse(); + + if (D.Error) + *Status = llvm::demangle_invalid_mangled_name; + else + *Status = llvm::demangle_success; + + D.output(); + return OS.getBuffer(); +} diff --git a/test/Demangle/ms-basic.test b/test/Demangle/ms-basic.test new file mode 100644 index 00000000000..17694b90284 --- /dev/null +++ b/test/Demangle/ms-basic.test @@ -0,0 +1,230 @@ +; RUN: llvm-undname < %s | FileCheck %s + +; CHECK-NOT: Invalid mangled name + +?x@@3HA +; CHECK: int x + +?x@@3PEAHEA +; CHECK: int *x + +?x@@3PEAPEAHEA +; CHECK: int **x + +?x@@3PEAY02HEA +; CHECK: int (*x)[3] + +?x@@3PEAY124HEA +; CHECK: int (*x)[3][5] + +?x@@3PEAY02$$CBHEA +; CHECK: int const (*x)[3] + +?x@@3PEAEEA +; CHECK: unsigned char *x + +?x@@3PEAY1NKM@5HEA +; CHECK: int (*x)[3500][6] + +?x@@YAXMH@Z +; CHECK: void __cdecl x(float, int) + +?x@@3P6AHMNH@ZEA +; CHECK: int __cdecl (*x)(float, double, int) + +?x@@3P6AHP6AHM@ZN@ZEA +; CHECK: int __cdecl (*x)(int __cdecl (*)(float), double) + +?x@@3P6AHP6AHM@Z0@ZEA +; CHECK: int __cdecl (*x)(int __cdecl (*)(float), int __cdecl (*)(float)) + +?x@ns@@3HA +; CHECK: int ns::x + +; Microsoft's undname doesn't handle Q correctly or the multiple occurrences +; of the const modifier. So the results here differ, but ours are correct. +?x@@3PEAHEA +; CHECK: int *x + +?x@@3PEBHEB +; CHECK: int const *x + +?x@@3QEAHEA +; CHECK: int *const x + +?x@@3QEBHEB +; CHECK: int const *const x + + +?x@@3AEBHEB +; CHECK: int const &x + +?x@@3PEAUty@@EA +; CHECK: struct ty *x + +?x@@3PEATty@@EA +; CHECK: union ty *x + +?x@@3PEAUty@@EA +; CHECK: struct ty *x + +?x@@3PEAW4ty@@EA +; CHECK: enum ty *x + +?x@@3PEAVty@@EA +; CHECK: class ty *x + +?x@@3PEAV?$tmpl@H@@EA +; CHECK: class tmpl *x + +?x@@3PEAU?$tmpl@H@@EA +; CHECK: struct tmpl *x + +?x@@3PEAT?$tmpl@H@@EA +; CHECK: union tmpl *x + +?instance@@3Vklass@@A +; CHECK: class klass instance + +?instance$initializer$@@3P6AXXZEA +; CHECK: void __cdecl (*instance$initializer$)(void) + +??0klass@@QEAA@XZ +; CHECK: __cdecl klass::klass(void) + +??1klass@@QEAA@XZ +; CHECK: __cdecl klass::~klass(void) + +?x@@YAHPEAVklass@@AEAV1@@Z +; CHECK: int __cdecl x(class klass *, class klass &) + +?x@ns@@3PEAV?$klass@HH@1@EA +; CHECK: class ns::klass *ns::x + +?fn@?$klass@H@ns@@QEBAIXZ +; CHECK: unsigned int __cdecl ns::klass::fn(void) const + + +??4klass@@QEAAAEBV0@AEBV0@@Z +; CHECK: class klass const &__cdecl klass::operator=(class klass const &) + +??7klass@@QEAA_NXZ +; CHECK: bool __cdecl klass::operator!(void) + +??8klass@@QEAA_NAEBV0@@Z +; CHECK: bool __cdecl klass::operator==(class klass const &) + +??9klass@@QEAA_NAEBV0@@Z +; CHECK: bool __cdecl klass::operator!=(class klass const &) + +??Aklass@@QEAAH_K@Z +; CHECK: int __cdecl klass::operator[](unsigned __int64) + +??Cklass@@QEAAHXZ +; CHECK: int __cdecl klass::operator->(void) + +??Dklass@@QEAAHXZ +; CHECK: int __cdecl klass::operator*(void) + +??Eklass@@QEAAHXZ +; CHECK: int __cdecl klass::operator++(void) + +??Eklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator++(int) + +??Fklass@@QEAAHXZ +; CHECK: int __cdecl klass::operator--(void) + +??Fklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator--(int) + +??Hklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator+(int) + +??Gklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator-(int) + +??Iklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator&(int) + +??Jklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator->*(int) + +??Kklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator/(int) + +??Mklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator<(int) + +??Nklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator<=(int) + +??Oklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator>(int) + +??Pklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator>=(int) + +??Qklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator,(int) + +??Rklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator()(int) + +??Sklass@@QEAAHXZ +; CHECK: int __cdecl klass::operator~(void) + +??Tklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator^(int) + +??Uklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator|(int) + +??Vklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator&&(int) + +??Wklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator||(int) + +??Xklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator*=(int) + +??Yklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator+=(int) + +??Zklass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator-=(int) + +??_0klass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator/=(int) + +??_1klass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator%=(int) + +??_2klass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator>>=(int) + +??_3klass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator<<=(int) + +??_6klass@@QEAAHH@Z +; CHECK: int __cdecl klass::operator^=(int) + +??6@YAAEBVklass@@AEBV0@H@Z +; CHECK: class klass const &__cdecl operator<<(class klass const &, int) + +??5@YAAEBVklass@@AEBV0@_K@Z +; CHECK: class klass const &__cdecl operator>>(class klass const &, unsigned __int64) + +??2@YAPEAX_KAEAVklass@@@Z +; CHECK: void *__cdecl operator new(unsigned __int64, class klass &) + +??_U@YAPEAX_KAEAVklass@@@Z +; CHECK: void *__cdecl operator new[](unsigned __int64, class klass &) + +??3@YAXPEAXAEAVklass@@@Z +; CHECK: void __cdecl operator delete(void *, class klass &) + +??_V@YAXPEAXAEAVklass@@@Z +; CHECK: void __cdecl operator delete[](void *, class klass &) + diff --git a/test/Demangle/ms-mangle.test b/test/Demangle/ms-mangle.test new file mode 100644 index 00000000000..18e260b30c6 --- /dev/null +++ b/test/Demangle/ms-mangle.test @@ -0,0 +1,363 @@ +; These tests are based on clang/test/CodeGenCXX/mangle-ms.cpp +; RUN: llvm-undname < %s | FileCheck %s + +; CHECK-NOT: Invalid mangled name + +?k@@3PTfoo@@DT1 + + +?a@@3HA +; CHECK: int a + +?b@N@@3HA +; CHECK: int N::b + +?anonymous@?A@N@@3HA +; CHECK: int N::`anonymous namespace'::anonymous + +?_c@@YAHXZ +; CHECK: int __cdecl _c(void) + +?d@foo@@0FB +; CHECK: static short const foo::d + +?e@foo@@1JC +; CHECK: static long volatile foo::e + +?f@foo@@2DD +; CHECK: static char const volatile foo::f + +??0foo@@QAE@XZ +; CHECK: __thiscall foo::foo(void) + +??0foo@@QEAA@XZ +; CHECK: __cdecl foo::foo(void) + +??1foo@@QAE@XZ +; CHECK: __thiscall foo::~foo(void) + +??1foo@@QEAA@XZ +; CHECK: __cdecl foo::~foo(void) + +??0foo@@QAE@H@Z +; CHECK: __thiscall foo::foo(int) + +??0foo@@QEAA@H@Z +; CHECK: __cdecl foo::foo(int) + +??0foo@@QAE@PAD@Z +; CHECK: __thiscall foo::foo(char *) + +??0foo@@QEAA@PEAD@Z +; CHECK: __cdecl foo::foo(char *) + +?bar@@YA?AVfoo@@XZ +; CHECK: class foo __cdecl bar(void) + +?bar@@YA?AVfoo@@XZ +; CHECK: class foo __cdecl bar(void) + +??Hfoo@@QAEHH@Z +; CHECK: int __thiscall foo::operator+(int) + +??Hfoo@@QEAAHH@Z +; CHECK: int __cdecl foo::operator+(int) + +?static_method@foo@@SAPAV1@XZ +; CHECK: static class foo *__cdecl foo::static_method(void) + +?static_method@foo@@SAPEAV1@XZ +; CHECK: static class foo *__cdecl foo::static_method(void) + +?g@bar@@2HA +; CHECK: static int bar::g + +; undname returns `int *h1`, but it is a bug in their demangler. Their mangler +; correctly mangles `int *h1` as ?h1@3PAHA and `int * const h1` as ?h1@3QAHA +?h1@@3QAHA +; CHECK: int *const h1 + +?h2@@3QBHB +; CHECK: int const *const h2 + +?h3@@3QIAHIA +; CHECK: int * __restrict const h3 + +?h3@@3QEIAHEIA +; CHECK: int * __restrict const h3 + +?i@@3PAY0BE@HA +; CHECK: int (*i)[20] + +?FunArr@@3PAY0BE@P6AHHH@ZA +; CHECK: int __cdecl (*(*FunArr)[20])(int, int) + +?j@@3P6GHCE@ZA +; CHECK: int __stdcall (*j)(signed char, unsigned char) + + +; FIXME: We don't handle member pointers yet. +; ?k@@3PTfoo@@DT1 +; FIXME: char const volatile foo::*k + +; ?k@@3PETfoo@@DET1 +; FIXME: char const volatile foo::*k + +; ?l@@3P8foo@@AEHH@ZQ1 +; FIXME: int __thiscall (foo::*l)(int) + +?g_cInt@@3HB +; CHECK: int const g_cInt + +?g_vInt@@3HC +; CHECK: int volatile g_vInt + +?g_cvInt@@3HD +; CHECK: int const volatile g_cvInt + +?beta@@YI_N_J_W@Z +; CHECK: bool __fastcall beta(__int64, wchar_t) + +?beta@@YA_N_J_W@Z +; CHECK: bool __cdecl beta(__int64, wchar_t) + +?alpha@@YGXMN@Z +; CHECK: void __stdcall alpha(float, double) + +?alpha@@YAXMN@Z +; CHECK: void __cdecl alpha(float, double) + +?gamma@@YAXVfoo@@Ubar@@Tbaz@@W4quux@@@Z +; CHECK: void __cdecl gamma(class foo, struct bar, union baz, enum quux) + +?gamma@@YAXVfoo@@Ubar@@Tbaz@@W4quux@@@Z +; CHECK: void __cdecl gamma(class foo, struct bar, union baz, enum quux) + +?delta@@YAXQAHABJ@Z +; CHECK: void __cdecl delta(int *const, long const &) + +?delta@@YAXQEAHAEBJ@Z +; CHECK: void __cdecl delta(int *const, long const &) + +?epsilon@@YAXQAY19BE@H@Z +; CHECK: void __cdecl epsilon(int (*const)[10][20]) + +?epsilon@@YAXQEAY19BE@H@Z +; CHECK: void __cdecl epsilon(int (*const)[10][20]) + +?zeta@@YAXP6AHHH@Z@Z +; CHECK: void __cdecl zeta(int __cdecl (*)(int, int)) + +?zeta@@YAXP6AHHH@Z@Z +; CHECK: void __cdecl zeta(int __cdecl (*)(int, int)) + +??2@YAPAXI@Z +; CHECK: void *__cdecl operator new(unsigned int) + +??3@YAXPAX@Z +; CHECK: void __cdecl operator delete(void *) + +??_U@YAPAXI@Z +; CHECK: void *__cdecl operator new[](unsigned int) + +??_V@YAXPAX@Z +; CHECK: void __cdecl operator delete[](void *) + +?color1@@3PANA +; CHECK: double *color1 + +?color2@@3QBNB +; CHECK: double const *const color2 + +; FIXME-EXTRACONST: These tests fails because we print an extra const inside the parens. +; ?color3@@3QAY02$$CBNA +; FIXME-EXTRACONST: double const (*color3)[3] + +; ?color4@@3QAY02$$CBNA +; FIXME-EXTRACONST: double const (*color4)[3] + +; FIXME-MEMBERPTR: We don't support member pointers yet. +; ?memptr1@@3RESB@@HES1 +; FIXME-MEMBERPTR: volatile int B::*memptr2 + +; ?memptr2@@3PESB@@HES1 +; FIXME: volatile int B::*memptr2 + +; ?memptr3@@3REQB@@HEQ1 +; FIXME-MEMBERPTR: int B::* volatile memptr3 + +; ?funmemptr1@@3RESB@@R6AHXZES1 +; FIXME-MEMBERPTR: int __cdecl (* volatile B::* volatile funmemptr1)(void) + +; ?funmemptr2@@3PESB@@R6AHXZES1 +; FIXME-MEMBERPTR: int __cdecl (* volatile B::*funmemptr2)(void) + +; ?funmemptr3@@3REQB@@P6AHXZEQ1 +; FIXME-MEMBERPTR: int __cdecl (* B::*volatile funmemptr3)(void) + +; ?memptrtofun1@@3R8B@@EAAXXZEQ1 +; FIXME-MEMBERPTR: void __cdecl (B::*volatile memptrtofun1)(void) + +; ?memptrtofun2@@3P8B@@EAAXXZEQ1 +; FIXME-MEMBERPTR: void __cdecl (B::*memptrtofun2)(void) + +; ?memptrtofun3@@3P8B@@EAAXXZEQ1 +; FIXME-MEMBERPTR: void __cdecl (B::*memptrtofun3)(void) + +; ?memptrtofun4@@3R8B@@EAAHXZEQ1 +; FIXME-MEMBERPTR: int __cdecl (B::* volatile memptrtofun4)(void) + +; ?memptrtofun5@@3P8B@@EAA?CHXZEQ1 +; FIXME-MEMBERPTR: int volatile __cdecl (B::*memptrtofun5)(void) + +; ?memptrtofun6@@3P8B@@EAA?BHXZEQ1 +; FIXME-MEMBERPTR: int const __cdecl (B::*memptrtofun6)(void) + +; ?memptrtofun7@@3R8B@@EAAP6AHXZXZEQ1 +; FIXME-MEMBERPTR: int __cdecl (*(__cdecl B::*volatile memptrtofun7)(void))(void) + +; ?memptrtofun8@@3P8B@@EAAR6AHXZXZEQ1 +; FIXME-MEMBERPTR: int __cdecl (*(__cdecl B::*memptrtofun8)(void))(void) + +; ?memptrtofun9@@3P8B@@EAAQ6AHXZXZEQ1 +; FIXME-MEMBERPTR: int __cdecl(*(__cdecl B::*memptrtofun9)(void))(void) + + +?fooE@@YA?AW4E@@XZ +; CHECK: enum E __cdecl fooE(void) + +?fooE@@YA?AW4E@@XZ +; CHECK: enum E __cdecl fooE(void) + +?fooX@@YA?AVX@@XZ +; CHECK: class X __cdecl fooX(void) + +?fooX@@YA?AVX@@XZ +; CHECK: class X __cdecl fooX(void) + +?s0@PR13182@@3PADA +; CHECK: char *PR13182::s0 + +?s1@PR13182@@3PADA +; CHECK: char *PR13182::s1 + +?s2@PR13182@@3QBDB +; CHECK: char const *const PR13182::s2 + +?s3@PR13182@@3QBDB +; CHECK: char const *const PR13182::s3 + +?s4@PR13182@@3RCDC +; CHECK: char volatile *volatile PR13182::s4 + +?s5@PR13182@@3SDDD +; CHECK: char const volatile *const volatile PR13182::s5 + +; undname adds an extra const in here, but it seems like their bug. +?s6@PR13182@@3PBQBDB +; CHECK: char const *const *PR13182::s6 + +; FIXME-EXTERNC: We don't properly support static locals in extern c functions yet. +; ?local@?1??extern_c_func@@9@4HA +; FIXME-EXTERNC: int `extern_c_func'::`2'::local + +; ?local@?1??extern_c_func@@9@4HA +; FIXME-EXTERNC: int `extern_c_func'::`2'::local + +??2OverloadedNewDelete@@SAPAXI@Z +; CHECK: static void *__cdecl OverloadedNewDelete::operator new(unsigned int) + + +??_UOverloadedNewDelete@@SAPAXI@Z +; CHECK: static void *__cdecl OverloadedNewDelete::operator new[](unsigned int) + +??3OverloadedNewDelete@@SAXPAX@Z +; CHECK: static void __cdecl OverloadedNewDelete::operator delete(void *) + + +??_VOverloadedNewDelete@@SAXPAX@Z +; CHECK: static void __cdecl OverloadedNewDelete::operator delete[](void *) + +??HOverloadedNewDelete@@QAEHH@Z +; CHECK: int __thiscall OverloadedNewDelete::operator+(int) + +??2OverloadedNewDelete@@SAPEAX_K@Z +; CHECK: static void *__cdecl OverloadedNewDelete::operator new(unsigned __int64) + +??_UOverloadedNewDelete@@SAPEAX_K@Z +; CHECK: static void *__cdecl OverloadedNewDelete::operator new[](unsigned __int64) + +??3OverloadedNewDelete@@SAXPEAX@Z +; CHECK: static void __cdecl OverloadedNewDelete::operator delete(void *) + + +??_VOverloadedNewDelete@@SAXPEAX@Z +; CHECK: static void __cdecl OverloadedNewDelete::operator delete[](void *) + +??HOverloadedNewDelete@@QEAAHH@Z +; CHECK: int __cdecl OverloadedNewDelete::operator+(int) + + +??2TypedefNewDelete@@SAPAXI@Z +; CHECK: static void *__cdecl TypedefNewDelete::operator new(unsigned int) + + +??_UTypedefNewDelete@@SAPAXI@Z +; CHECK: static void *__cdecl TypedefNewDelete::operator new[](unsigned int) + +??3TypedefNewDelete@@SAXPAX@Z +; CHECK: static void __cdecl TypedefNewDelete::operator delete(void *) + +??_VTypedefNewDelete@@SAXPAX@Z +; CHECK: static void __cdecl TypedefNewDelete::operator delete[](void *) + +?vector_func@@YQXXZ +; CHECK: void __vectorcall vector_func(void) + +; FIXME-EXTERNC: We don't support extern C funcs currently. +; ??$fn_tmpl@$1?extern_c_func@@YAXXZ@@YAXXZ +; FIXME-EXTERNC: void __cdecl fn_tmpl<&void __cdecl extern_c_func(void)>(void) + +; ?overloaded_fn@@$$J0YAXXZ +; FIXME-EXTERNC: extern \"C\" void __cdecl overloaded_fn(void) + + +?f@UnnamedType@@YAXUT2@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T2) + +?f@UnnamedType@@YAXPAUT4@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T4 *) + +?f@UnnamedType@@YAXUT4@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T4) + +?f@UnnamedType@@YAXUT5@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T5) + +?f@UnnamedType@@YAXUT2@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T2) + +?f@UnnamedType@@YAXUT4@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T4) + +?f@UnnamedType@@YAXUT5@S@1@@Z +; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T5) + +?f@Atomic@@YAXU?$_Atomic@H@__clang@@@Z +; CHECK: void __cdecl Atomic::f(struct __clang::_Atomic) + +?f@Complex@@YAXU?$_Complex@H@__clang@@@Z +; CHECK: void __cdecl Complex::f(struct __clang::_Complex) + +?f@Float16@@YAXU_Float16@__clang@@@Z +; CHECK: void __cdecl Float16::f(struct __clang::_Float16) + + +??0?$L@H@NS@@QEAA@XZ +; CHECK: __cdecl NS::L::L(void) + +??0Bar@Foo@@QEAA@XZ +; CHECK: __cdecl Foo::Bar::Bar(void) + +??0?$L@V?$H@PAH@PR26029@@@PR26029@@QAE@XZ +; CHECK: __thiscall PR26029::L>::L>(void) diff --git a/test/Demangle/ms-windows.test b/test/Demangle/ms-windows.test new file mode 100644 index 00000000000..bc800903583 --- /dev/null +++ b/test/Demangle/ms-windows.test @@ -0,0 +1,17 @@ +; See clang/test/CodeGenCXX/mangle-windows.cpp +; These tests are based on clang/test/CodeGenCXX/mangle-ms.cpp +; RUN: llvm-undname < %s | FileCheck %s + +; CHECK-NOT: Invalid mangled name + +?bar@Foo@@SGXXZ +; CHECK: static void __stdcall Foo::bar(void) + +?bar@Foo@@QAGXXZ +; CHECK: void __stdcall Foo::bar(void) + +?f2@@YIXXZ +; CHECK: void __fastcall f2(void) + +?f1@@YGXXZ +; CHECK: void __stdcall f1(void) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 5dea5b44048..1732ea0cb8a 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -50,6 +50,7 @@ subdirectories = llvm-rtdyld llvm-size llvm-split + llvm-undname opt verify-uselistorder diff --git a/tools/llvm-undname/CMakeLists.txt b/tools/llvm-undname/CMakeLists.txt new file mode 100644 index 00000000000..062f0052597 --- /dev/null +++ b/tools/llvm-undname/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Demangle + Support + ) + +add_llvm_tool(llvm-undname + llvm-undname.cpp + ) diff --git a/tools/llvm-undname/LLVMBuild.txt b/tools/llvm-undname/LLVMBuild.txt new file mode 100644 index 00000000000..6e598f888ff --- /dev/null +++ b/tools/llvm-undname/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./tools/llvm-undname/LLVMBuild.txt -----------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-undname +parent = Tools +required_libraries = Demangle Support + diff --git a/tools/llvm-undname/llvm-undname.cpp b/tools/llvm-undname/llvm-undname.cpp new file mode 100644 index 00000000000..f4c09f57505 --- /dev/null +++ b/tools/llvm-undname/llvm-undname.cpp @@ -0,0 +1,79 @@ +//===-- llvm-undname.cpp - Microsoft ABI name undecorator +//------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility works like the windows undname utility. It converts mangled +// Microsoft symbol names into pretty C/C++ human-readable names. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; + +cl::list Symbols(cl::Positional, cl::desc(""), + cl::ZeroOrMore); + +static void demangle(const std::string &S) { + int Status; + char *ResultBuf = microsoftDemangle(S.c_str(), nullptr, nullptr, &Status); + if (Status == llvm::demangle_success) { + outs() << ResultBuf << "\n"; + outs().flush(); + } else { + errs() << "Error: Invalid mangled name\n"; + } + std::free(ResultBuf); +}; + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "llvm-undname\n"); + + if (Symbols.empty()) { + while (true) { + std::string LineStr; + std::getline(std::cin, LineStr); + if (std::cin.eof()) + break; + + StringRef Line(LineStr); + Line = Line.trim(); + if (Line.empty() || Line.startswith("#") || Line.startswith(";")) + continue; + + // If the user is manually typing in these decorated names, don't echo + // them to the terminal a second time. If they're coming from redirected + // input, however, then we should display the input line so that the + // mangled and demangled name can be easily correlated in the output. + if (!sys::Process::StandardInIsUserInput()) + outs() << Line << "\n"; + demangle(Line); + outs() << "\n"; + } + } else { + for (StringRef S : Symbols) { + outs() << S << "\n"; + demangle(S); + outs() << "\n"; + } + } + + return 0; +} \ No newline at end of file