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