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