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