1 //===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===//
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 // This utility works much like "addr2line". It is able of transforming
10 // tuples (module name, module offset) to code locations (function name,
11 // file, line number, column number). It is targeted for compiler-rt tools
12 // (especially AddressSanitizer and ThreadSanitizer) that can use it
13 // to symbolize stack traces in their error reports.
14 //
15 //===----------------------------------------------------------------------===//
16 
17 #include "Opts.inc"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Config/config.h"
21 #include "llvm/DebugInfo/Symbolize/DIPrinter.h"
22 #include "llvm/DebugInfo/Symbolize/Markup.h"
23 #include "llvm/DebugInfo/Symbolize/MarkupFilter.h"
24 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
25 #include "llvm/DebugInfo/Symbolize/Symbolize.h"
26 #include "llvm/Debuginfod/BuildIDFetcher.h"
27 #include "llvm/Debuginfod/Debuginfod.h"
28 #include "llvm/Debuginfod/HTTPClient.h"
29 #include "llvm/Option/Arg.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/Option.h"
32 #include "llvm/Support/COM.h"
33 #include "llvm/Support/CommandLine.h"
34 #include "llvm/Support/Debug.h"
35 #include "llvm/Support/Errc.h"
36 #include "llvm/Support/FileSystem.h"
37 #include "llvm/Support/LLVMDriver.h"
38 #include "llvm/Support/Path.h"
39 #include "llvm/Support/StringSaver.h"
40 #include "llvm/Support/WithColor.h"
41 #include "llvm/Support/raw_ostream.h"
42 #include <algorithm>
43 #include <cstdio>
44 #include <cstring>
45 #include <iostream>
46 #include <string>
47 
48 using namespace llvm;
49 using namespace symbolize;
50 
51 namespace {
52 enum ID {
53   OPT_INVALID = 0, // This is not an option ID.
54 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
55 #include "Opts.inc"
56 #undef OPTION
57 };
58 
59 #define PREFIX(NAME, VALUE)                                                    \
60   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
61   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
62                                                 std::size(NAME##_init) - 1);
63 #include "Opts.inc"
64 #undef PREFIX
65 
66 using namespace llvm::opt;
67 static constexpr opt::OptTable::Info InfoTable[] = {
68 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
69 #include "Opts.inc"
70 #undef OPTION
71 };
72 
73 class SymbolizerOptTable : public opt::GenericOptTable {
74 public:
SymbolizerOptTable()75   SymbolizerOptTable() : GenericOptTable(InfoTable) {
76     setGroupedShortOptions(true);
77   }
78 };
79 } // namespace
80 
81 static std::string ToolName;
82 
printError(const ErrorInfoBase & EI,StringRef AuxInfo)83 static void printError(const ErrorInfoBase &EI, StringRef AuxInfo) {
84   WithColor::error(errs(), ToolName);
85   if (!AuxInfo.empty())
86     errs() << "'" << AuxInfo << "': ";
87   EI.log(errs());
88   errs() << '\n';
89 }
90 
91 template <typename T>
print(const Request & Request,Expected<T> & ResOrErr,DIPrinter & Printer)92 static void print(const Request &Request, Expected<T> &ResOrErr,
93                   DIPrinter &Printer) {
94   if (ResOrErr) {
95     // No error, print the result.
96     Printer.print(Request, *ResOrErr);
97     return;
98   }
99 
100   // Handle the error.
101   bool PrintEmpty = true;
102   handleAllErrors(std::move(ResOrErr.takeError()),
103                   [&](const ErrorInfoBase &EI) {
104                     PrintEmpty = Printer.printError(Request, EI);
105                   });
106 
107   if (PrintEmpty)
108     Printer.print(Request, T());
109 }
110 
111 enum class OutputStyle { LLVM, GNU, JSON };
112 
113 enum class Command {
114   Code,
115   Data,
116   Frame,
117 };
118 
enableDebuginfod(LLVMSymbolizer & Symbolizer,const opt::ArgList & Args)119 static void enableDebuginfod(LLVMSymbolizer &Symbolizer,
120                              const opt::ArgList &Args) {
121   static bool IsEnabled = false;
122   if (IsEnabled)
123     return;
124   IsEnabled = true;
125   // Look up symbols using the debuginfod client.
126   Symbolizer.setBuildIDFetcher(std::make_unique<DebuginfodFetcher>(
127       Args.getAllArgValues(OPT_debug_file_directory_EQ)));
128   // The HTTPClient must be initialized for use by the debuginfod client.
129   HTTPClient::initialize();
130 }
131 
getSpaceDelimitedWord(StringRef & Source)132 static StringRef getSpaceDelimitedWord(StringRef &Source) {
133   const char kDelimiters[] = " \n\r";
134   const char *Pos = Source.data();
135   StringRef Result;
136   Pos += strspn(Pos, kDelimiters);
137   if (*Pos == '"' || *Pos == '\'') {
138     char Quote = *Pos;
139     Pos++;
140     const char *End = strchr(Pos, Quote);
141     if (!End)
142       return StringRef();
143     Result = StringRef(Pos, End - Pos);
144     Pos = End + 1;
145   } else {
146     int NameLength = strcspn(Pos, kDelimiters);
147     Result = StringRef(Pos, NameLength);
148     Pos += NameLength;
149   }
150   Source = StringRef(Pos, Source.end() - Pos);
151   return Result;
152 }
153 
makeStringError(StringRef Msg)154 static Error makeStringError(StringRef Msg) {
155   return make_error<StringError>(Msg, inconvertibleErrorCode());
156 }
157 
parseCommand(StringRef BinaryName,bool IsAddr2Line,StringRef InputString,Command & Cmd,std::string & ModuleName,object::BuildID & BuildID,StringRef & Symbol,uint64_t & Offset)158 static Error parseCommand(StringRef BinaryName, bool IsAddr2Line,
159                           StringRef InputString, Command &Cmd,
160                           std::string &ModuleName, object::BuildID &BuildID,
161                           StringRef &Symbol, uint64_t &Offset) {
162   ModuleName = BinaryName;
163   if (InputString.consume_front("CODE ")) {
164     Cmd = Command::Code;
165   } else if (InputString.consume_front("DATA ")) {
166     Cmd = Command::Data;
167   } else if (InputString.consume_front("FRAME ")) {
168     Cmd = Command::Frame;
169   } else {
170     // If no cmd, assume it's CODE.
171     Cmd = Command::Code;
172   }
173 
174   // Parse optional input file specification.
175   bool HasFilePrefix = false;
176   bool HasBuildIDPrefix = false;
177   while (!InputString.empty()) {
178     InputString = InputString.ltrim();
179     if (InputString.consume_front("FILE:")) {
180       if (HasFilePrefix || HasBuildIDPrefix)
181         return makeStringError("duplicate input file specification prefix");
182       HasFilePrefix = true;
183       continue;
184     }
185     if (InputString.consume_front("BUILDID:")) {
186       if (HasBuildIDPrefix || HasFilePrefix)
187         return makeStringError("duplicate input file specification prefix");
188       HasBuildIDPrefix = true;
189       continue;
190     }
191     break;
192   }
193 
194   // If an input file is not specified on the command line, try to extract it
195   // from the command.
196   if (HasBuildIDPrefix || HasFilePrefix) {
197     InputString = InputString.ltrim();
198     if (InputString.empty()) {
199       if (HasFilePrefix)
200         return makeStringError("must be followed by an input file");
201       else
202         return makeStringError("must be followed by a hash");
203     }
204 
205     if (!BinaryName.empty() || !BuildID.empty())
206       return makeStringError("input file has already been specified");
207 
208     StringRef Name = getSpaceDelimitedWord(InputString);
209     if (Name.empty())
210       return makeStringError("unbalanced quotes in input file name");
211     if (HasBuildIDPrefix) {
212       BuildID = parseBuildID(Name);
213       if (BuildID.empty())
214         return makeStringError("wrong format of build-id");
215     } else {
216       ModuleName = Name;
217     }
218   } else if (BinaryName.empty() && BuildID.empty()) {
219     // No input file has been specified. If the input string contains at least
220     // two items, assume that the first item is a file name.
221     ModuleName = getSpaceDelimitedWord(InputString);
222     if (ModuleName.empty())
223       return makeStringError("no input filename has been specified");
224   }
225 
226   // Parse address specification, which can be an offset in module or a
227   // symbol with optional offset.
228   InputString = InputString.trim();
229   if (InputString.empty())
230     return makeStringError("no module offset has been specified");
231 
232   // If input string contains a space, ignore everything after it. This behavior
233   // is consistent with GNU addr2line.
234   int AddrSpecLength = InputString.find_first_of(" \n\r");
235   StringRef AddrSpec = InputString.substr(0, AddrSpecLength);
236   bool StartsWithDigit = std::isdigit(AddrSpec.front());
237 
238   // GNU addr2line assumes the address is hexadecimal and allows a redundant
239   // "0x" or "0X" prefix; do the same for compatibility.
240   if (IsAddr2Line)
241     AddrSpec.consume_front("0x") || AddrSpec.consume_front("0X");
242 
243   // If address specification is a number, treat it as a module offset.
244   if (!AddrSpec.getAsInteger(IsAddr2Line ? 16 : 0, Offset)) {
245     // Module offset is an address.
246     Symbol = StringRef();
247     return Error::success();
248   }
249 
250   // If address specification starts with a digit, but is not a number, consider
251   // it as invalid.
252   if (StartsWithDigit || AddrSpec.empty())
253     return makeStringError("expected a number as module offset");
254 
255   // Otherwise it is a symbol name, potentially with an offset.
256   Symbol = AddrSpec;
257   Offset = 0;
258 
259   // If the address specification contains '+', try treating it as
260   // "symbol + offset".
261   size_t Plus = AddrSpec.rfind('+');
262   if (Plus != StringRef::npos) {
263     StringRef SymbolStr = AddrSpec.take_front(Plus);
264     StringRef OffsetStr = AddrSpec.substr(Plus + 1);
265     if (!SymbolStr.empty() && !OffsetStr.empty() &&
266         !OffsetStr.getAsInteger(0, Offset)) {
267       Symbol = SymbolStr;
268       return Error::success();
269     }
270     // The found '+' is not an offset delimiter.
271   }
272 
273   return Error::success();
274 }
275 
276 template <typename T>
executeCommand(StringRef ModuleName,const T & ModuleSpec,Command Cmd,StringRef Symbol,uint64_t Offset,uint64_t AdjustVMA,bool ShouldInline,OutputStyle Style,LLVMSymbolizer & Symbolizer,DIPrinter & Printer)277 void executeCommand(StringRef ModuleName, const T &ModuleSpec, Command Cmd,
278                     StringRef Symbol, uint64_t Offset, uint64_t AdjustVMA,
279                     bool ShouldInline, OutputStyle Style,
280                     LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
281   uint64_t AdjustedOffset = Offset - AdjustVMA;
282   object::SectionedAddress Address = {AdjustedOffset,
283                                       object::SectionedAddress::UndefSection};
284   Request SymRequest = {
285       ModuleName, Symbol.empty() ? std::make_optional(Offset) : std::nullopt,
286       Symbol};
287   if (Cmd == Command::Data) {
288     Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(ModuleSpec, Address);
289     print(SymRequest, ResOrErr, Printer);
290   } else if (Cmd == Command::Frame) {
291     Expected<std::vector<DILocal>> ResOrErr =
292         Symbolizer.symbolizeFrame(ModuleSpec, Address);
293     print(SymRequest, ResOrErr, Printer);
294   } else if (!Symbol.empty()) {
295     Expected<std::vector<DILineInfo>> ResOrErr =
296         Symbolizer.findSymbol(ModuleSpec, Symbol, Offset);
297     print(SymRequest, ResOrErr, Printer);
298   } else if (ShouldInline) {
299     Expected<DIInliningInfo> ResOrErr =
300         Symbolizer.symbolizeInlinedCode(ModuleSpec, Address);
301     print(SymRequest, ResOrErr, Printer);
302   } else if (Style == OutputStyle::GNU) {
303     // With PrintFunctions == FunctionNameKind::LinkageName (default)
304     // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode()
305     // may override the name of an inlined function with the name of the topmost
306     // caller function in the inlining chain. This contradicts the existing
307     // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only
308     // the topmost function, which suits our needs better.
309     Expected<DIInliningInfo> ResOrErr =
310         Symbolizer.symbolizeInlinedCode(ModuleSpec, Address);
311     Expected<DILineInfo> Res0OrErr =
312         !ResOrErr
313             ? Expected<DILineInfo>(ResOrErr.takeError())
314             : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo()
315                                                     : ResOrErr->getFrame(0));
316     print(SymRequest, Res0OrErr, Printer);
317   } else {
318     Expected<DILineInfo> ResOrErr =
319         Symbolizer.symbolizeCode(ModuleSpec, Address);
320     print(SymRequest, ResOrErr, Printer);
321   }
322   Symbolizer.pruneCache();
323 }
324 
printUnknownLineInfo(std::string ModuleName,DIPrinter & Printer)325 static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) {
326   Request SymRequest = {ModuleName, std::nullopt, StringRef()};
327   Printer.print(SymRequest, DILineInfo());
328 }
329 
symbolizeInput(const opt::InputArgList & Args,object::BuildIDRef IncomingBuildID,uint64_t AdjustVMA,bool IsAddr2Line,OutputStyle Style,StringRef InputString,LLVMSymbolizer & Symbolizer,DIPrinter & Printer)330 static void symbolizeInput(const opt::InputArgList &Args,
331                            object::BuildIDRef IncomingBuildID,
332                            uint64_t AdjustVMA, bool IsAddr2Line,
333                            OutputStyle Style, StringRef InputString,
334                            LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
335   Command Cmd;
336   std::string ModuleName;
337   object::BuildID BuildID(IncomingBuildID.begin(), IncomingBuildID.end());
338   uint64_t Offset = 0;
339   StringRef Symbol;
340   if (Error E = parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
341                              StringRef(InputString), Cmd, ModuleName, BuildID,
342                              Symbol, Offset)) {
343     handleAllErrors(std::move(E), [&](const StringError &EI) {
344       printError(EI, InputString);
345       printUnknownLineInfo(ModuleName, Printer);
346     });
347     return;
348   }
349   bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line);
350   if (!BuildID.empty()) {
351     assert(ModuleName.empty());
352     if (!Args.hasArg(OPT_no_debuginfod))
353       enableDebuginfod(Symbolizer, Args);
354     std::string BuildIDStr = toHex(BuildID);
355     executeCommand(BuildIDStr, BuildID, Cmd, Symbol, Offset, AdjustVMA,
356                    ShouldInline, Style, Symbolizer, Printer);
357   } else {
358     executeCommand(ModuleName, ModuleName, Cmd, Symbol, Offset, AdjustVMA,
359                    ShouldInline, Style, Symbolizer, Printer);
360   }
361 }
362 
printHelp(StringRef ToolName,const SymbolizerOptTable & Tbl,raw_ostream & OS)363 static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl,
364                       raw_ostream &OS) {
365   const char HelpText[] = " [options] addresses...";
366   Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(),
367                 ToolName.str().c_str());
368   // TODO Replace this with OptTable API once it adds extrahelp support.
369   OS << "\nPass @FILE as argument to read options from FILE.\n";
370 }
371 
parseOptions(int Argc,char * Argv[],bool IsAddr2Line,StringSaver & Saver,SymbolizerOptTable & Tbl)372 static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line,
373                                       StringSaver &Saver,
374                                       SymbolizerOptTable &Tbl) {
375   StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer";
376   // The environment variable specifies initial options which can be overridden
377   // by commnad line options.
378   Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS"
379                                                    : "LLVM_SYMBOLIZER_OPTS");
380   bool HasError = false;
381   opt::InputArgList Args =
382       Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
383         errs() << ("error: " + Msg + "\n");
384         HasError = true;
385       });
386   if (HasError)
387     exit(1);
388   if (Args.hasArg(OPT_help)) {
389     printHelp(ToolName, Tbl, outs());
390     exit(0);
391   }
392   if (Args.hasArg(OPT_version)) {
393     outs() << ToolName << '\n';
394     cl::PrintVersionMessage();
395     exit(0);
396   }
397 
398   return Args;
399 }
400 
401 template <typename T>
parseIntArg(const opt::InputArgList & Args,int ID,T & Value)402 static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
403   if (const opt::Arg *A = Args.getLastArg(ID)) {
404     StringRef V(A->getValue());
405     if (!llvm::to_integer(V, Value, 0)) {
406       errs() << A->getSpelling() +
407                     ": expected a non-negative integer, but got '" + V + "'";
408       exit(1);
409     }
410   } else {
411     Value = 0;
412   }
413 }
414 
decideHowToPrintFunctions(const opt::InputArgList & Args,bool IsAddr2Line)415 static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args,
416                                                   bool IsAddr2Line) {
417   if (Args.hasArg(OPT_functions))
418     return FunctionNameKind::LinkageName;
419   if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ))
420     return StringSwitch<FunctionNameKind>(A->getValue())
421         .Case("none", FunctionNameKind::None)
422         .Case("short", FunctionNameKind::ShortName)
423         .Default(FunctionNameKind::LinkageName);
424   return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName;
425 }
426 
parseColorArg(const opt::InputArgList & Args)427 static std::optional<bool> parseColorArg(const opt::InputArgList &Args) {
428   if (Args.hasArg(OPT_color))
429     return true;
430   if (const opt::Arg *A = Args.getLastArg(OPT_color_EQ))
431     return StringSwitch<std::optional<bool>>(A->getValue())
432         .Case("always", true)
433         .Case("never", false)
434         .Case("auto", std::nullopt);
435   return std::nullopt;
436 }
437 
parseBuildIDArg(const opt::InputArgList & Args,int ID)438 static object::BuildID parseBuildIDArg(const opt::InputArgList &Args, int ID) {
439   const opt::Arg *A = Args.getLastArg(ID);
440   if (!A)
441     return {};
442 
443   StringRef V(A->getValue());
444   object::BuildID BuildID = parseBuildID(V);
445   if (BuildID.empty()) {
446     errs() << A->getSpelling() + ": expected a build ID, but got '" + V + "'\n";
447     exit(1);
448   }
449   return BuildID;
450 }
451 
452 // Symbolize markup from stdin and write the result to stdout.
filterMarkup(const opt::InputArgList & Args,LLVMSymbolizer & Symbolizer)453 static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) {
454   MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args));
455   std::string InputString;
456   while (std::getline(std::cin, InputString)) {
457     InputString += '\n';
458     Filter.filter(std::move(InputString));
459   }
460   Filter.finish();
461 }
462 
llvm_symbolizer_main(int argc,char ** argv,const llvm::ToolContext &)463 int llvm_symbolizer_main(int argc, char **argv, const llvm::ToolContext &) {
464   sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded);
465 
466   ToolName = argv[0];
467   bool IsAddr2Line = sys::path::stem(ToolName).contains("addr2line");
468   BumpPtrAllocator A;
469   StringSaver Saver(A);
470   SymbolizerOptTable Tbl;
471   opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl);
472 
473   LLVMSymbolizer::Options Opts;
474   uint64_t AdjustVMA;
475   PrinterConfig Config;
476   parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA);
477   if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) {
478     Opts.PathStyle =
479         A->getOption().matches(OPT_basenames)
480             ? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly
481             : DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath;
482   } else {
483     Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath;
484   }
485   Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ);
486   Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str();
487   Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line);
488   Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str();
489   Opts.FallbackDebugPath =
490       Args.getLastArgValue(OPT_fallback_debug_path_EQ).str();
491   Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line);
492   parseIntArg(Args, OPT_print_source_context_lines_EQ,
493               Config.SourceContextLines);
494   Opts.RelativeAddresses = Args.hasArg(OPT_relative_address);
495   Opts.UntagAddresses =
496       Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line);
497   Opts.UseDIA = Args.hasArg(OPT_use_dia);
498 #if !defined(LLVM_ENABLE_DIA_SDK)
499   if (Opts.UseDIA) {
500     WithColor::warning() << "DIA not available; using native PDB reader\n";
501     Opts.UseDIA = false;
502   }
503 #endif
504   Opts.UseSymbolTable = true;
505   if (Args.hasArg(OPT_cache_size_EQ))
506     parseIntArg(Args, OPT_cache_size_EQ, Opts.MaxCacheSize);
507   Config.PrintAddress = Args.hasArg(OPT_addresses);
508   Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None;
509   Config.Pretty = Args.hasArg(OPT_pretty_print);
510   Config.Verbose = Args.hasArg(OPT_verbose);
511 
512   for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) {
513     StringRef Hint(A->getValue());
514     if (sys::path::extension(Hint) == ".dSYM") {
515       Opts.DsymHints.emplace_back(Hint);
516     } else {
517       errs() << "Warning: invalid dSYM hint: \"" << Hint
518              << "\" (must have the '.dSYM' extension).\n";
519     }
520   }
521 
522   LLVMSymbolizer Symbolizer(Opts);
523 
524   if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, canUseDebuginfod()))
525     enableDebuginfod(Symbolizer, Args);
526 
527   if (Args.hasArg(OPT_filter_markup)) {
528     filterMarkup(Args, Symbolizer);
529     return 0;
530   }
531 
532   auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM;
533   if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) {
534     if (strcmp(A->getValue(), "GNU") == 0)
535       Style = OutputStyle::GNU;
536     else if (strcmp(A->getValue(), "JSON") == 0)
537       Style = OutputStyle::JSON;
538     else
539       Style = OutputStyle::LLVM;
540   }
541 
542   if (Args.hasArg(OPT_build_id_EQ) && Args.hasArg(OPT_obj_EQ)) {
543     errs() << "error: cannot specify both --build-id and --obj\n";
544     return EXIT_FAILURE;
545   }
546   object::BuildID BuildID = parseBuildIDArg(Args, OPT_build_id_EQ);
547 
548   std::unique_ptr<DIPrinter> Printer;
549   if (Style == OutputStyle::GNU)
550     Printer = std::make_unique<GNUPrinter>(outs(), printError, Config);
551   else if (Style == OutputStyle::JSON)
552     Printer = std::make_unique<JSONPrinter>(outs(), Config);
553   else
554     Printer = std::make_unique<LLVMPrinter>(outs(), printError, Config);
555 
556   // When an input file is specified, exit immediately if the file cannot be
557   // read. If getOrCreateModuleInfo succeeds, symbolizeInput will reuse the
558   // cached file handle.
559   if (auto *Arg = Args.getLastArg(OPT_obj_EQ); Arg) {
560     auto Status = Symbolizer.getOrCreateModuleInfo(Arg->getValue());
561     if (!Status) {
562       Request SymRequest = {Arg->getValue(), 0, StringRef()};
563       handleAllErrors(Status.takeError(), [&](const ErrorInfoBase &EI) {
564         Printer->printError(SymRequest, EI);
565       });
566       return EXIT_FAILURE;
567     }
568   }
569 
570   std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT);
571   if (InputAddresses.empty()) {
572     const int kMaxInputStringLength = 1024;
573     char InputString[kMaxInputStringLength];
574 
575     while (fgets(InputString, sizeof(InputString), stdin)) {
576       // Strip newline characters.
577       std::string StrippedInputString(InputString);
578       llvm::erase_if(StrippedInputString,
579                      [](char c) { return c == '\r' || c == '\n'; });
580       symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style,
581                      StrippedInputString, Symbolizer, *Printer);
582       outs().flush();
583     }
584   } else {
585     Printer->listBegin();
586     for (StringRef Address : InputAddresses)
587       symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, Address,
588                      Symbolizer, *Printer);
589     Printer->listEnd();
590   }
591 
592   return 0;
593 }
594