xref: /llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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-exception
6 //
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 
30 using namespace llvm;
31 using namespace object;
32 
33 namespace {
34 enum 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 OPTION
39 };
40 
41 #define OPTTABLE_STR_TABLE_CODE
42 #include "Options.inc"
43 #undef OPTTABLE_STR_TABLE_CODE
44 
45 #define OPTTABLE_PREFIXES_TABLE_CODE
46 #include "Options.inc"
47 #undef OPTTABLE_PREFIXES_TABLE_CODE
48 
49 using namespace llvm::opt;
50 static constexpr opt::OptTable::Info InfoTable[] = {
51 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
52 #include "Options.inc"
53 #undef OPTION
54 };
55 
56 class DwarfutilOptTable : public opt::GenericOptTable {
57 public:
58   DwarfutilOptTable()
59       : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
60 };
61 } // namespace
62 
63 namespace llvm {
64 namespace dwarfutil {
65 
66 std::string ToolName;
67 
68 static mc::RegisterMCTargetOptionsFlags MOF;
69 
70 static 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   else
103     Options.NumThreads = 0; // Use all available hardware threads
104 
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     else
116       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     else
128       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     else
141       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 
167 static 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 
188 static 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.BuildSeparateDebugFile
195                              ? 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 steps
216   // (i.e. it is already known that it should pass the isObject check).
217   // If the createBinary() function does not return an error, the isObject
218   // 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 
226 class raw_crc_ostream : public raw_ostream {
227 public:
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 
236 protected:
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 bytes
248   /// currently in the buffer.
249   uint64_t current_pos() const override { return OS.tell(); }
250 };
251 
252 static 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 
276 static 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 
299 static 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 
313 using DebugInfoBits = SmallString<10000>;
314 
315 static 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   } else
351     return createStringError(std::errc::invalid_argument,
352                              "unsupported file format");
353 
354   return Error::success();
355 }
356 
357 static Expected<uint32_t>
358 saveSeparateLinkedDebugInfo(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 
388 static 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 
409 static 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 
430 static 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 
445 static 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 dwarfutil
473 } // end of namespace llvm
474 
475 int 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