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 PREFIX(NAME, VALUE) \ |
35 | static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \ |
36 | static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \ |
37 | NAME##_init, std::size(NAME##_init) - 1); |
38 | #include "Opts.inc" |
39 | #undef PREFIX |
40 | |
41 | using namespace llvm::opt; |
42 | static constexpr opt::OptTable::Info InfoTable[] = { |
43 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
44 | #include "Opts.inc" |
45 | #undef OPTION |
46 | }; |
47 | |
48 | class CxxfiltOptTable : public opt::GenericOptTable { |
49 | public: |
50 | CxxfiltOptTable() : opt::GenericOptTable(InfoTable) { |
51 | setGroupedShortOptions(true); |
52 | } |
53 | }; |
54 | } // namespace |
55 | |
56 | static bool ParseParams; |
57 | static bool StripUnderscore; |
58 | static bool Types; |
59 | |
60 | static StringRef ToolName; |
61 | |
62 | static void error(const Twine &Message) { |
63 | WithColor::error(OS&: errs(), Prefix: ToolName) << Message << '\n'; |
64 | exit(status: 1); |
65 | } |
66 | |
67 | static std::string demangle(const std::string &Mangled) { |
68 | using llvm::itanium_demangle::starts_with; |
69 | std::string_view DecoratedStr = Mangled; |
70 | bool CanHaveLeadingDot = true; |
71 | if (StripUnderscore && DecoratedStr[0] == '_') { |
72 | DecoratedStr.remove_prefix(n: 1); |
73 | CanHaveLeadingDot = false; |
74 | } |
75 | |
76 | std::string Result; |
77 | if (nonMicrosoftDemangle(MangledName: DecoratedStr, Result, CanHaveLeadingDot, |
78 | ParseParams)) |
79 | return Result; |
80 | |
81 | std::string Prefix; |
82 | char *Undecorated = nullptr; |
83 | |
84 | if (Types) |
85 | Undecorated = itaniumDemangle(mangled_name: DecoratedStr, ParseParams); |
86 | |
87 | if (!Undecorated && starts_with(haystack: DecoratedStr, needle: "__imp_" )) { |
88 | Prefix = "import thunk for " ; |
89 | Undecorated = itaniumDemangle(mangled_name: DecoratedStr.substr(pos: 6), ParseParams); |
90 | } |
91 | |
92 | Result = Undecorated ? Prefix + Undecorated : Mangled; |
93 | free(ptr: Undecorated); |
94 | return Result; |
95 | } |
96 | |
97 | // Split 'Source' on any character that fails to pass 'IsLegalChar'. The |
98 | // returned vector consists of pairs where 'first' is the delimited word, and |
99 | // 'second' are the delimiters following that word. |
100 | static void SplitStringDelims( |
101 | StringRef Source, |
102 | SmallVectorImpl<std::pair<StringRef, StringRef>> &OutFragments, |
103 | function_ref<bool(char)> IsLegalChar) { |
104 | // The beginning of the input string. |
105 | const auto Head = Source.begin(); |
106 | |
107 | // Obtain any leading delimiters. |
108 | auto Start = std::find_if(first: Head, last: Source.end(), pred: IsLegalChar); |
109 | if (Start != Head) |
110 | OutFragments.push_back(Elt: {"" , Source.slice(Start: 0, End: Start - Head)}); |
111 | |
112 | // Capture each word and the delimiters following that word. |
113 | while (Start != Source.end()) { |
114 | Start = std::find_if(first: Start, last: Source.end(), pred: IsLegalChar); |
115 | auto End = std::find_if_not(first: Start, last: Source.end(), pred: IsLegalChar); |
116 | auto DEnd = std::find_if(first: End, last: Source.end(), pred: IsLegalChar); |
117 | OutFragments.push_back(Elt: {Source.slice(Start: Start - Head, End: End - Head), |
118 | Source.slice(Start: End - Head, End: DEnd - Head)}); |
119 | Start = DEnd; |
120 | } |
121 | } |
122 | |
123 | // This returns true if 'C' is a character that can show up in an |
124 | // Itanium-mangled string. |
125 | static bool IsLegalItaniumChar(char C) { |
126 | // Itanium CXX ABI [External Names]p5.1.1: |
127 | // '$' and '.' in mangled names are reserved for private implementations. |
128 | return isAlnum(C) || C == '.' || C == '$' || C == '_'; |
129 | } |
130 | |
131 | // If 'Split' is true, then 'Mangled' is broken into individual words and each |
132 | // word is demangled. Otherwise, the entire string is treated as a single |
133 | // mangled item. The result is output to 'OS'. |
134 | static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) { |
135 | std::string Result; |
136 | if (Split) { |
137 | SmallVector<std::pair<StringRef, StringRef>, 16> Words; |
138 | SplitStringDelims(Source: Mangled, OutFragments&: Words, IsLegalChar: IsLegalItaniumChar); |
139 | for (const auto &Word : Words) |
140 | Result += ::demangle(Mangled: std::string(Word.first)) + Word.second.str(); |
141 | } else |
142 | Result = ::demangle(Mangled: std::string(Mangled)); |
143 | OS << Result << '\n'; |
144 | OS.flush(); |
145 | } |
146 | |
147 | int llvm_cxxfilt_main(int argc, char **argv, const llvm::ToolContext &) { |
148 | BumpPtrAllocator A; |
149 | StringSaver Saver(A); |
150 | CxxfiltOptTable Tbl; |
151 | ToolName = argv[0]; |
152 | opt::InputArgList Args = Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, |
153 | ErrorFn: [&](StringRef Msg) { error(Message: Msg); }); |
154 | if (Args.hasArg(Ids: OPT_help)) { |
155 | Tbl.printHelp(OS&: outs(), |
156 | Usage: (Twine(ToolName) + " [options] <mangled>" ).str().c_str(), |
157 | Title: "LLVM symbol undecoration tool" ); |
158 | // TODO Replace this with OptTable API once it adds extrahelp support. |
159 | outs() << "\nPass @FILE as argument to read options from FILE.\n" ; |
160 | return 0; |
161 | } |
162 | if (Args.hasArg(Ids: OPT_version)) { |
163 | outs() << ToolName << '\n'; |
164 | cl::PrintVersionMessage(); |
165 | return 0; |
166 | } |
167 | |
168 | // The default value depends on the default triple. Mach-O has symbols |
169 | // prefixed with "_", so strip by default. |
170 | if (opt::Arg *A = |
171 | Args.getLastArg(Ids: OPT_strip_underscore, Ids: OPT_no_strip_underscore)) |
172 | StripUnderscore = A->getOption().matches(ID: OPT_strip_underscore); |
173 | else |
174 | StripUnderscore = Triple(sys::getProcessTriple()).isOSBinFormatMachO(); |
175 | |
176 | ParseParams = !Args.hasArg(Ids: OPT_no_params); |
177 | |
178 | Types = Args.hasArg(Ids: OPT_types); |
179 | |
180 | std::vector<std::string> Decorated = Args.getAllArgValues(Id: OPT_INPUT); |
181 | if (Decorated.empty()) |
182 | for (std::string Mangled; std::getline(is&: std::cin, str&: Mangled);) |
183 | demangleLine(OS&: llvm::outs(), Mangled, Split: true); |
184 | else |
185 | for (const auto &Symbol : Decorated) |
186 | demangleLine(OS&: llvm::outs(), Mangled: Symbol, Split: false); |
187 | |
188 | return EXIT_SUCCESS; |
189 | } |
190 | |