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
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 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
66using namespace llvm::opt;
67static constexpr opt::OptTable::Info InfoTable[] = {
68#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
69#include "Opts.inc"
70#undef OPTION
71};
72
73class SymbolizerOptTable : public opt::GenericOptTable {
74public:
75  SymbolizerOptTable() : GenericOptTable(InfoTable) {
76    setGroupedShortOptions(true);
77  }
78};
79} // namespace
80
81static std::string ToolName;
82
83static 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
91template <typename T>
92static 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
111enum class OutputStyle { LLVM, GNU, JSON };
112
113enum class Command {
114  Code,
115  Data,
116  Frame,
117};
118
119static 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
132static 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
154static Error makeStringError(StringRef Msg) {
155  return make_error<StringError>(Msg, inconvertibleErrorCode());
156}
157
158static 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
276template <typename T>
277void 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
325static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) {
326  Request SymRequest = {ModuleName, std::nullopt, StringRef()};
327  Printer.print(SymRequest, DILineInfo());
328}
329
330static 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
363static 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
372static 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
401template <typename T>
402static 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
415static 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
427static 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
438static 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.
453static 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
463int 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