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