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