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
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 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
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 OPTION
54};
55
56class DwarfutilOptTable : public opt::GenericOptTable {
57public:
58 DwarfutilOptTable()
59 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
60};
61} // namespace
62
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(Ids: OPT_UNKNOWN);
72 if (!UnknownArgs.empty())
73 return createStringError(
74 EC: std::errc::invalid_argument,
75 Fmt: formatv(Fmt: "unknown option: {0}", Vals: (*UnknownArgs.begin())->getSpelling())
76 .str()
77 .c_str());
78
79 std::vector<std::string> InputFiles = Args.getAllArgValues(Id: OPT_INPUT);
80 if (InputFiles.size() != 2)
81 return createStringError(
82 EC: std::errc::invalid_argument,
83 Fmt: formatv(Fmt: "exactly two positional arguments expected, {0} provided",
84 Vals: 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(Pos: OPT_separate_debug_file, Neg: OPT_no_separate_debug_file, Default: false);
93 Options.DoODRDeduplication =
94 Args.hasFlag(Pos: OPT_odr_deduplication, Neg: OPT_no_odr_deduplication, Default: true);
95 Options.DoGarbageCollection =
96 Args.hasFlag(Pos: OPT_garbage_collection, Neg: OPT_no_garbage_collection, Default: true);
97 Options.Verbose = Args.hasArg(Ids: OPT_verbose);
98 Options.Verify = Args.hasArg(Ids: OPT_verify);
99
100 if (opt::Arg *NumThreads = Args.getLastArg(Ids: OPT_threads))
101 Options.NumThreads = atoi(nptr: NumThreads->getValue());
102 else
103 Options.NumThreads = 0; // Use all available hardware threads
104
105 if (opt::Arg *Tombstone = Args.getLastArg(Ids: 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 EC: std::errc::invalid_argument,
118 Fmt: formatv(Fmt: "unknown tombstone value: '{0}'", Vals&: S).str().c_str());
119 }
120
121 if (opt::Arg *LinkerKind = Args.getLastArg(Ids: 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 EC: std::errc::invalid_argument,
130 Fmt: formatv(Fmt: "unknown linker kind value: '{0}'", Vals&: S).str().c_str());
131 }
132
133 if (opt::Arg *BuildAccelerator = Args.getLastArg(Ids: 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 EC: std::errc::invalid_argument,
143 Fmt: formatv(Fmt: "unknown build-accelerator value: '{0}'", Vals&: S).str().c_str());
144 }
145
146 if (Options.Verbose) {
147 if (Options.NumThreads != 1 && Args.hasArg(Ids: OPT_threads))
148 warning(Message: "--num-threads set to 1 because verbose mode is specified");
149
150 Options.NumThreads = 1;
151 }
152
153 if (Options.DoODRDeduplication && Args.hasArg(Ids: OPT_odr_deduplication) &&
154 !Options.DoGarbageCollection)
155 return createStringError(
156 EC: std::errc::invalid_argument,
157 Fmt: "cannot use --odr-deduplication without --garbage-collection");
158
159 if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-")
160 return createStringError(
161 EC: std::errc::invalid_argument,
162 Fmt: "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: *SecName)) {
176 Expected<StringRef> SecData = Sec.getContents();
177 if (!SecData)
178 return SecData.takeError();
179
180 Config.Common.AddSection.emplace_back(Args: objcopy::NewSectionInfo(
181 *SecName, MemoryBuffer::getMemBuffer(InputData: *SecData, BufferName: *SecName, RequiresNullTerminator: false)));
182 }
183 }
184
185 return Error::success();
186}
187
188static Error verifyOutput(const Options &Opts) {
189 if (Opts.OutputFileName == "-") {
190 warning(Message: "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(Path: FileName);
198 if (!BinOrErr)
199 return createFileError(F: FileName, E: BinOrErr.takeError());
200
201 if (BinOrErr->getBinary()->isObject()) {
202 if (ObjectFile *Obj = static_cast<ObjectFile *>(BinOrErr->getBinary())) {
203 verbose(Message: "Verifying DWARF...", Verbose: Opts.Verbose);
204 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(Obj: *Obj);
205 DIDumpOptions DumpOpts;
206 if (!DICtx->verify(OS&: Opts.Verbose ? outs() : nulls(),
207 DumpOpts: DumpOpts.noImplicitRecursion()))
208 return createFileError(F: FileName,
209 E: createError(Err: "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
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 CRC: CRC32, Data: 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
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 OutputFileName: Config.Common.OutputFilename, Write: [&](raw_ostream &OutFile) -> Error {
263 raw_crc_ostream CRCBuffer(OutFile);
264 if (Error Err = objcopy::executeObjcopyOnBinary(Config, In&: InputFile,
265 Out&: 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(path: SeparateDebugFileName);
284 Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32;
285
286 if (Error Err = writeToOutput(
287 OutputFileName: Config.Common.OutputFilename, Write: [&](raw_ostream &OutFile) -> Error {
288 if (Error Err =
289 objcopy::executeObjcopyOnBinary(Config, In&: InputFile, Out&: 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, GnuDebugLinkCRC32: *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>>(Val: &InputFile)) {
319 Expected<ELFObjectFile<ELF32LE>> MemFile = ELFObjectFile<ELF32LE>::create(
320 Object: MemoryBufferRef(LinkedDebugInfoBits, ""));
321 if (!MemFile)
322 return MemFile.takeError();
323
324 if (Error Err = setConfigToAddNewDebugSections(Config, ObjFile&: *MemFile))
325 return Err;
326 } else if (isa<ELFObjectFile<ELF64LE>>(Val: &InputFile)) {
327 Expected<ELFObjectFile<ELF64LE>> MemFile = ELFObjectFile<ELF64LE>::create(
328 Object: MemoryBufferRef(LinkedDebugInfoBits, ""));
329 if (!MemFile)
330 return MemFile.takeError();
331
332 if (Error Err = setConfigToAddNewDebugSections(Config, ObjFile&: *MemFile))
333 return Err;
334 } else if (isa<ELFObjectFile<ELF32BE>>(Val: &InputFile)) {
335 Expected<ELFObjectFile<ELF32BE>> MemFile = ELFObjectFile<ELF32BE>::create(
336 Object: MemoryBufferRef(LinkedDebugInfoBits, ""));
337 if (!MemFile)
338 return MemFile.takeError();
339
340 if (Error Err = setConfigToAddNewDebugSections(Config, ObjFile&: *MemFile))
341 return Err;
342 } else if (isa<ELFObjectFile<ELF64BE>>(Val: &InputFile)) {
343 Expected<ELFObjectFile<ELF64BE>> MemFile = ELFObjectFile<ELF64BE>::create(
344 Object: MemoryBufferRef(LinkedDebugInfoBits, ""));
345 if (!MemFile)
346 return MemFile.takeError();
347
348 if (Error Err = setConfigToAddNewDebugSections(Config, ObjFile&: *MemFile))
349 return Err;
350 } else
351 return createStringError(EC: std::errc::invalid_argument,
352 Fmt: "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 OutputFileName: Config.Common.OutputFilename, Write: [&](raw_ostream &OutFile) -> Error {
374 raw_crc_ostream CRCBuffer(OutFile);
375
376 if (Error Err = objcopy::executeObjcopyOnBinary(Config, In&: InputFile,
377 Out&: 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 OutputFileName: Config.Common.OutputFilename, Write: [&](raw_ostream &OutFile) -> Error {
402 return objcopy::executeObjcopyOnBinary(Config, In&: InputFile, Out&: 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 LinkedDebugInfoBits: std::move(LinkedDebugInfoBits));
415 if (!SeparateDebugFileCRC32OrErr)
416 return SeparateDebugFileCRC32OrErr.takeError();
417
418 if (Error Err =
419 saveNonDebugInfo(Opts, InputFile, GnuDebugLinkCRC32: *SeparateDebugFileCRC32OrErr))
420 return Err;
421 } else {
422 if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile,
423 LinkedDebugInfoBits: 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 OutputFileName: Config.Common.OutputFilename, Write: [&](raw_ostream &OutFile) -> Error {
438 return objcopy::executeObjcopyOnBinary(Config, In&: InputFile, Out&: 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(Message: "Do debug info linking...", Verbose: Opts.Verbose);
449
450 DebugInfoBits LinkedDebugInfo;
451 raw_svector_ostream OutStream(LinkedDebugInfo);
452
453 if (Error Err = linkDebugInfo(file&: InputFile, Options: Opts, OutStream))
454 return Err;
455
456 if (Error Err =
457 saveLinkedDebugInfo(Opts, InputFile, LinkedDebugInfoBits: 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
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(Args: ArgsArr, MissingArgIndex&: MAI, MissingArgCount&: MAC);
487
488 if (Args.hasArg(Ids: OPT_help) || Args.size() == 0) {
489 T.printHelp(
490 OS&: outs(), Usage: (ToolName + " [options] <input file> <output file>").c_str(),
491 Title: "llvm-dwarfutil is a tool to copy and manipulate debug info", ShowHidden: false);
492 return EXIT_SUCCESS;
493 }
494
495 if (Args.hasArg(Ids: OPT_version)) {
496 cl::PrintVersionMessage();
497 return EXIT_SUCCESS;
498 }
499
500 Options Opts;
501 if (Error Err = validateAndSetOptions(Args, Options&: Opts))
502 error(Err: std::move(Err), Prefix: dwarfutil::ToolName);
503
504 InitializeAllTargets();
505 InitializeAllTargetMCs();
506 InitializeAllTargetInfos();
507 InitializeAllAsmPrinters();
508
509 ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
510 MemoryBuffer::getFileOrSTDIN(Filename: Opts.InputFileName);
511 if (BuffOrErr.getError())
512 error(Err: createFileError(F: Opts.InputFileName, EC: BuffOrErr.getError()));
513
514 Expected<std::unique_ptr<Binary>> BinOrErr =
515 object::createBinary(Source: **BuffOrErr);
516 if (!BinOrErr)
517 error(Err: createFileError(F: Opts.InputFileName, E: BinOrErr.takeError()));
518
519 Expected<FilePermissionsApplier> PermsApplierOrErr =
520 FilePermissionsApplier::create(InputFilename: Opts.InputFileName);
521 if (!PermsApplierOrErr)
522 error(Err: createFileError(F: Opts.InputFileName, E: PermsApplierOrErr.takeError()));
523
524 if (!(*BinOrErr)->isObject())
525 error(Err: createFileError(F: Opts.InputFileName,
526 E: createError(Err: "unsupported input file")));
527
528 if (Error Err =
529 applyCLOptions(Opts, InputFile&: *static_cast<ObjectFile *>((*BinOrErr).get())))
530 error(Err: createFileError(F: Opts.InputFileName, E: std::move(Err)));
531
532 BinOrErr->reset();
533 BuffOrErr->reset();
534
535 if (Error Err = PermsApplierOrErr->apply(OutputFilename: Opts.OutputFileName))
536 error(Err: std::move(Err));
537
538 if (Opts.BuildSeparateDebugFile)
539 if (Error Err = PermsApplierOrErr->apply(OutputFilename: Opts.getSeparateDebugFileName()))
540 error(Err: std::move(Err));
541
542 if (Opts.Verify) {
543 if (Error Err = verifyOutput(Opts))
544 error(Err: std::move(Err));
545 }
546
547 return EXIT_SUCCESS;
548}
549