| 1 | //===-- llvm-c++filt.cpp --------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "llvm/ADT/StringExtras.h" |
| 10 | #include "llvm/Demangle/Demangle.h" |
| 11 | #include "llvm/Demangle/StringViewExtras.h" |
| 12 | #include "llvm/Option/Arg.h" |
| 13 | #include "llvm/Option/ArgList.h" |
| 14 | #include "llvm/Option/Option.h" |
| 15 | #include "llvm/Support/CommandLine.h" |
| 16 | #include "llvm/Support/LLVMDriver.h" |
| 17 | #include "llvm/Support/WithColor.h" |
| 18 | #include "llvm/Support/raw_ostream.h" |
| 19 | #include "llvm/TargetParser/Host.h" |
| 20 | #include "llvm/TargetParser/Triple.h" |
| 21 | #include <cstdlib> |
| 22 | #include <iostream> |
| 23 | |
| 24 | using namespace llvm; |
| 25 | |
| 26 | namespace { |
| 27 | enum ID { |
| 28 | OPT_INVALID = 0, // This is not an option ID. |
| 29 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
| 30 | #include "Opts.inc" |
| 31 | #undef OPTION |
| 32 | }; |
| 33 | |
| 34 | #define OPTTABLE_STR_TABLE_CODE |
| 35 | #include "Opts.inc" |
| 36 | #undef OPTTABLE_STR_TABLE_CODE |
| 37 | |
| 38 | #define OPTTABLE_PREFIXES_TABLE_CODE |
| 39 | #include "Opts.inc" |
| 40 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
| 41 | |
| 42 | using namespace llvm::opt; |
| 43 | static constexpr opt::OptTable::Info InfoTable[] = { |
| 44 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
| 45 | #include "Opts.inc" |
| 46 | #undef OPTION |
| 47 | }; |
| 48 | |
| 49 | class CxxfiltOptTable : public opt::GenericOptTable { |
| 50 | public: |
| 51 | CxxfiltOptTable() |
| 52 | : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) { |
| 53 | setGroupedShortOptions(true); |
| 54 | } |
| 55 | }; |
| 56 | } // namespace |
| 57 | |
| 58 | static bool ParseParams; |
| 59 | static bool Quote; |
| 60 | static bool StripUnderscore; |
| 61 | static bool Types; |
| 62 | |
| 63 | static StringRef ToolName; |
| 64 | |
| 65 | static void error(const Twine &Message) { |
| 66 | WithColor::error(OS&: errs(), Prefix: ToolName) << Message << '\n'; |
| 67 | exit(status: 1); |
| 68 | } |
| 69 | |
| 70 | // Quote Undecorated with "" if asked for and not already followed by a '"'. |
| 71 | static std::string optionalQuote(const std::string &Undecorated, |
| 72 | StringRef Delimiters) { |
| 73 | if (Quote && (Delimiters.empty() || Delimiters[0] != '"')) |
| 74 | return '"' + Undecorated + '"'; |
| 75 | return Undecorated; |
| 76 | } |
| 77 | |
| 78 | static std::string demangle(const std::string &Mangled, StringRef Delimiters) { |
| 79 | using llvm::itanium_demangle::starts_with; |
| 80 | std::string_view DecoratedStr = Mangled; |
| 81 | bool CanHaveLeadingDot = true; |
| 82 | if (StripUnderscore && DecoratedStr[0] == '_') { |
| 83 | DecoratedStr.remove_prefix(n: 1); |
| 84 | CanHaveLeadingDot = false; |
| 85 | } |
| 86 | |
| 87 | std::string Result; |
| 88 | if (nonMicrosoftDemangle(MangledName: DecoratedStr, Result, CanHaveLeadingDot, |
| 89 | ParseParams)) |
| 90 | return optionalQuote(Undecorated: Result, Delimiters); |
| 91 | |
| 92 | std::string Prefix; |
| 93 | char *Undecorated = nullptr; |
| 94 | |
| 95 | if (Types) |
| 96 | Undecorated = itaniumDemangle(mangled_name: DecoratedStr, ParseParams); |
| 97 | |
| 98 | if (!Undecorated && starts_with(haystack: DecoratedStr, needle: "__imp_" )) { |
| 99 | Prefix = "import thunk for " ; |
| 100 | Undecorated = itaniumDemangle(mangled_name: DecoratedStr.substr(pos: 6), ParseParams); |
| 101 | } |
| 102 | |
| 103 | Result = |
| 104 | Undecorated ? optionalQuote(Undecorated: Prefix + Undecorated, Delimiters) : Mangled; |
| 105 | free(ptr: Undecorated); |
| 106 | return Result; |
| 107 | } |
| 108 | |
| 109 | // Split 'Source' on any character that fails to pass 'IsLegalChar'. The |
| 110 | // returned vector consists of pairs where 'first' is the delimited word, and |
| 111 | // 'second' are the delimiters following that word. |
| 112 | static void SplitStringDelims( |
| 113 | StringRef Source, |
| 114 | SmallVectorImpl<std::pair<StringRef, StringRef>> &OutFragments, |
| 115 | function_ref<bool(char)> IsLegalChar) { |
| 116 | // The beginning of the input string. |
| 117 | const auto Head = Source.begin(); |
| 118 | |
| 119 | // Obtain any leading delimiters. |
| 120 | auto Start = std::find_if(first: Head, last: Source.end(), pred: IsLegalChar); |
| 121 | if (Start != Head) |
| 122 | OutFragments.push_back(Elt: {"" , Source.slice(Start: 0, End: Start - Head)}); |
| 123 | |
| 124 | // Capture each word and the delimiters following that word. |
| 125 | while (Start != Source.end()) { |
| 126 | Start = std::find_if(first: Start, last: Source.end(), pred: IsLegalChar); |
| 127 | auto End = std::find_if_not(first: Start, last: Source.end(), pred: IsLegalChar); |
| 128 | auto DEnd = std::find_if(first: End, last: Source.end(), pred: IsLegalChar); |
| 129 | OutFragments.push_back(Elt: {Source.slice(Start: Start - Head, End: End - Head), |
| 130 | Source.slice(Start: End - Head, End: DEnd - Head)}); |
| 131 | Start = DEnd; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | // This returns true if 'C' is a character that can show up in an |
| 136 | // Itanium-mangled string. |
| 137 | static bool IsLegalItaniumChar(char C) { |
| 138 | // Itanium CXX ABI [External Names]p5.1.1: |
| 139 | // '$' and '.' in mangled names are reserved for private implementations. |
| 140 | return isAlnum(C) || C == '.' || C == '$' || C == '_'; |
| 141 | } |
| 142 | |
| 143 | // If 'Split' is true, then 'Mangled' is broken into individual words and each |
| 144 | // word is demangled. Otherwise, the entire string is treated as a single |
| 145 | // mangled item. The result is output to 'OS'. |
| 146 | static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) { |
| 147 | std::string Result; |
| 148 | if (Split) { |
| 149 | SmallVector<std::pair<StringRef, StringRef>, 16> Words; |
| 150 | SplitStringDelims(Source: Mangled, OutFragments&: Words, IsLegalChar: IsLegalItaniumChar); |
| 151 | for (const auto &Word : Words) |
| 152 | Result += |
| 153 | ::demangle(Mangled: std::string(Word.first), Delimiters: Word.second) + Word.second.str(); |
| 154 | } else |
| 155 | Result = ::demangle(Mangled: std::string(Mangled), Delimiters: "" ); |
| 156 | OS << Result << '\n'; |
| 157 | OS.flush(); |
| 158 | } |
| 159 | |
| 160 | int llvm_cxxfilt_main(int argc, char **argv, const llvm::ToolContext &) { |
| 161 | BumpPtrAllocator A; |
| 162 | StringSaver Saver(A); |
| 163 | CxxfiltOptTable Tbl; |
| 164 | ToolName = argv[0]; |
| 165 | opt::InputArgList Args = Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, |
| 166 | ErrorFn: [&](StringRef Msg) { error(Message: Msg); }); |
| 167 | if (Args.hasArg(Ids: OPT_help)) { |
| 168 | Tbl.printHelp(OS&: outs(), |
| 169 | Usage: (Twine(ToolName) + " [options] <mangled>" ).str().c_str(), |
| 170 | Title: "LLVM symbol undecoration tool" ); |
| 171 | // TODO Replace this with OptTable API once it adds extrahelp support. |
| 172 | outs() << "\nPass @FILE as argument to read options from FILE.\n" ; |
| 173 | return 0; |
| 174 | } |
| 175 | if (Args.hasArg(Ids: OPT_version)) { |
| 176 | outs() << ToolName << '\n'; |
| 177 | cl::PrintVersionMessage(); |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | StripUnderscore = |
| 182 | Args.hasFlag(Pos: OPT_strip_underscore, Neg: OPT_no_strip_underscore, Default: false); |
| 183 | |
| 184 | ParseParams = !Args.hasArg(Ids: OPT_no_params); |
| 185 | |
| 186 | Quote = Args.hasArg(Ids: OPT_quote); |
| 187 | |
| 188 | Types = Args.hasArg(Ids: OPT_types); |
| 189 | |
| 190 | std::vector<std::string> Decorated = Args.getAllArgValues(Id: OPT_INPUT); |
| 191 | if (Decorated.empty()) |
| 192 | for (std::string Mangled; std::getline(is&: std::cin, str&: Mangled);) |
| 193 | demangleLine(OS&: llvm::outs(), Mangled, Split: true); |
| 194 | else |
| 195 | for (const auto &Symbol : Decorated) |
| 196 | demangleLine(OS&: llvm::outs(), Mangled: Symbol, Split: false); |
| 197 | |
| 198 | return EXIT_SUCCESS; |
| 199 | } |
| 200 | |