brintos

brintos / llvm-project-archived public Read only

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