Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
35231 views
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(...) 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
48
using namespace llvm::opt;
49
static constexpr opt::OptTable::Info InfoTable[] = {
50
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
51
#include "Options.inc"
52
#undef OPTION
53
};
54
55
class DwarfutilOptTable : public opt::GenericOptTable {
56
public:
57
DwarfutilOptTable() : opt::GenericOptTable(InfoTable) {}
58
};
59
} // namespace
60
61
namespace llvm {
62
namespace dwarfutil {
63
64
std::string ToolName;
65
66
static mc::RegisterMCTargetOptionsFlags MOF;
67
68
static 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(OPT_INPUT);
78
if (InputFiles.size() != 2)
79
return createStringError(
80
std::errc::invalid_argument,
81
formatv("exactly two positional arguments expected, {0} provided",
82
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(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
std::errc::invalid_argument,
116
formatv("unknown tombstone value: '{0}'", 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
std::errc::invalid_argument,
128
formatv("unknown linker kind value: '{0}'", 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
std::errc::invalid_argument,
141
formatv("unknown build-accelerator value: '{0}'", S).str().c_str());
142
}
143
144
if (Options.Verbose) {
145
if (Options.NumThreads != 1 && Args.hasArg(OPT_threads))
146
warning("--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
std::errc::invalid_argument,
155
"cannot use --odr-deduplication without --garbage-collection");
156
157
if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-")
158
return createStringError(
159
std::errc::invalid_argument,
160
"unable to write to stdout when --separate-debug-file specified");
161
162
return Error::success();
163
}
164
165
static 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)) {
174
Expected<StringRef> SecData = Sec.getContents();
175
if (!SecData)
176
return SecData.takeError();
177
178
Config.Common.AddSection.emplace_back(objcopy::NewSectionInfo(
179
*SecName, MemoryBuffer::getMemBuffer(*SecData, *SecName, false)));
180
}
181
}
182
183
return Error::success();
184
}
185
186
static Error verifyOutput(const Options &Opts) {
187
if (Opts.OutputFileName == "-") {
188
warning("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(FileName);
196
if (!BinOrErr)
197
return createFileError(FileName, BinOrErr.takeError());
198
199
if (BinOrErr->getBinary()->isObject()) {
200
if (ObjectFile *Obj = static_cast<ObjectFile *>(BinOrErr->getBinary())) {
201
verbose("Verifying DWARF...", Opts.Verbose);
202
std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
203
DIDumpOptions DumpOpts;
204
if (!DICtx->verify(Opts.Verbose ? outs() : nulls(),
205
DumpOpts.noImplicitRecursion()))
206
return createFileError(FileName,
207
createError("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
224
class raw_crc_ostream : public raw_ostream {
225
public:
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
234
protected:
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
CRC32, 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
250
static 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
Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
261
raw_crc_ostream CRCBuffer(OutFile);
262
if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile,
263
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
274
static 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(SeparateDebugFileName);
282
Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32;
283
284
if (Error Err = writeToOutput(
285
Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
286
if (Error Err =
287
objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile))
288
return Err;
289
290
return Error::success();
291
}))
292
return Err;
293
294
return Error::success();
295
}
296
297
static 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, *SeparateDebugFileCRC32OrErr))
306
return Err;
307
308
return Error::success();
309
}
310
311
using DebugInfoBits = SmallString<10000>;
312
313
static Error addSectionsFromLinkedData(objcopy::ConfigManager &Config,
314
ObjectFile &InputFile,
315
DebugInfoBits &LinkedDebugInfoBits) {
316
if (isa<ELFObjectFile<ELF32LE>>(&InputFile)) {
317
Expected<ELFObjectFile<ELF32LE>> MemFile = ELFObjectFile<ELF32LE>::create(
318
MemoryBufferRef(LinkedDebugInfoBits, ""));
319
if (!MemFile)
320
return MemFile.takeError();
321
322
if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
323
return Err;
324
} else if (isa<ELFObjectFile<ELF64LE>>(&InputFile)) {
325
Expected<ELFObjectFile<ELF64LE>> MemFile = ELFObjectFile<ELF64LE>::create(
326
MemoryBufferRef(LinkedDebugInfoBits, ""));
327
if (!MemFile)
328
return MemFile.takeError();
329
330
if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
331
return Err;
332
} else if (isa<ELFObjectFile<ELF32BE>>(&InputFile)) {
333
Expected<ELFObjectFile<ELF32BE>> MemFile = ELFObjectFile<ELF32BE>::create(
334
MemoryBufferRef(LinkedDebugInfoBits, ""));
335
if (!MemFile)
336
return MemFile.takeError();
337
338
if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
339
return Err;
340
} else if (isa<ELFObjectFile<ELF64BE>>(&InputFile)) {
341
Expected<ELFObjectFile<ELF64BE>> MemFile = ELFObjectFile<ELF64BE>::create(
342
MemoryBufferRef(LinkedDebugInfoBits, ""));
343
if (!MemFile)
344
return MemFile.takeError();
345
346
if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
347
return Err;
348
} else
349
return createStringError(std::errc::invalid_argument,
350
"unsupported file format");
351
352
return Error::success();
353
}
354
355
static Expected<uint32_t>
356
saveSeparateLinkedDebugInfo(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
Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
372
raw_crc_ostream CRCBuffer(OutFile);
373
374
if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile,
375
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
386
static 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
Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
400
return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile);
401
}))
402
return Err;
403
404
return Error::success();
405
}
406
407
static Error saveLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile,
408
DebugInfoBits LinkedDebugInfoBits) {
409
if (Opts.BuildSeparateDebugFile) {
410
Expected<uint32_t> SeparateDebugFileCRC32OrErr =
411
saveSeparateLinkedDebugInfo(Opts, InputFile,
412
std::move(LinkedDebugInfoBits));
413
if (!SeparateDebugFileCRC32OrErr)
414
return SeparateDebugFileCRC32OrErr.takeError();
415
416
if (Error Err =
417
saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr))
418
return Err;
419
} else {
420
if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile,
421
std::move(LinkedDebugInfoBits)))
422
return Err;
423
}
424
425
return Error::success();
426
}
427
428
static 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
Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
436
return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile);
437
}))
438
return Err;
439
440
return Error::success();
441
}
442
443
static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) {
444
if (Opts.DoGarbageCollection ||
445
Opts.AccelTableKind != DwarfUtilAccelKind::None) {
446
verbose("Do debug info linking...", Opts.Verbose);
447
448
DebugInfoBits LinkedDebugInfo;
449
raw_svector_ostream OutStream(LinkedDebugInfo);
450
451
if (Error Err = linkDebugInfo(InputFile, Opts, OutStream))
452
return Err;
453
454
if (Error Err =
455
saveLinkedDebugInfo(Opts, InputFile, 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
473
int 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(ArgsArr, MAI, MAC);
485
486
if (Args.hasArg(OPT_help) || Args.size() == 0) {
487
T.printHelp(
488
outs(), (ToolName + " [options] <input file> <output file>").c_str(),
489
"llvm-dwarfutil is a tool to copy and manipulate debug info", 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, Opts))
500
error(std::move(Err), dwarfutil::ToolName);
501
502
InitializeAllTargets();
503
InitializeAllTargetMCs();
504
InitializeAllTargetInfos();
505
InitializeAllAsmPrinters();
506
507
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
508
MemoryBuffer::getFileOrSTDIN(Opts.InputFileName);
509
if (BuffOrErr.getError())
510
error(createFileError(Opts.InputFileName, BuffOrErr.getError()));
511
512
Expected<std::unique_ptr<Binary>> BinOrErr =
513
object::createBinary(**BuffOrErr);
514
if (!BinOrErr)
515
error(createFileError(Opts.InputFileName, BinOrErr.takeError()));
516
517
Expected<FilePermissionsApplier> PermsApplierOrErr =
518
FilePermissionsApplier::create(Opts.InputFileName);
519
if (!PermsApplierOrErr)
520
error(createFileError(Opts.InputFileName, PermsApplierOrErr.takeError()));
521
522
if (!(*BinOrErr)->isObject())
523
error(createFileError(Opts.InputFileName,
524
createError("unsupported input file")));
525
526
if (Error Err =
527
applyCLOptions(Opts, *static_cast<ObjectFile *>((*BinOrErr).get())))
528
error(createFileError(Opts.InputFileName, std::move(Err)));
529
530
BinOrErr->reset();
531
BuffOrErr->reset();
532
533
if (Error Err = PermsApplierOrErr->apply(Opts.OutputFileName))
534
error(std::move(Err));
535
536
if (Opts.BuildSeparateDebugFile)
537
if (Error Err = PermsApplierOrErr->apply(Opts.getSeparateDebugFileName()))
538
error(std::move(Err));
539
540
if (Opts.Verify) {
541
if (Error Err = verifyOutput(Opts))
542
error(std::move(Err));
543
}
544
545
return EXIT_SUCCESS;
546
}
547
548