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