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 | |