aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
diff options
context:
space:
mode:
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.cpp527
1 files changed, 527 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..e77c82e0fad9
--- /dev/null
+++ b/contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
@@ -0,0 +1,527 @@
+//=== 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(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class DwarfutilOptTable : public opt::OptTable {
+public:
+ DwarfutilOptTable() : OptTable(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 (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 (dyn_cast<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 (dyn_cast<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 (dyn_cast<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 (dyn_cast<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) {
+ verbose("Do garbage collection for debug info ...", Opts.Verbose);
+
+ DebugInfoBits LinkedDebugInfo;
+ raw_svector_ostream OutStream(LinkedDebugInfo);
+
+ if (linkDebugInfo(InputFile, Opts, OutStream)) {
+ if (Error Err =
+ saveLinkedDebugInfo(Opts, InputFile, std::move(LinkedDebugInfo)))
+ return Err;
+
+ return Error::success();
+ }
+
+ return createStringError(std::errc::invalid_argument,
+ "possible broken debug info");
+ } 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 = makeArrayRef(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();
+ InitializeAllAsmParsers();
+
+ 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;
+}