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