diff options
Diffstat (limited to 'contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp | 546 | 
1 files changed, 546 insertions, 0 deletions
| diff --git a/contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp b/contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp new file mode 100644 index 000000000000..e09060abb626 --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp @@ -0,0 +1,546 @@ +//=== llvm-dwarfutil.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DebugInfoLinker.h" +#include "Error.h" +#include "Options.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; +using namespace object; + +namespace { +enum ID { +  OPT_INVALID = 0, // This is not an option ID. +#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE)                                                    \ +  static constexpr StringLiteral NAME##_init[] = VALUE;                        \ +  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \ +                                                std::size(NAME##_init) - 1); +#include "Options.inc" +#undef PREFIX + +using namespace llvm::opt; +static constexpr opt::OptTable::Info InfoTable[] = { +#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), +#include "Options.inc" +#undef OPTION +}; + +class DwarfutilOptTable : public opt::GenericOptTable { +public: +  DwarfutilOptTable() : opt::GenericOptTable(InfoTable) {} +}; +} // namespace + +namespace llvm { +namespace dwarfutil { + +std::string ToolName; + +static mc::RegisterMCTargetOptionsFlags MOF; + +static Error validateAndSetOptions(opt::InputArgList &Args, Options &Options) { +  auto UnknownArgs = Args.filtered(OPT_UNKNOWN); +  if (!UnknownArgs.empty()) +    return createStringError( +        std::errc::invalid_argument, +        formatv("unknown option: {0}", (*UnknownArgs.begin())->getSpelling()) +            .str() +            .c_str()); + +  std::vector<std::string> InputFiles = Args.getAllArgValues(OPT_INPUT); +  if (InputFiles.size() != 2) +    return createStringError( +        std::errc::invalid_argument, +        formatv("exactly two positional arguments expected, {0} provided", +                InputFiles.size()) +            .str() +            .c_str()); + +  Options.InputFileName = InputFiles[0]; +  Options.OutputFileName = InputFiles[1]; + +  Options.BuildSeparateDebugFile = +      Args.hasFlag(OPT_separate_debug_file, OPT_no_separate_debug_file, false); +  Options.DoODRDeduplication = +      Args.hasFlag(OPT_odr_deduplication, OPT_no_odr_deduplication, true); +  Options.DoGarbageCollection = +      Args.hasFlag(OPT_garbage_collection, OPT_no_garbage_collection, true); +  Options.Verbose = Args.hasArg(OPT_verbose); +  Options.Verify = Args.hasArg(OPT_verify); + +  if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) +    Options.NumThreads = atoi(NumThreads->getValue()); +  else +    Options.NumThreads = 0; // Use all available hardware threads + +  if (opt::Arg *Tombstone = Args.getLastArg(OPT_tombstone)) { +    StringRef S = Tombstone->getValue(); +    if (S == "bfd") +      Options.Tombstone = TombstoneKind::BFD; +    else if (S == "maxpc") +      Options.Tombstone = TombstoneKind::MaxPC; +    else if (S == "universal") +      Options.Tombstone = TombstoneKind::Universal; +    else if (S == "exec") +      Options.Tombstone = TombstoneKind::Exec; +    else +      return createStringError( +          std::errc::invalid_argument, +          formatv("unknown tombstone value: '{0}'", S).str().c_str()); +  } + +  if (opt::Arg *LinkerKind = Args.getLastArg(OPT_linker)) { +    StringRef S = LinkerKind->getValue(); +    if (S == "apple") +      Options.UseLLVMDWARFLinker = false; +    else if (S == "llvm") +      Options.UseLLVMDWARFLinker = true; +    else +      return createStringError( +          std::errc::invalid_argument, +          formatv("unknown linker kind value: '{0}'", S).str().c_str()); +  } + +  if (opt::Arg *BuildAccelerator = Args.getLastArg(OPT_build_accelerator)) { +    StringRef S = BuildAccelerator->getValue(); + +    if (S == "none") +      Options.AccelTableKind = DwarfUtilAccelKind::None; +    else if (S == "DWARF") +      Options.AccelTableKind = DwarfUtilAccelKind::DWARF; +    else +      return createStringError( +          std::errc::invalid_argument, +          formatv("unknown build-accelerator value: '{0}'", S).str().c_str()); +  } + +  if (Options.Verbose) { +    if (Options.NumThreads != 1 && Args.hasArg(OPT_threads)) +      warning("--num-threads set to 1 because verbose mode is specified"); + +    Options.NumThreads = 1; +  } + +  if (Options.DoODRDeduplication && Args.hasArg(OPT_odr_deduplication) && +      !Options.DoGarbageCollection) +    return createStringError( +        std::errc::invalid_argument, +        "cannot use --odr-deduplication without --garbage-collection"); + +  if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-") +    return createStringError( +        std::errc::invalid_argument, +        "unable to write to stdout when --separate-debug-file specified"); + +  return Error::success(); +} + +static Error setConfigToAddNewDebugSections(objcopy::ConfigManager &Config, +                                            ObjectFile &ObjFile) { +  // Add new debug sections. +  for (SectionRef Sec : ObjFile.sections()) { +    Expected<StringRef> SecName = Sec.getName(); +    if (!SecName) +      return SecName.takeError(); + +    if (isDebugSection(*SecName)) { +      Expected<StringRef> SecData = Sec.getContents(); +      if (!SecData) +        return SecData.takeError(); + +      Config.Common.AddSection.emplace_back(objcopy::NewSectionInfo( +          *SecName, MemoryBuffer::getMemBuffer(*SecData, *SecName, false))); +    } +  } + +  return Error::success(); +} + +static Error verifyOutput(const Options &Opts) { +  if (Opts.OutputFileName == "-") { +    warning("verification skipped because writing to stdout"); +    return Error::success(); +  } + +  std::string FileName = Opts.BuildSeparateDebugFile +                             ? Opts.getSeparateDebugFileName() +                             : Opts.OutputFileName; +  Expected<OwningBinary<Binary>> BinOrErr = createBinary(FileName); +  if (!BinOrErr) +    return createFileError(FileName, BinOrErr.takeError()); + +  if (BinOrErr->getBinary()->isObject()) { +    if (ObjectFile *Obj = static_cast<ObjectFile *>(BinOrErr->getBinary())) { +      verbose("Verifying DWARF...", Opts.Verbose); +      std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); +      DIDumpOptions DumpOpts; +      if (!DICtx->verify(Opts.Verbose ? outs() : nulls(), +                         DumpOpts.noImplicitRecursion())) +        return createFileError(FileName, +                               createError("output verification failed")); + +      return Error::success(); +    } +  } + +  // The file "FileName" was created by this utility in the previous steps +  // (i.e. it is already known that it should pass the isObject check). +  // If the createBinary() function does not return an error, the isObject +  // check should also be successful. +  llvm_unreachable( +      formatv("tool unexpectedly did not emit a supported object file: '{0}'", +              FileName) +          .str() +          .c_str()); +} + +class raw_crc_ostream : public raw_ostream { +public: +  explicit raw_crc_ostream(raw_ostream &O) : OS(O) { SetUnbuffered(); } + +  void reserveExtraSpace(uint64_t ExtraSize) override { +    OS.reserveExtraSpace(ExtraSize); +  } + +  uint32_t getCRC32() { return CRC32; } + +protected: +  raw_ostream &OS; +  uint32_t CRC32 = 0; + +  /// See raw_ostream::write_impl. +  void write_impl(const char *Ptr, size_t Size) override { +    CRC32 = crc32( +        CRC32, ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Ptr), Size)); +    OS.write(Ptr, Size); +  } + +  /// Return the current position within the stream, not counting the bytes +  /// currently in the buffer. +  uint64_t current_pos() const override { return OS.tell(); } +}; + +static Expected<uint32_t> saveSeparateDebugInfo(const Options &Opts, +                                                ObjectFile &InputFile) { +  objcopy::ConfigManager Config; +  std::string OutputFilename = Opts.getSeparateDebugFileName(); +  Config.Common.InputFilename = Opts.InputFileName; +  Config.Common.OutputFilename = OutputFilename; +  Config.Common.OnlyKeepDebug = true; +  uint32_t WrittenFileCRC32 = 0; + +  if (Error Err = writeToOutput( +          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { +            raw_crc_ostream CRCBuffer(OutFile); +            if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile, +                                                            CRCBuffer)) +              return Err; + +            WrittenFileCRC32 = CRCBuffer.getCRC32(); +            return Error::success(); +          })) +    return std::move(Err); + +  return WrittenFileCRC32; +} + +static Error saveNonDebugInfo(const Options &Opts, ObjectFile &InputFile, +                              uint32_t GnuDebugLinkCRC32) { +  objcopy::ConfigManager Config; +  Config.Common.InputFilename = Opts.InputFileName; +  Config.Common.OutputFilename = Opts.OutputFileName; +  Config.Common.StripDebug = true; +  std::string SeparateDebugFileName = Opts.getSeparateDebugFileName(); +  Config.Common.AddGnuDebugLink = sys::path::filename(SeparateDebugFileName); +  Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32; + +  if (Error Err = writeToOutput( +          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { +            if (Error Err = +                    objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile)) +              return Err; + +            return Error::success(); +          })) +    return Err; + +  return Error::success(); +} + +static Error splitDebugIntoSeparateFile(const Options &Opts, +                                        ObjectFile &InputFile) { +  Expected<uint32_t> SeparateDebugFileCRC32OrErr = +      saveSeparateDebugInfo(Opts, InputFile); +  if (!SeparateDebugFileCRC32OrErr) +    return SeparateDebugFileCRC32OrErr.takeError(); + +  if (Error Err = +          saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr)) +    return Err; + +  return Error::success(); +} + +using DebugInfoBits = SmallString<10000>; + +static Error addSectionsFromLinkedData(objcopy::ConfigManager &Config, +                                       ObjectFile &InputFile, +                                       DebugInfoBits &LinkedDebugInfoBits) { +  if (isa<ELFObjectFile<ELF32LE>>(&InputFile)) { +    Expected<ELFObjectFile<ELF32LE>> MemFile = ELFObjectFile<ELF32LE>::create( +        MemoryBufferRef(LinkedDebugInfoBits, "")); +    if (!MemFile) +      return MemFile.takeError(); + +    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) +      return Err; +  } else if (isa<ELFObjectFile<ELF64LE>>(&InputFile)) { +    Expected<ELFObjectFile<ELF64LE>> MemFile = ELFObjectFile<ELF64LE>::create( +        MemoryBufferRef(LinkedDebugInfoBits, "")); +    if (!MemFile) +      return MemFile.takeError(); + +    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) +      return Err; +  } else if (isa<ELFObjectFile<ELF32BE>>(&InputFile)) { +    Expected<ELFObjectFile<ELF32BE>> MemFile = ELFObjectFile<ELF32BE>::create( +        MemoryBufferRef(LinkedDebugInfoBits, "")); +    if (!MemFile) +      return MemFile.takeError(); + +    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) +      return Err; +  } else if (isa<ELFObjectFile<ELF64BE>>(&InputFile)) { +    Expected<ELFObjectFile<ELF64BE>> MemFile = ELFObjectFile<ELF64BE>::create( +        MemoryBufferRef(LinkedDebugInfoBits, "")); +    if (!MemFile) +      return MemFile.takeError(); + +    if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) +      return Err; +  } else +    return createStringError(std::errc::invalid_argument, +                             "unsupported file format"); + +  return Error::success(); +} + +static Expected<uint32_t> +saveSeparateLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile, +                            DebugInfoBits LinkedDebugInfoBits) { +  objcopy::ConfigManager Config; +  std::string OutputFilename = Opts.getSeparateDebugFileName(); +  Config.Common.InputFilename = Opts.InputFileName; +  Config.Common.OutputFilename = OutputFilename; +  Config.Common.StripDebug = true; +  Config.Common.OnlyKeepDebug = true; +  uint32_t WrittenFileCRC32 = 0; + +  if (Error Err = +          addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits)) +    return std::move(Err); + +  if (Error Err = writeToOutput( +          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { +            raw_crc_ostream CRCBuffer(OutFile); + +            if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile, +                                                            CRCBuffer)) +              return Err; + +            WrittenFileCRC32 = CRCBuffer.getCRC32(); +            return Error::success(); +          })) +    return std::move(Err); + +  return WrittenFileCRC32; +} + +static Error saveSingleLinkedDebugInfo(const Options &Opts, +                                       ObjectFile &InputFile, +                                       DebugInfoBits LinkedDebugInfoBits) { +  objcopy::ConfigManager Config; + +  Config.Common.InputFilename = Opts.InputFileName; +  Config.Common.OutputFilename = Opts.OutputFileName; +  Config.Common.StripDebug = true; +  if (Error Err = +          addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits)) +    return Err; + +  if (Error Err = writeToOutput( +          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { +            return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile); +          })) +    return Err; + +  return Error::success(); +} + +static Error saveLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile, +                                 DebugInfoBits LinkedDebugInfoBits) { +  if (Opts.BuildSeparateDebugFile) { +    Expected<uint32_t> SeparateDebugFileCRC32OrErr = +        saveSeparateLinkedDebugInfo(Opts, InputFile, +                                    std::move(LinkedDebugInfoBits)); +    if (!SeparateDebugFileCRC32OrErr) +      return SeparateDebugFileCRC32OrErr.takeError(); + +    if (Error Err = +            saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr)) +      return Err; +  } else { +    if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile, +                                              std::move(LinkedDebugInfoBits))) +      return Err; +  } + +  return Error::success(); +} + +static Error saveCopyOfFile(const Options &Opts, ObjectFile &InputFile) { +  objcopy::ConfigManager Config; + +  Config.Common.InputFilename = Opts.InputFileName; +  Config.Common.OutputFilename = Opts.OutputFileName; + +  if (Error Err = writeToOutput( +          Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { +            return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile); +          })) +    return Err; + +  return Error::success(); +} + +static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) { +  if (Opts.DoGarbageCollection || +      Opts.AccelTableKind != DwarfUtilAccelKind::None) { +    verbose("Do debug info linking...", Opts.Verbose); + +    DebugInfoBits LinkedDebugInfo; +    raw_svector_ostream OutStream(LinkedDebugInfo); + +    if (Error Err = linkDebugInfo(InputFile, Opts, OutStream)) +      return Err; + +    if (Error Err = +            saveLinkedDebugInfo(Opts, InputFile, std::move(LinkedDebugInfo))) +      return Err; + +    return Error::success(); +  } else if (Opts.BuildSeparateDebugFile) { +    if (Error Err = splitDebugIntoSeparateFile(Opts, InputFile)) +      return Err; +  } else { +    if (Error Err = saveCopyOfFile(Opts, InputFile)) +      return Err; +  } + +  return Error::success(); +} + +} // end of namespace dwarfutil +} // end of namespace llvm + +int main(int Argc, char const *Argv[]) { +  using namespace dwarfutil; + +  InitLLVM X(Argc, Argv); +  ToolName = Argv[0]; + +  // Parse arguments. +  DwarfutilOptTable T; +  unsigned MAI; +  unsigned MAC; +  ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1); +  opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); + +  if (Args.hasArg(OPT_help) || Args.size() == 0) { +    T.printHelp( +        outs(), (ToolName + " [options] <input file> <output file>").c_str(), +        "llvm-dwarfutil is a tool to copy and manipulate debug info", false); +    return EXIT_SUCCESS; +  } + +  if (Args.hasArg(OPT_version)) { +    cl::PrintVersionMessage(); +    return EXIT_SUCCESS; +  } + +  Options Opts; +  if (Error Err = validateAndSetOptions(Args, Opts)) +    error(std::move(Err), dwarfutil::ToolName); + +  InitializeAllTargets(); +  InitializeAllTargetMCs(); +  InitializeAllTargetInfos(); +  InitializeAllAsmPrinters(); + +  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = +      MemoryBuffer::getFileOrSTDIN(Opts.InputFileName); +  if (BuffOrErr.getError()) +    error(createFileError(Opts.InputFileName, BuffOrErr.getError())); + +  Expected<std::unique_ptr<Binary>> BinOrErr = +      object::createBinary(**BuffOrErr); +  if (!BinOrErr) +    error(createFileError(Opts.InputFileName, BinOrErr.takeError())); + +  Expected<FilePermissionsApplier> PermsApplierOrErr = +      FilePermissionsApplier::create(Opts.InputFileName); +  if (!PermsApplierOrErr) +    error(createFileError(Opts.InputFileName, PermsApplierOrErr.takeError())); + +  if (!(*BinOrErr)->isObject()) +    error(createFileError(Opts.InputFileName, +                          createError("unsupported input file"))); + +  if (Error Err = +          applyCLOptions(Opts, *static_cast<ObjectFile *>((*BinOrErr).get()))) +    error(createFileError(Opts.InputFileName, std::move(Err))); + +  BinOrErr->reset(); +  BuffOrErr->reset(); + +  if (Error Err = PermsApplierOrErr->apply(Opts.OutputFileName)) +    error(std::move(Err)); + +  if (Opts.BuildSeparateDebugFile) +    if (Error Err = PermsApplierOrErr->apply(Opts.getSeparateDebugFileName())) +      error(std::move(Err)); + +  if (Opts.Verify) { +    if (Error Err = verifyOutput(Opts)) +      error(std::move(Err)); +  } + +  return EXIT_SUCCESS; +} | 
