1311116Sdim//===-- llvm-c++filt.cpp --------------------------------------------------===//
2311116Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6311116Sdim//
7311116Sdim//===----------------------------------------------------------------------===//
8311116Sdim
9353358Sdim#include "llvm/ADT/StringExtras.h"
10360784Sdim#include "llvm/ADT/Triple.h"
11311116Sdim#include "llvm/Demangle/Demangle.h"
12321369Sdim#include "llvm/Support/CommandLine.h"
13360784Sdim#include "llvm/Support/Host.h"
14341825Sdim#include "llvm/Support/InitLLVM.h"
15311116Sdim#include "llvm/Support/raw_ostream.h"
16311116Sdim#include <cstdlib>
17311116Sdim#include <iostream>
18311116Sdim
19311116Sdimusing namespace llvm;
20311116Sdim
21321369Sdimenum Style {
22321369Sdim  Auto,  ///< auto-detect mangling
23321369Sdim  GNU,   ///< GNU
24321369Sdim  Lucid, ///< Lucid compiler (lcc)
25321369Sdim  ARM,
26321369Sdim  HP,    ///< HP compiler (xCC)
27321369Sdim  EDG,   ///< EDG compiler
28321369Sdim  GNUv3, ///< GNU C++ v3 ABI
29321369Sdim  Java,  ///< Java (gcj)
30353358Sdim  GNAT   ///< ADA compiler (gnat)
31321369Sdim};
32321369Sdimstatic cl::opt<Style>
33321369Sdim    Format("format", cl::desc("decoration style"),
34321369Sdim           cl::values(clEnumValN(Auto, "auto", "auto-detect style"),
35321369Sdim                      clEnumValN(GNU, "gnu", "GNU (itanium) style")),
36321369Sdim           cl::init(Auto));
37321369Sdimstatic cl::alias FormatShort("s", cl::desc("alias for --format"),
38321369Sdim                             cl::aliasopt(Format));
39321369Sdim
40321369Sdimstatic cl::opt<bool> StripUnderscore("strip-underscore",
41321369Sdim                                     cl::desc("strip the leading underscore"),
42321369Sdim                                     cl::init(false));
43321369Sdimstatic cl::alias StripUnderscoreShort("_",
44321369Sdim                                      cl::desc("alias for --strip-underscore"),
45321369Sdim                                      cl::aliasopt(StripUnderscore));
46360784Sdimstatic cl::opt<bool>
47360784Sdim    NoStripUnderscore("no-strip-underscore",
48360784Sdim                      cl::desc("do not strip the leading underscore"),
49360784Sdim                      cl::init(false));
50360784Sdimstatic cl::alias
51360784Sdim    NoStripUnderscoreShort("n", cl::desc("alias for --no-strip-underscore"),
52360784Sdim                           cl::aliasopt(NoStripUnderscore));
53321369Sdim
54321369Sdimstatic cl::opt<bool>
55321369Sdim    Types("types",
56321369Sdim          cl::desc("attempt to demangle types as well as function names"),
57321369Sdim          cl::init(false));
58321369Sdimstatic cl::alias TypesShort("t", cl::desc("alias for --types"),
59321369Sdim                            cl::aliasopt(Types));
60321369Sdim
61321369Sdimstatic cl::list<std::string>
62321369SdimDecorated(cl::Positional, cl::desc("<mangled>"), cl::ZeroOrMore);
63321369Sdim
64353358Sdimstatic cl::extrahelp
65353358Sdim    HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
66353358Sdim
67360784Sdimstatic bool shouldStripUnderscore() {
68360784Sdim  if (StripUnderscore)
69360784Sdim    return true;
70360784Sdim  if (NoStripUnderscore)
71360784Sdim    return false;
72360784Sdim  // If none of them are set, use the default value for platform.
73360784Sdim  // macho has symbols prefix with "_" so strip by default.
74360784Sdim  return Triple(sys::getProcessTriple()).isOSBinFormatMachO();
75360784Sdim}
76360784Sdim
77360784Sdimstatic std::string demangle(const std::string &Mangled) {
78311116Sdim  int Status;
79360784Sdim  std::string Prefix;
80321369Sdim
81353358Sdim  const char *DecoratedStr = Mangled.c_str();
82360784Sdim  if (shouldStripUnderscore())
83353358Sdim    if (DecoratedStr[0] == '_')
84353358Sdim      ++DecoratedStr;
85353358Sdim  size_t DecoratedLength = strlen(DecoratedStr);
86321369Sdim
87321369Sdim  char *Undecorated = nullptr;
88321369Sdim
89353358Sdim  if (Types ||
90353358Sdim      ((DecoratedLength >= 2 && strncmp(DecoratedStr, "_Z", 2) == 0) ||
91353358Sdim       (DecoratedLength >= 4 && strncmp(DecoratedStr, "___Z", 4) == 0)))
92353358Sdim    Undecorated = itaniumDemangle(DecoratedStr, nullptr, nullptr, &Status);
93321369Sdim
94321369Sdim  if (!Undecorated &&
95353358Sdim      (DecoratedLength > 6 && strncmp(DecoratedStr, "__imp_", 6) == 0)) {
96360784Sdim    Prefix = "import thunk for ";
97353358Sdim    Undecorated = itaniumDemangle(DecoratedStr + 6, nullptr, nullptr, &Status);
98321369Sdim  }
99321369Sdim
100360784Sdim  std::string Result(Undecorated ? Prefix + Undecorated : Mangled);
101321369Sdim  free(Undecorated);
102353358Sdim  return Result;
103311116Sdim}
104311116Sdim
105353358Sdim// Split 'Source' on any character that fails to pass 'IsLegalChar'.  The
106353358Sdim// returned vector consists of pairs where 'first' is the delimited word, and
107353358Sdim// 'second' are the delimiters following that word.
108353358Sdimstatic void SplitStringDelims(
109353358Sdim    StringRef Source,
110353358Sdim    SmallVectorImpl<std::pair<StringRef, StringRef>> &OutFragments,
111353358Sdim    function_ref<bool(char)> IsLegalChar) {
112353358Sdim  // The beginning of the input string.
113353358Sdim  const auto Head = Source.begin();
114353358Sdim
115353358Sdim  // Obtain any leading delimiters.
116353358Sdim  auto Start = std::find_if(Head, Source.end(), IsLegalChar);
117353358Sdim  if (Start != Head)
118353358Sdim    OutFragments.push_back({"", Source.slice(0, Start - Head)});
119353358Sdim
120353358Sdim  // Capture each word and the delimiters following that word.
121353358Sdim  while (Start != Source.end()) {
122353358Sdim    Start = std::find_if(Start, Source.end(), IsLegalChar);
123353358Sdim    auto End = std::find_if_not(Start, Source.end(), IsLegalChar);
124353358Sdim    auto DEnd = std::find_if(End, Source.end(), IsLegalChar);
125353358Sdim    OutFragments.push_back({Source.slice(Start - Head, End - Head),
126353358Sdim                            Source.slice(End - Head, DEnd - Head)});
127353358Sdim    Start = DEnd;
128353358Sdim  }
129353358Sdim}
130353358Sdim
131353358Sdim// This returns true if 'C' is a character that can show up in an
132353358Sdim// Itanium-mangled string.
133353358Sdimstatic bool IsLegalItaniumChar(char C) {
134353358Sdim  // Itanium CXX ABI [External Names]p5.1.1:
135353358Sdim  // '$' and '.' in mangled names are reserved for private implementations.
136353358Sdim  return isalnum(C) || C == '.' || C == '$' || C == '_';
137353358Sdim}
138353358Sdim
139353358Sdim// If 'Split' is true, then 'Mangled' is broken into individual words and each
140353358Sdim// word is demangled.  Otherwise, the entire string is treated as a single
141353358Sdim// mangled item.  The result is output to 'OS'.
142353358Sdimstatic void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) {
143353358Sdim  std::string Result;
144353358Sdim  if (Split) {
145353358Sdim    SmallVector<std::pair<StringRef, StringRef>, 16> Words;
146353358Sdim    SplitStringDelims(Mangled, Words, IsLegalItaniumChar);
147353358Sdim    for (const auto &Word : Words)
148360784Sdim      Result += ::demangle(Word.first) + Word.second.str();
149353358Sdim  } else
150360784Sdim    Result = ::demangle(Mangled);
151353358Sdim  OS << Result << '\n';
152353358Sdim  OS.flush();
153353358Sdim}
154353358Sdim
155311116Sdimint main(int argc, char **argv) {
156341825Sdim  InitLLVM X(argc, argv);
157321369Sdim
158321369Sdim  cl::ParseCommandLineOptions(argc, argv, "llvm symbol undecoration tool\n");
159321369Sdim
160321369Sdim  if (Decorated.empty())
161311116Sdim    for (std::string Mangled; std::getline(std::cin, Mangled);)
162353358Sdim      demangleLine(llvm::outs(), Mangled, true);
163311116Sdim  else
164321369Sdim    for (const auto &Symbol : Decorated)
165353358Sdim      demangleLine(llvm::outs(), Symbol, false);
166311116Sdim
167311116Sdim  return EXIT_SUCCESS;
168311116Sdim}
169