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

source code of llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp