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