diff --git a/llvm/docs/CommandGuide/llvm-cxxfilt.rst b/llvm/docs/CommandGuide/llvm-cxxfilt.rst --- a/llvm/docs/CommandGuide/llvm-cxxfilt.rst +++ b/llvm/docs/CommandGuide/llvm-cxxfilt.rst @@ -41,8 +41,11 @@ .. option:: --format=, -s - Mangling scheme to assume. Valid values are ``auto`` (default, auto-detect the - style) and ``gnu`` (assume GNU/Itanium style). + Mangling scheme to assume. Valid values are ``gnu`` (default, auto-detect the + style, but omit the ``microsoft`` format), ``auto`` (auto-detect the style, + including ``microsoft``), ``itanium`` (assume Itanium format), ``dlang`` (assume + DLang format), ``rust`` (assume Rust format) and ``microsoft`` (assume Microsoft + format). .. option:: --help, -h diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -231,6 +231,9 @@ * ``llvm-objdump`` now uses ``--print-imm-hex`` by default, which brings its default behavior closer in line with ``objdump``. +* ``llvm-cxxfilt`` now supports the ``--format`` flag, which can be used to + specify the mangled format. It also supports demangling Microsoft symbols. + Changes to LLDB --------------------------------- diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -41,6 +41,12 @@ MSDF_NoVariableType = 1 << 5, }; +/// These functions will return True if they look like the matching +/// demangling format. +bool isItaniumEncoding(const char *S); +bool isRustEncoding(const char *S); +bool isDLangEncoding(const std::string &MangledName); + /// Demangles the Microsoft symbol pointed at by mangled_name and returns it. /// Returns a pointer to the start of a null-terminated demangled string on /// success, or nullptr on error. diff --git a/llvm/lib/Demangle/Demangle.cpp b/llvm/lib/Demangle/Demangle.cpp --- a/llvm/lib/Demangle/Demangle.cpp +++ b/llvm/lib/Demangle/Demangle.cpp @@ -14,14 +14,14 @@ #include #include -static bool isItaniumEncoding(const char *S) { +bool llvm::isItaniumEncoding(const char *S) { // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'. return std::strncmp(S, "_Z", 2) == 0 || std::strncmp(S, "___Z", 4) == 0; } -static bool isRustEncoding(const char *S) { return S[0] == '_' && S[1] == 'R'; } +bool llvm::isRustEncoding(const char *S) { return S[0] == '_' && S[1] == 'R'; } -static bool isDLangEncoding(const std::string &MangledName) { +bool llvm::isDLangEncoding(const std::string &MangledName) { return MangledName.size() >= 2 && MangledName[0] == '_' && MangledName[1] == 'D'; } diff --git a/llvm/test/tools/llvm-cxxfilt/format-flag.test b/llvm/test/tools/llvm-cxxfilt/format-flag.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxfilt/format-flag.test @@ -0,0 +1,37 @@ +## Test the --format flag to cxxfilt. +## +## Demangle one string that should be demangled (matching the format) +## and one string that won't match the format, which will stay the same. + +RUN: llvm-cxxfilt --format=rust _RNvC1a4main _Z1fi | FileCheck %s --check-prefix=RUST +RUN: llvm-cxxfilt --format=itanium _Z1fi _RNvC1a4main | FileCheck %s --check-prefix=ITANIUM +RUN: llvm-cxxfilt --format=dlang _D8demangle4testZ _RNvC1a4main | FileCheck %s --check-prefix=DLANG +RUN: llvm-cxxfilt --format=microsoft ?a@@YAHD@Z _Z1fi | FileCheck %s --check-prefix=MICROSOFT + +## Test format=gnu which will demangle rust, itanium and dlang but not microsoft. +RUN: llvm-cxxfilt --format=gnu _RNvC1a4main _Z1fi _D8demangle4testZ ?a@@YAHD@Z | FileCheck %s --check-prefix=GNU + +## Test format=auto which will demangle it all +RUN: llvm-cxxfilt --format=auto _RNvC1a4main _Z1fi _D8demangle4testZ ?a@@YAHD@Z | FileCheck %s --check-prefix=AUTO + +RUST: a::main +RUST-NEXT: _Z1fi + +ITANIUM: f(int) +ITANIUM-NEXT: _RNvC1a4main + +DLANG: demangle.test +DLANG-NEXT: _RNvC1a4main + +MICROSOFT: int __cdecl a(char) +MICROSOFT-NEXT: _Z1fi + +GNU: a::main +GNU-NEXT: f(int) +GNU-NEXT: demangle.test +GNU-NEXT: ?a@@YAHD@Z + +AUTO: a::main +AUTO-NEXT: f(int) +AUTO-NEXT: demangle.test +AUTO-NEXT: int __cdecl a(char) diff --git a/llvm/test/tools/llvm-cxxfilt/microsoft-format.test b/llvm/test/tools/llvm-cxxfilt/microsoft-format.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cxxfilt/microsoft-format.test @@ -0,0 +1,13 @@ +## Test microsoft demangle format. +## This should only demangle microsofts format if format=microsoft or format=auto. +## And fail if we set format=gnu. + +RUN: llvm-cxxfilt --format=microsoft ?a@@YAHD@Z ?c@b@@AAGXM@Z | FileCheck %s +RUN: llvm-cxxfilt --format=auto ?a@@YAHD@Z ?c@b@@AAGXM@Z | FileCheck %s +RUN: llvm-cxxfilt --format=gnu ?a@@YAHD@Z ?c@b@@AAGXM@Z | FileCheck %s --check-prefix=GNU + +CHECK: int __cdecl a(char) +CHECK-NEXT: private: void __stdcall b::c(float) + +GNU: ?a@@YAHD@Z +GNU-NEXT: ?c@b@@AAGXM@Z diff --git a/llvm/tools/llvm-cxxfilt/Opts.td b/llvm/tools/llvm-cxxfilt/Opts.td --- a/llvm/tools/llvm-cxxfilt/Opts.td +++ b/llvm/tools/llvm-cxxfilt/Opts.td @@ -19,8 +19,13 @@ def types : FF<"types", "Attempt to demangle types as well as function names">; def version : FF<"version", "Display the version">; -defm : Eq<"format", "Specify mangling format. Currently ignored because only 'gnu' is supported">; -def : F<"s", "Alias for --format">; +defm format : Eq<"format", "Specify mangling format: gnu (default), itanium, " + "dlang, rust, microsoft or auto">, + MetaVarName<"">; +def : JoinedOrSeparate<["-"], "s">, + Alias, + HelpText<"Alias for --format">, + MetaVarName<"">; def : F<"_", "Alias for --strip-underscore">, Alias; def : F<"h", "Alias for --help">, Alias; diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp --- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Demangle/Demangle.h" @@ -48,6 +49,18 @@ #undef OPTION }; +/// The different Demangle formats +enum class DemangleFormatTy { + Itanium = 0, + DLang, + Rust, + GNU, // gnu means itaninum, dlang and rust + Microsoft, + AutoDetect +}; + +static DemangleFormatTy DemangleFormat; + class CxxfiltOptTable : public opt::OptTable { public: CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } @@ -64,17 +77,70 @@ exit(1); } +// Demangle MangledName with specific DemangleFormat. +// Return an llvm::Optional that's false if it couldn't +// demangle the string. +static llvm::Optional demangleFormat(const char *MangledName, + DemangleFormatTy Format) { + llvm::Optional Result; + char *Demangled = nullptr; + int Status = demangle_unknown_error; + + switch (Format) { + case DemangleFormatTy::Itanium: + if (isItaniumEncoding(MangledName)) { + Demangled = itaniumDemangle(MangledName, nullptr, nullptr, nullptr); + Status = demangle_success; + } + break; + case DemangleFormatTy::DLang: + if (isDLangEncoding(MangledName)) { + Demangled = dlangDemangle(MangledName); + Status = demangle_success; + } + break; + case DemangleFormatTy::Rust: + if (isRustEncoding(MangledName)) { + Demangled = rustDemangle(MangledName); + Status = demangle_success; + } + break; + case DemangleFormatTy::Microsoft: + Demangled = + microsoftDemangle(MangledName, nullptr, nullptr, nullptr, &Status); + break; + case DemangleFormatTy::AutoDetect: + case DemangleFormatTy::GNU: + std::string DM; + if (nonMicrosoftDemangle(MangledName, DM)) + return DM; + // If the flag is GNU we don't want to run the code below, if it's auto we + // run it as well. + if (Format == DemangleFormatTy::AutoDetect) + if (auto D = demangleFormat(MangledName, DemangleFormatTy::Microsoft)) + return D; + return Result; + } + + if (Status == demangle_success && Demangled) { + Result = Demangled; + std::free(Demangled); + } + + return Result; +} + static std::string demangle(const std::string &Mangled) { const char *DecoratedStr = Mangled.c_str(); if (StripUnderscore) if (DecoratedStr[0] == '_') ++DecoratedStr; - std::string Result; - if (nonMicrosoftDemangle(DecoratedStr, Result)) - return Result; + if (auto Result = demangleFormat(DecoratedStr, DemangleFormat)) + return Result.value(); std::string Prefix; + std::string Result = DecoratedStr; char *Undecorated = nullptr; if (Types) @@ -162,6 +228,23 @@ return 0; } + StringRef V = Args.getLastArgValue(OPT_format_EQ, "gnu"); + if (V == "gnu") + DemangleFormat = DemangleFormatTy::GNU; + else if (V == "microsoft") + DemangleFormat = DemangleFormatTy::Microsoft; + else if (V == "auto") + DemangleFormat = DemangleFormatTy::AutoDetect; + else if (V == "dlang") + DemangleFormat = DemangleFormatTy::DLang; + else if (V == "rust") + DemangleFormat = DemangleFormatTy::Rust; + else if (V == "itanium") + DemangleFormat = DemangleFormatTy::Itanium; + else + error("--format value should be one of: gnu (default), itanium, dlang, " + "rust, microsoft or auto"); + // The default value depends on the default triple. Mach-O has symbols // prefixed with "_", so strip by default. if (opt::Arg *A =