brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.9 KiB · 0180abb Raw
549 lines · cpp
1//=== llvm-dwarfutil.cpp --------------------------------------------------===//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#include "DebugInfoLinker.h"10#include "Error.h"11#include "Options.h"12#include "llvm/DebugInfo/DWARF/DWARFContext.h"13#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"14#include "llvm/MC/MCTargetOptionsCommandFlags.h"15#include "llvm/ObjCopy/CommonConfig.h"16#include "llvm/ObjCopy/ConfigManager.h"17#include "llvm/ObjCopy/ObjCopy.h"18#include "llvm/Option/Arg.h"19#include "llvm/Option/ArgList.h"20#include "llvm/Option/Option.h"21#include "llvm/Support/CRC.h"22#include "llvm/Support/CommandLine.h"23#include "llvm/Support/FileUtilities.h"24#include "llvm/Support/InitLLVM.h"25#include "llvm/Support/PrettyStackTrace.h"26#include "llvm/Support/Process.h"27#include "llvm/Support/Signals.h"28#include "llvm/Support/TargetSelect.h"29 30using namespace llvm;31using namespace object;32 33namespace {34enum ID {35  OPT_INVALID = 0, // This is not an option ID.36#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),37#include "Options.inc"38#undef OPTION39};40 41#define OPTTABLE_STR_TABLE_CODE42#include "Options.inc"43#undef OPTTABLE_STR_TABLE_CODE44 45#define OPTTABLE_PREFIXES_TABLE_CODE46#include "Options.inc"47#undef OPTTABLE_PREFIXES_TABLE_CODE48 49using namespace llvm::opt;50static constexpr opt::OptTable::Info InfoTable[] = {51#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),52#include "Options.inc"53#undef OPTION54};55 56class DwarfutilOptTable : public opt::GenericOptTable {57public:58  DwarfutilOptTable()59      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}60};61} // namespace62 63namespace llvm {64namespace dwarfutil {65 66std::string ToolName;67 68static mc::RegisterMCTargetOptionsFlags MOF;69 70static Error validateAndSetOptions(opt::InputArgList &Args, Options &Options) {71  auto UnknownArgs = Args.filtered(OPT_UNKNOWN);72  if (!UnknownArgs.empty())73    return createStringError(74        std::errc::invalid_argument,75        formatv("unknown option: {0}", (*UnknownArgs.begin())->getSpelling())76            .str()77            .c_str());78 79  std::vector<std::string> InputFiles = Args.getAllArgValues(OPT_INPUT);80  if (InputFiles.size() != 2)81    return createStringError(82        std::errc::invalid_argument,83        formatv("exactly two positional arguments expected, {0} provided",84                InputFiles.size())85            .str()86            .c_str());87 88  Options.InputFileName = InputFiles[0];89  Options.OutputFileName = InputFiles[1];90 91  Options.BuildSeparateDebugFile =92      Args.hasFlag(OPT_separate_debug_file, OPT_no_separate_debug_file, false);93  Options.DoODRDeduplication =94      Args.hasFlag(OPT_odr_deduplication, OPT_no_odr_deduplication, true);95  Options.DoGarbageCollection =96      Args.hasFlag(OPT_garbage_collection, OPT_no_garbage_collection, true);97  Options.Verbose = Args.hasArg(OPT_verbose);98  Options.Verify = Args.hasArg(OPT_verify);99 100  if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))101    Options.NumThreads = atoi(NumThreads->getValue());102  else103    Options.NumThreads = 0; // Use all available hardware threads104 105  if (opt::Arg *Tombstone = Args.getLastArg(OPT_tombstone)) {106    StringRef S = Tombstone->getValue();107    if (S == "bfd")108      Options.Tombstone = TombstoneKind::BFD;109    else if (S == "maxpc")110      Options.Tombstone = TombstoneKind::MaxPC;111    else if (S == "universal")112      Options.Tombstone = TombstoneKind::Universal;113    else if (S == "exec")114      Options.Tombstone = TombstoneKind::Exec;115    else116      return createStringError(117          std::errc::invalid_argument,118          formatv("unknown tombstone value: '{0}'", S).str().c_str());119  }120 121  if (opt::Arg *LinkerKind = Args.getLastArg(OPT_linker)) {122    StringRef S = LinkerKind->getValue();123    if (S == "classic")124      Options.UseDWARFLinkerParallel = false;125    else if (S == "parallel")126      Options.UseDWARFLinkerParallel = true;127    else128      return createStringError(129          std::errc::invalid_argument,130          formatv("unknown linker kind value: '{0}'", S).str().c_str());131  }132 133  if (opt::Arg *BuildAccelerator = Args.getLastArg(OPT_build_accelerator)) {134    StringRef S = BuildAccelerator->getValue();135 136    if (S == "none")137      Options.AccelTableKind = DwarfUtilAccelKind::None;138    else if (S == "DWARF")139      Options.AccelTableKind = DwarfUtilAccelKind::DWARF;140    else141      return createStringError(142          std::errc::invalid_argument,143          formatv("unknown build-accelerator value: '{0}'", S).str().c_str());144  }145 146  if (Options.Verbose) {147    if (Options.NumThreads != 1 && Args.hasArg(OPT_threads))148      warning("--num-threads set to 1 because verbose mode is specified");149 150    Options.NumThreads = 1;151  }152 153  if (Options.DoODRDeduplication && Args.hasArg(OPT_odr_deduplication) &&154      !Options.DoGarbageCollection)155    return createStringError(156        std::errc::invalid_argument,157        "cannot use --odr-deduplication without --garbage-collection");158 159  if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-")160    return createStringError(161        std::errc::invalid_argument,162        "unable to write to stdout when --separate-debug-file specified");163 164  return Error::success();165}166 167static Error setConfigToAddNewDebugSections(objcopy::ConfigManager &Config,168                                            ObjectFile &ObjFile) {169  // Add new debug sections.170  for (SectionRef Sec : ObjFile.sections()) {171    Expected<StringRef> SecName = Sec.getName();172    if (!SecName)173      return SecName.takeError();174 175    if (isDebugSection(*SecName)) {176      Expected<StringRef> SecData = Sec.getContents();177      if (!SecData)178        return SecData.takeError();179 180      Config.Common.AddSection.emplace_back(objcopy::NewSectionInfo(181          *SecName, MemoryBuffer::getMemBuffer(*SecData, *SecName, false)));182    }183  }184 185  return Error::success();186}187 188static Error verifyOutput(const Options &Opts) {189  if (Opts.OutputFileName == "-") {190    warning("verification skipped because writing to stdout");191    return Error::success();192  }193 194  std::string FileName = Opts.BuildSeparateDebugFile195                             ? Opts.getSeparateDebugFileName()196                             : Opts.OutputFileName;197  Expected<OwningBinary<Binary>> BinOrErr = createBinary(FileName);198  if (!BinOrErr)199    return createFileError(FileName, BinOrErr.takeError());200 201  if (BinOrErr->getBinary()->isObject()) {202    if (ObjectFile *Obj = static_cast<ObjectFile *>(BinOrErr->getBinary())) {203      verbose("Verifying DWARF...", Opts.Verbose);204      std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);205      DIDumpOptions DumpOpts;206      if (!DICtx->verify(Opts.Verbose ? outs() : nulls(),207                         DumpOpts.noImplicitRecursion()))208        return createFileError(FileName,209                               createError("output verification failed"));210 211      return Error::success();212    }213  }214 215  // The file "FileName" was created by this utility in the previous steps216  // (i.e. it is already known that it should pass the isObject check).217  // If the createBinary() function does not return an error, the isObject218  // check should also be successful.219  llvm_unreachable(220      formatv("tool unexpectedly did not emit a supported object file: '{0}'",221              FileName)222          .str()223          .c_str());224}225 226class raw_crc_ostream : public raw_ostream {227public:228  explicit raw_crc_ostream(raw_ostream &O) : OS(O) { SetUnbuffered(); }229 230  void reserveExtraSpace(uint64_t ExtraSize) override {231    OS.reserveExtraSpace(ExtraSize);232  }233 234  uint32_t getCRC32() { return CRC32; }235 236protected:237  raw_ostream &OS;238  uint32_t CRC32 = 0;239 240  /// See raw_ostream::write_impl.241  void write_impl(const char *Ptr, size_t Size) override {242    CRC32 = crc32(243        CRC32, ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Ptr), Size));244    OS.write(Ptr, Size);245  }246 247  /// Return the current position within the stream, not counting the bytes248  /// currently in the buffer.249  uint64_t current_pos() const override { return OS.tell(); }250};251 252static Expected<uint32_t> saveSeparateDebugInfo(const Options &Opts,253                                                ObjectFile &InputFile) {254  objcopy::ConfigManager Config;255  std::string OutputFilename = Opts.getSeparateDebugFileName();256  Config.Common.InputFilename = Opts.InputFileName;257  Config.Common.OutputFilename = OutputFilename;258  Config.Common.OnlyKeepDebug = true;259  uint32_t WrittenFileCRC32 = 0;260 261  if (Error Err = writeToOutput(262          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {263            raw_crc_ostream CRCBuffer(OutFile);264            if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile,265                                                            CRCBuffer))266              return Err;267 268            WrittenFileCRC32 = CRCBuffer.getCRC32();269            return Error::success();270          }))271    return std::move(Err);272 273  return WrittenFileCRC32;274}275 276static Error saveNonDebugInfo(const Options &Opts, ObjectFile &InputFile,277                              uint32_t GnuDebugLinkCRC32) {278  objcopy::ConfigManager Config;279  Config.Common.InputFilename = Opts.InputFileName;280  Config.Common.OutputFilename = Opts.OutputFileName;281  Config.Common.StripDebug = true;282  std::string SeparateDebugFileName = Opts.getSeparateDebugFileName();283  Config.Common.AddGnuDebugLink = sys::path::filename(SeparateDebugFileName);284  Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32;285 286  if (Error Err = writeToOutput(287          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {288            if (Error Err =289                    objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile))290              return Err;291 292            return Error::success();293          }))294    return Err;295 296  return Error::success();297}298 299static Error splitDebugIntoSeparateFile(const Options &Opts,300                                        ObjectFile &InputFile) {301  Expected<uint32_t> SeparateDebugFileCRC32OrErr =302      saveSeparateDebugInfo(Opts, InputFile);303  if (!SeparateDebugFileCRC32OrErr)304    return SeparateDebugFileCRC32OrErr.takeError();305 306  if (Error Err =307          saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr))308    return Err;309 310  return Error::success();311}312 313using DebugInfoBits = SmallString<10000>;314 315static Error addSectionsFromLinkedData(objcopy::ConfigManager &Config,316                                       ObjectFile &InputFile,317                                       DebugInfoBits &LinkedDebugInfoBits) {318  if (isa<ELFObjectFile<ELF32LE>>(&InputFile)) {319    Expected<ELFObjectFile<ELF32LE>> MemFile = ELFObjectFile<ELF32LE>::create(320        MemoryBufferRef(LinkedDebugInfoBits, ""));321    if (!MemFile)322      return MemFile.takeError();323 324    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))325      return Err;326  } else if (isa<ELFObjectFile<ELF64LE>>(&InputFile)) {327    Expected<ELFObjectFile<ELF64LE>> MemFile = ELFObjectFile<ELF64LE>::create(328        MemoryBufferRef(LinkedDebugInfoBits, ""));329    if (!MemFile)330      return MemFile.takeError();331 332    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))333      return Err;334  } else if (isa<ELFObjectFile<ELF32BE>>(&InputFile)) {335    Expected<ELFObjectFile<ELF32BE>> MemFile = ELFObjectFile<ELF32BE>::create(336        MemoryBufferRef(LinkedDebugInfoBits, ""));337    if (!MemFile)338      return MemFile.takeError();339 340    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))341      return Err;342  } else if (isa<ELFObjectFile<ELF64BE>>(&InputFile)) {343    Expected<ELFObjectFile<ELF64BE>> MemFile = ELFObjectFile<ELF64BE>::create(344        MemoryBufferRef(LinkedDebugInfoBits, ""));345    if (!MemFile)346      return MemFile.takeError();347 348    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))349      return Err;350  } else351    return createStringError(std::errc::invalid_argument,352                             "unsupported file format");353 354  return Error::success();355}356 357static Expected<uint32_t>358saveSeparateLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile,359                            DebugInfoBits LinkedDebugInfoBits) {360  objcopy::ConfigManager Config;361  std::string OutputFilename = Opts.getSeparateDebugFileName();362  Config.Common.InputFilename = Opts.InputFileName;363  Config.Common.OutputFilename = OutputFilename;364  Config.Common.StripDebug = true;365  Config.Common.OnlyKeepDebug = true;366  uint32_t WrittenFileCRC32 = 0;367 368  if (Error Err =369          addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits))370    return std::move(Err);371 372  if (Error Err = writeToOutput(373          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {374            raw_crc_ostream CRCBuffer(OutFile);375 376            if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile,377                                                            CRCBuffer))378              return Err;379 380            WrittenFileCRC32 = CRCBuffer.getCRC32();381            return Error::success();382          }))383    return std::move(Err);384 385  return WrittenFileCRC32;386}387 388static Error saveSingleLinkedDebugInfo(const Options &Opts,389                                       ObjectFile &InputFile,390                                       DebugInfoBits LinkedDebugInfoBits) {391  objcopy::ConfigManager Config;392 393  Config.Common.InputFilename = Opts.InputFileName;394  Config.Common.OutputFilename = Opts.OutputFileName;395  Config.Common.StripDebug = true;396  if (Error Err =397          addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits))398    return Err;399 400  if (Error Err = writeToOutput(401          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {402            return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile);403          }))404    return Err;405 406  return Error::success();407}408 409static Error saveLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile,410                                 DebugInfoBits LinkedDebugInfoBits) {411  if (Opts.BuildSeparateDebugFile) {412    Expected<uint32_t> SeparateDebugFileCRC32OrErr =413        saveSeparateLinkedDebugInfo(Opts, InputFile,414                                    std::move(LinkedDebugInfoBits));415    if (!SeparateDebugFileCRC32OrErr)416      return SeparateDebugFileCRC32OrErr.takeError();417 418    if (Error Err =419            saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr))420      return Err;421  } else {422    if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile,423                                              std::move(LinkedDebugInfoBits)))424      return Err;425  }426 427  return Error::success();428}429 430static Error saveCopyOfFile(const Options &Opts, ObjectFile &InputFile) {431  objcopy::ConfigManager Config;432 433  Config.Common.InputFilename = Opts.InputFileName;434  Config.Common.OutputFilename = Opts.OutputFileName;435 436  if (Error Err = writeToOutput(437          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {438            return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile);439          }))440    return Err;441 442  return Error::success();443}444 445static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) {446  if (Opts.DoGarbageCollection ||447      Opts.AccelTableKind != DwarfUtilAccelKind::None) {448    verbose("Do debug info linking...", Opts.Verbose);449 450    DebugInfoBits LinkedDebugInfo;451    raw_svector_ostream OutStream(LinkedDebugInfo);452 453    if (Error Err = linkDebugInfo(InputFile, Opts, OutStream))454      return Err;455 456    if (Error Err =457            saveLinkedDebugInfo(Opts, InputFile, std::move(LinkedDebugInfo)))458      return Err;459 460    return Error::success();461  } else if (Opts.BuildSeparateDebugFile) {462    if (Error Err = splitDebugIntoSeparateFile(Opts, InputFile))463      return Err;464  } else {465    if (Error Err = saveCopyOfFile(Opts, InputFile))466      return Err;467  }468 469  return Error::success();470}471 472} // end of namespace dwarfutil473} // end of namespace llvm474 475int main(int Argc, char const *Argv[]) {476  using namespace dwarfutil;477 478  InitLLVM X(Argc, Argv);479  ToolName = Argv[0];480 481  // Parse arguments.482  DwarfutilOptTable T;483  unsigned MAI;484  unsigned MAC;485  ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1);486  opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);487 488  if (Args.hasArg(OPT_help) || Args.size() == 0) {489    T.printHelp(490        outs(), (ToolName + " [options] <input file> <output file>").c_str(),491        "llvm-dwarfutil is a tool to copy and manipulate debug info", false);492    return EXIT_SUCCESS;493  }494 495  if (Args.hasArg(OPT_version)) {496    cl::PrintVersionMessage();497    return EXIT_SUCCESS;498  }499 500  Options Opts;501  if (Error Err = validateAndSetOptions(Args, Opts))502    error(std::move(Err), dwarfutil::ToolName);503 504  InitializeAllTargets();505  InitializeAllTargetMCs();506  InitializeAllTargetInfos();507  InitializeAllAsmPrinters();508 509  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =510      MemoryBuffer::getFileOrSTDIN(Opts.InputFileName);511  if (BuffOrErr.getError())512    error(createFileError(Opts.InputFileName, BuffOrErr.getError()));513 514  Expected<std::unique_ptr<Binary>> BinOrErr =515      object::createBinary(**BuffOrErr);516  if (!BinOrErr)517    error(createFileError(Opts.InputFileName, BinOrErr.takeError()));518 519  Expected<FilePermissionsApplier> PermsApplierOrErr =520      FilePermissionsApplier::create(Opts.InputFileName);521  if (!PermsApplierOrErr)522    error(createFileError(Opts.InputFileName, PermsApplierOrErr.takeError()));523 524  if (!(*BinOrErr)->isObject())525    error(createFileError(Opts.InputFileName,526                          createError("unsupported input file")));527 528  if (Error Err =529          applyCLOptions(Opts, *static_cast<ObjectFile *>((*BinOrErr).get())))530    error(createFileError(Opts.InputFileName, std::move(Err)));531 532  BinOrErr->reset();533  BuffOrErr->reset();534 535  if (Error Err = PermsApplierOrErr->apply(Opts.OutputFileName))536    error(std::move(Err));537 538  if (Opts.BuildSeparateDebugFile)539    if (Error Err = PermsApplierOrErr->apply(Opts.getSeparateDebugFileName()))540      error(std::move(Err));541 542  if (Opts.Verify) {543    if (Error Err = verifyOutput(Opts))544      error(std::move(Err));545  }546 547  return EXIT_SUCCESS;548}549