1 //===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This program is a utility that aims to be a dropin replacement for Darwin's
11 //===----------------------------------------------------------------------===//
14 #include "BinaryHolder.h"
17 #include "DwarfLinkerForBinary.h"
18 #include "LinkUtils.h"
19 #include "MachOUtils.h"
20 #include "Reproducer.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/ADT/StringExtras.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/DebugInfo/DIContext.h"
27 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
28 #include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
29 #include "llvm/MC/MCSubtargetInfo.h"
30 #include "llvm/Object/Binary.h"
31 #include "llvm/Object/MachO.h"
32 #include "llvm/Option/Arg.h"
33 #include "llvm/Option/ArgList.h"
34 #include "llvm/Option/Option.h"
35 #include "llvm/Support/CommandLine.h"
36 #include "llvm/Support/CrashRecoveryContext.h"
37 #include "llvm/Support/FileCollector.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/FormatVariadic.h"
40 #include "llvm/Support/InitLLVM.h"
41 #include "llvm/Support/LLVMDriver.h"
42 #include "llvm/Support/Path.h"
43 #include "llvm/Support/TargetSelect.h"
44 #include "llvm/Support/ThreadPool.h"
45 #include "llvm/Support/WithColor.h"
46 #include "llvm/Support/raw_ostream.h"
47 #include "llvm/Support/thread.h"
48 #include "llvm/TargetParser/Triple.h"
53 #include <system_error>
56 using namespace llvm::dsymutil
;
57 using namespace object
;
61 OPT_INVALID
= 0, // This is not an option ID.
62 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
63 #include "Options.inc"
67 #define PREFIX(NAME, VALUE) \
68 static constexpr StringLiteral NAME##_init[] = VALUE; \
69 static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
70 std::size(NAME##_init) - 1);
71 #include "Options.inc"
74 using namespace llvm::opt
;
75 static constexpr opt::OptTable::Info InfoTable
[] = {
76 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
77 #include "Options.inc"
81 class DsymutilOptTable
: public opt::GenericOptTable
{
83 DsymutilOptTable() : opt::GenericOptTable(InfoTable
) {}
87 enum class DWARFVerify
: uint8_t {
91 OutputOnValidInput
= 1 << 2,
93 Auto
= Input
| OutputOnValidInput
,
94 #if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)
101 inline bool flagIsSet(DWARFVerify Flags
, DWARFVerify SingleFlag
) {
102 return static_cast<uint8_t>(Flags
) & static_cast<uint8_t>(SingleFlag
);
105 struct DsymutilOptions
{
106 bool DumpDebugMap
= false;
107 bool DumpStab
= false;
109 bool InputIsYAMLDebugMap
= false;
110 bool ForceKeepFunctionForStatic
= false;
111 std::string SymbolMap
;
112 std::string OutputFile
;
113 std::string Toolchain
;
114 std::string ReproducerPath
;
115 std::vector
<std::string
> Archs
;
116 std::vector
<std::string
> InputFiles
;
118 DWARFVerify Verify
= DWARFVerify::Default
;
119 ReproducerMode ReproMode
= ReproducerMode::GenerateOnCrash
;
120 dsymutil::LinkOptions LinkOpts
;
123 /// Return a list of input files. This function has logic for dealing with the
124 /// special case where we might have dSYM bundles as input. The function
125 /// returns an error when the directory structure doesn't match that of a dSYM
127 static Expected
<std::vector
<std::string
>> getInputs(opt::InputArgList
&Args
,
129 std::vector
<std::string
> InputFiles
;
130 for (auto *File
: Args
.filtered(OPT_INPUT
))
131 InputFiles
.push_back(File
->getValue());
136 // If we are updating, we might get dSYM bundles as input.
137 std::vector
<std::string
> Inputs
;
138 for (const auto &Input
: InputFiles
) {
139 if (!sys::fs::is_directory(Input
)) {
140 Inputs
.push_back(Input
);
144 // Make sure that we're dealing with a dSYM bundle.
145 SmallString
<256> BundlePath(Input
);
146 sys::path::append(BundlePath
, "Contents", "Resources", "DWARF");
147 if (!sys::fs::is_directory(BundlePath
))
148 return make_error
<StringError
>(
149 Input
+ " is a directory, but doesn't look like a dSYM bundle.",
150 inconvertibleErrorCode());
152 // Create a directory iterator to iterate over all the entries in the
155 sys::fs::directory_iterator
DirIt(BundlePath
, EC
);
156 sys::fs::directory_iterator DirEnd
;
158 return errorCodeToError(EC
);
160 // Add each entry to the list of inputs.
161 while (DirIt
!= DirEnd
) {
162 Inputs
.push_back(DirIt
->path());
165 return errorCodeToError(EC
);
171 // Verify that the given combination of options makes sense.
172 static Error
verifyOptions(const DsymutilOptions
&Options
) {
173 if (Options
.InputFiles
.empty()) {
174 return make_error
<StringError
>("no input files specified",
175 errc::invalid_argument
);
178 if (Options
.LinkOpts
.Update
&& llvm::is_contained(Options
.InputFiles
, "-")) {
179 // FIXME: We cannot use stdin for an update because stdin will be
180 // consumed by the BinaryHolder during the debugmap parsing, and
181 // then we will want to consume it again in DwarfLinker. If we
182 // used a unique BinaryHolder object that could cache multiple
183 // binaries this restriction would go away.
184 return make_error
<StringError
>(
185 "standard input cannot be used as input for a dSYM update.",
186 errc::invalid_argument
);
189 if (!Options
.Flat
&& Options
.OutputFile
== "-")
190 return make_error
<StringError
>(
191 "cannot emit to standard output without --flat.",
192 errc::invalid_argument
);
194 if (Options
.InputFiles
.size() > 1 && Options
.Flat
&&
195 !Options
.OutputFile
.empty())
196 return make_error
<StringError
>(
197 "cannot use -o with multiple inputs in flat mode.",
198 errc::invalid_argument
);
200 if (!Options
.ReproducerPath
.empty() &&
201 Options
.ReproMode
!= ReproducerMode::Use
)
202 return make_error
<StringError
>(
203 "cannot combine --gen-reproducer and --use-reproducer.",
204 errc::invalid_argument
);
206 return Error::success();
209 static Expected
<DsymutilAccelTableKind
>
210 getAccelTableKind(opt::InputArgList
&Args
) {
211 if (opt::Arg
*Accelerator
= Args
.getLastArg(OPT_accelerator
)) {
212 StringRef S
= Accelerator
->getValue();
214 return DsymutilAccelTableKind::Apple
;
216 return DsymutilAccelTableKind::Dwarf
;
218 return DsymutilAccelTableKind::Pub
;
220 return DsymutilAccelTableKind::Default
;
222 return DsymutilAccelTableKind::None
;
223 return make_error
<StringError
>("invalid accelerator type specified: '" + S
+
224 "'. Supported values are 'Apple', "
225 "'Dwarf', 'Pub', 'Default' and 'None'.",
226 inconvertibleErrorCode());
228 return DsymutilAccelTableKind::Default
;
231 static Expected
<DsymutilDWARFLinkerType
>
232 getDWARFLinkerType(opt::InputArgList
&Args
) {
233 if (opt::Arg
*LinkerType
= Args
.getLastArg(OPT_linker
)) {
234 StringRef S
= LinkerType
->getValue();
236 return DsymutilDWARFLinkerType::Apple
;
238 return DsymutilDWARFLinkerType::LLVM
;
239 return make_error
<StringError
>("invalid DWARF linker type specified: '" +
241 "'. Supported values are 'apple', "
243 inconvertibleErrorCode());
246 return DsymutilDWARFLinkerType::Apple
;
249 static Expected
<ReproducerMode
> getReproducerMode(opt::InputArgList
&Args
) {
250 if (Args
.hasArg(OPT_gen_reproducer
))
251 return ReproducerMode::GenerateOnExit
;
252 if (opt::Arg
*Reproducer
= Args
.getLastArg(OPT_reproducer
)) {
253 StringRef S
= Reproducer
->getValue();
254 if (S
== "GenerateOnExit")
255 return ReproducerMode::GenerateOnExit
;
256 if (S
== "GenerateOnCrash")
257 return ReproducerMode::GenerateOnCrash
;
259 return ReproducerMode::Off
;
260 return make_error
<StringError
>(
261 "invalid reproducer mode: '" + S
+
262 "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "
264 inconvertibleErrorCode());
266 return ReproducerMode::GenerateOnCrash
;
269 static Expected
<DWARFVerify
> getVerifyKind(opt::InputArgList
&Args
) {
270 if (Args
.hasArg(OPT_verify
))
271 return DWARFVerify::Output
;
272 if (opt::Arg
*Verify
= Args
.getLastArg(OPT_verify_dwarf
)) {
273 StringRef S
= Verify
->getValue();
275 return DWARFVerify::Input
;
277 return DWARFVerify::Output
;
279 return DWARFVerify::All
;
281 return DWARFVerify::Auto
;
283 return DWARFVerify::None
;
284 return make_error
<StringError
>("invalid verify type specified: '" + S
+
285 "'. Supported values are 'none', "
286 "'input', 'output', 'all' and 'auto'.",
287 inconvertibleErrorCode());
289 return DWARFVerify::Default
;
292 /// Parses the command line options into the LinkOptions struct and performs
293 /// some sanity checking. Returns an error in case the latter fails.
294 static Expected
<DsymutilOptions
> getOptions(opt::InputArgList
&Args
) {
295 DsymutilOptions Options
;
297 Options
.DumpDebugMap
= Args
.hasArg(OPT_dump_debug_map
);
298 Options
.DumpStab
= Args
.hasArg(OPT_symtab
);
299 Options
.Flat
= Args
.hasArg(OPT_flat
);
300 Options
.InputIsYAMLDebugMap
= Args
.hasArg(OPT_yaml_input
);
302 if (Expected
<DWARFVerify
> Verify
= getVerifyKind(Args
)) {
303 Options
.Verify
= *Verify
;
305 return Verify
.takeError();
308 Options
.LinkOpts
.NoODR
= Args
.hasArg(OPT_no_odr
);
309 Options
.LinkOpts
.VerifyInputDWARF
=
310 flagIsSet(Options
.Verify
, DWARFVerify::Input
);
311 Options
.LinkOpts
.NoOutput
= Args
.hasArg(OPT_no_output
);
312 Options
.LinkOpts
.NoTimestamp
= Args
.hasArg(OPT_no_swiftmodule_timestamp
);
313 Options
.LinkOpts
.Update
= Args
.hasArg(OPT_update
);
314 Options
.LinkOpts
.Verbose
= Args
.hasArg(OPT_verbose
);
315 Options
.LinkOpts
.Statistics
= Args
.hasArg(OPT_statistics
);
316 Options
.LinkOpts
.Fat64
= Args
.hasArg(OPT_fat64
);
317 Options
.LinkOpts
.KeepFunctionForStatic
=
318 Args
.hasArg(OPT_keep_func_for_static
);
320 if (opt::Arg
*ReproducerPath
= Args
.getLastArg(OPT_use_reproducer
)) {
321 Options
.ReproMode
= ReproducerMode::Use
;
322 Options
.ReproducerPath
= ReproducerPath
->getValue();
324 if (Expected
<ReproducerMode
> ReproMode
= getReproducerMode(Args
)) {
325 Options
.ReproMode
= *ReproMode
;
327 return ReproMode
.takeError();
331 if (Expected
<DsymutilAccelTableKind
> AccelKind
= getAccelTableKind(Args
)) {
332 Options
.LinkOpts
.TheAccelTableKind
= *AccelKind
;
334 return AccelKind
.takeError();
337 if (Expected
<DsymutilDWARFLinkerType
> DWARFLinkerType
=
338 getDWARFLinkerType(Args
)) {
339 Options
.LinkOpts
.DWARFLinkerType
= *DWARFLinkerType
;
341 return DWARFLinkerType
.takeError();
344 if (opt::Arg
*SymbolMap
= Args
.getLastArg(OPT_symbolmap
))
345 Options
.SymbolMap
= SymbolMap
->getValue();
347 if (Args
.hasArg(OPT_symbolmap
))
348 Options
.LinkOpts
.Update
= true;
350 if (Expected
<std::vector
<std::string
>> InputFiles
=
351 getInputs(Args
, Options
.LinkOpts
.Update
)) {
352 Options
.InputFiles
= std::move(*InputFiles
);
354 return InputFiles
.takeError();
357 for (auto *Arch
: Args
.filtered(OPT_arch
))
358 Options
.Archs
.push_back(Arch
->getValue());
360 if (opt::Arg
*OsoPrependPath
= Args
.getLastArg(OPT_oso_prepend_path
))
361 Options
.LinkOpts
.PrependPath
= OsoPrependPath
->getValue();
363 for (const auto &Arg
: Args
.getAllArgValues(OPT_object_prefix_map
)) {
364 auto Split
= StringRef(Arg
).split('=');
365 Options
.LinkOpts
.ObjectPrefixMap
.insert(
366 {std::string(Split
.first
), std::string(Split
.second
)});
369 if (opt::Arg
*OutputFile
= Args
.getLastArg(OPT_output
))
370 Options
.OutputFile
= OutputFile
->getValue();
372 if (opt::Arg
*Toolchain
= Args
.getLastArg(OPT_toolchain
))
373 Options
.Toolchain
= Toolchain
->getValue();
375 if (Args
.hasArg(OPT_assembly
))
376 Options
.LinkOpts
.FileType
= DWARFLinker::OutputFileType::Assembly
;
378 if (opt::Arg
*NumThreads
= Args
.getLastArg(OPT_threads
))
379 Options
.LinkOpts
.Threads
= atoi(NumThreads
->getValue());
381 Options
.LinkOpts
.Threads
= 0; // Use all available hardware threads
383 if (Options
.DumpDebugMap
|| Options
.LinkOpts
.Verbose
)
384 Options
.LinkOpts
.Threads
= 1;
386 if (opt::Arg
*RemarksPrependPath
= Args
.getLastArg(OPT_remarks_prepend_path
))
387 Options
.LinkOpts
.RemarksPrependPath
= RemarksPrependPath
->getValue();
389 if (opt::Arg
*RemarksOutputFormat
=
390 Args
.getLastArg(OPT_remarks_output_format
)) {
391 if (Expected
<remarks::Format
> FormatOrErr
=
392 remarks::parseFormat(RemarksOutputFormat
->getValue()))
393 Options
.LinkOpts
.RemarksFormat
= *FormatOrErr
;
395 return FormatOrErr
.takeError();
398 Options
.LinkOpts
.RemarksKeepAll
=
399 !Args
.hasArg(OPT_remarks_drop_without_debug
);
401 if (opt::Arg
*BuildVariantSuffix
= Args
.getLastArg(OPT_build_variant_suffix
))
402 Options
.LinkOpts
.BuildVariantSuffix
= BuildVariantSuffix
->getValue();
404 for (auto *SearchPath
: Args
.filtered(OPT_dsym_search_path
))
405 Options
.LinkOpts
.DSYMSearchPaths
.push_back(SearchPath
->getValue());
407 if (Error E
= verifyOptions(Options
))
412 static Error
createPlistFile(StringRef Bin
, StringRef BundleRoot
,
413 StringRef Toolchain
) {
414 // Create plist file to write to.
415 SmallString
<128> InfoPlist(BundleRoot
);
416 sys::path::append(InfoPlist
, "Contents/Info.plist");
418 raw_fd_ostream
PL(InfoPlist
, EC
, sys::fs::OF_TextWithCRLF
);
420 return make_error
<StringError
>(
421 "cannot create Plist: " + toString(errorCodeToError(EC
)), EC
);
423 CFBundleInfo BI
= getBundleInfo(Bin
);
425 if (BI
.IDStr
.empty()) {
426 StringRef BundleID
= *sys::path::rbegin(BundleRoot
);
427 if (sys::path::extension(BundleRoot
) == ".dSYM")
428 BI
.IDStr
= std::string(sys::path::stem(BundleID
));
430 BI
.IDStr
= std::string(BundleID
);
433 // Print out information to the plist file.
434 PL
<< "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
435 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
436 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
437 << "<plist version=\"1.0\">\n"
439 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
440 << "\t\t<string>English</string>\n"
441 << "\t\t<key>CFBundleIdentifier</key>\n"
442 << "\t\t<string>com.apple.xcode.dsym.";
443 printHTMLEscaped(BI
.IDStr
, PL
);
445 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
446 << "\t\t<string>6.0</string>\n"
447 << "\t\t<key>CFBundlePackageType</key>\n"
448 << "\t\t<string>dSYM</string>\n"
449 << "\t\t<key>CFBundleSignature</key>\n"
450 << "\t\t<string>\?\?\?\?</string>\n";
452 if (!BI
.OmitShortVersion()) {
453 PL
<< "\t\t<key>CFBundleShortVersionString</key>\n";
454 PL
<< "\t\t<string>";
455 printHTMLEscaped(BI
.ShortVersionStr
, PL
);
459 PL
<< "\t\t<key>CFBundleVersion</key>\n";
460 PL
<< "\t\t<string>";
461 printHTMLEscaped(BI
.VersionStr
, PL
);
464 if (!Toolchain
.empty()) {
465 PL
<< "\t\t<key>Toolchain</key>\n";
466 PL
<< "\t\t<string>";
467 printHTMLEscaped(Toolchain
, PL
);
475 return Error::success();
478 static Error
createBundleDir(StringRef BundleBase
) {
479 SmallString
<128> Bundle(BundleBase
);
480 sys::path::append(Bundle
, "Contents", "Resources", "DWARF");
481 if (std::error_code EC
=
482 create_directories(Bundle
.str(), true, sys::fs::perms::all_all
))
483 return make_error
<StringError
>(
484 "cannot create bundle: " + toString(errorCodeToError(EC
)), EC
);
486 return Error::success();
489 static bool verifyOutput(StringRef OutputFile
, StringRef Arch
,
490 DsymutilOptions Options
, std::mutex
&Mutex
) {
492 if (OutputFile
== "-") {
493 std::lock_guard
<std::mutex
> Guard(Mutex
);
494 WithColor::warning() << "verification skipped for " << Arch
495 << " because writing to stdout.\n";
499 if (Options
.LinkOpts
.NoOutput
) {
500 std::lock_guard
<std::mutex
> Guard(Mutex
);
501 WithColor::warning() << "verification skipped for " << Arch
502 << " because --no-output was passed.\n";
506 Expected
<OwningBinary
<Binary
>> BinOrErr
= createBinary(OutputFile
);
508 std::lock_guard
<std::mutex
> Guard(Mutex
);
509 WithColor::error() << OutputFile
<< ": " << toString(BinOrErr
.takeError());
513 Binary
&Binary
= *BinOrErr
.get().getBinary();
514 if (auto *Obj
= dyn_cast
<MachOObjectFile
>(&Binary
)) {
515 std::unique_ptr
<DWARFContext
> DICtx
= DWARFContext::create(*Obj
);
516 if (DICtx
->getMaxVersion() > 5) {
517 std::lock_guard
<std::mutex
> Guard(Mutex
);
519 << "verification skipped for " << Arch
520 << " because DWARF standard greater than v5 is not supported yet.\n";
524 if (Options
.LinkOpts
.Verbose
) {
525 std::lock_guard
<std::mutex
> Guard(Mutex
);
526 errs() << "Verifying DWARF for architecture: " << Arch
<< "\n";
530 raw_string_ostream
OS(Buffer
);
532 DIDumpOptions DumpOpts
;
533 bool success
= DICtx
->verify(OS
, DumpOpts
.noImplicitRecursion());
535 std::lock_guard
<std::mutex
> Guard(Mutex
);
537 WithColor::error() << "output verification failed for " << Arch
<< '\n';
546 struct OutputLocation
{
547 OutputLocation(std::string DWARFFile
,
548 std::optional
<std::string
> ResourceDir
= {})
549 : DWARFFile(DWARFFile
), ResourceDir(ResourceDir
) {}
550 /// This method is a workaround for older compilers.
551 std::optional
<std::string
> getResourceDir() const { return ResourceDir
; }
552 std::string DWARFFile
;
553 std::optional
<std::string
> ResourceDir
;
557 static Expected
<OutputLocation
>
558 getOutputFileName(StringRef InputFile
, const DsymutilOptions
&Options
) {
559 if (Options
.OutputFile
== "-")
560 return OutputLocation(Options
.OutputFile
);
562 // When updating, do in place replacement.
563 if (Options
.OutputFile
.empty() &&
564 (Options
.LinkOpts
.Update
|| !Options
.SymbolMap
.empty()))
565 return OutputLocation(std::string(InputFile
));
567 // When dumping the debug map, just return an empty output location. This
568 // allows us to compute the output location once.
569 if (Options
.DumpDebugMap
)
570 return OutputLocation("");
572 // If a flat dSYM has been requested, things are pretty simple.
574 if (Options
.OutputFile
.empty()) {
575 if (InputFile
== "-")
576 return OutputLocation
{"a.out.dwarf", {}};
577 return OutputLocation((InputFile
+ ".dwarf").str());
580 return OutputLocation(Options
.OutputFile
);
583 // We need to create/update a dSYM bundle.
584 // A bundle hierarchy looks like this:
585 // <bundle name>.dSYM/
591 std::string DwarfFile
=
592 std::string(InputFile
== "-" ? StringRef("a.out") : InputFile
);
593 SmallString
<128> Path(Options
.OutputFile
);
595 Path
= DwarfFile
+ ".dSYM";
596 if (!Options
.LinkOpts
.NoOutput
) {
597 if (auto E
= createBundleDir(Path
))
599 if (auto E
= createPlistFile(DwarfFile
, Path
, Options
.Toolchain
))
603 sys::path::append(Path
, "Contents", "Resources");
604 std::string ResourceDir
= std::string(Path
.str());
605 sys::path::append(Path
, "DWARF", sys::path::filename(DwarfFile
));
606 return OutputLocation(std::string(Path
.str()), ResourceDir
);
609 int dsymutil_main(int argc
, char **argv
, const llvm::ToolContext
&) {
610 InitLLVM
X(argc
, argv
);
616 ArrayRef
<const char *> ArgsArr
= ArrayRef(argv
+ 1, argc
- 1);
617 opt::InputArgList Args
= T
.ParseArgs(ArgsArr
, MAI
, MAC
);
619 void *P
= (void *)(intptr_t)getOutputFileName
;
620 std::string SDKPath
= sys::fs::getMainExecutable(argv
[0], P
);
621 SDKPath
= std::string(sys::path::parent_path(SDKPath
));
623 for (auto *Arg
: Args
.filtered(OPT_UNKNOWN
)) {
624 WithColor::warning() << "ignoring unknown option: " << Arg
->getSpelling()
628 if (Args
.hasArg(OPT_help
)) {
630 outs(), (std::string(argv
[0]) + " [options] <input files>").c_str(),
631 "manipulate archived DWARF debug symbol files.\n\n"
632 "dsymutil links the DWARF debug information found in the object files\n"
633 "for the executable <input file> by using debug symbols information\n"
634 "contained in its symbol table.\n",
639 if (Args
.hasArg(OPT_version
)) {
640 cl::PrintVersionMessage();
644 auto OptionsOrErr
= getOptions(Args
);
646 WithColor::error() << toString(OptionsOrErr
.takeError()) << '\n';
650 auto &Options
= *OptionsOrErr
;
652 InitializeAllTargetInfos();
653 InitializeAllTargetMCs();
654 InitializeAllTargets();
655 InitializeAllAsmPrinters();
657 auto Repro
= Reproducer::createReproducer(Options
.ReproMode
,
658 Options
.ReproducerPath
, argc
, argv
);
660 WithColor::error() << toString(Repro
.takeError()) << '\n';
664 Options
.LinkOpts
.VFS
= (*Repro
)->getVFS();
666 for (const auto &Arch
: Options
.Archs
)
667 if (Arch
!= "*" && Arch
!= "all" &&
668 !object::MachOObjectFile::isValidArch(Arch
)) {
669 WithColor::error() << "unsupported cpu architecture: '" << Arch
<< "'\n";
673 SymbolMapLoader
SymMapLoader(Options
.SymbolMap
);
675 for (auto &InputFile
: Options
.InputFiles
) {
676 // Dump the symbol table for each input file and requested arch
677 if (Options
.DumpStab
) {
678 if (!dumpStab(Options
.LinkOpts
.VFS
, InputFile
, Options
.Archs
,
679 Options
.LinkOpts
.DSYMSearchPaths
,
680 Options
.LinkOpts
.PrependPath
,
681 Options
.LinkOpts
.BuildVariantSuffix
))
686 auto DebugMapPtrsOrErr
= parseDebugMap(
687 Options
.LinkOpts
.VFS
, InputFile
, Options
.Archs
,
688 Options
.LinkOpts
.DSYMSearchPaths
, Options
.LinkOpts
.PrependPath
,
689 Options
.LinkOpts
.BuildVariantSuffix
, Options
.LinkOpts
.Verbose
,
690 Options
.InputIsYAMLDebugMap
);
692 if (auto EC
= DebugMapPtrsOrErr
.getError()) {
693 WithColor::error() << "cannot parse the debug map for '" << InputFile
694 << "': " << EC
.message() << '\n';
698 // Remember the number of debug maps that are being processed to decide how
699 // to name the remark files.
700 Options
.LinkOpts
.NumDebugMaps
= DebugMapPtrsOrErr
->size();
702 if (Options
.LinkOpts
.Update
) {
703 // The debug map should be empty. Add one object file corresponding to
705 for (auto &Map
: *DebugMapPtrsOrErr
)
706 Map
->addDebugMapObject(InputFile
,
707 sys::TimePoint
<std::chrono::seconds
>());
710 // Ensure that the debug map is not empty (anymore).
711 if (DebugMapPtrsOrErr
->empty()) {
712 WithColor::error() << "no architecture to link\n";
716 // Shared a single binary holder for all the link steps.
717 BinaryHolder
BinHolder(Options
.LinkOpts
.VFS
);
719 // Compute the output location and update the resource directory.
720 Expected
<OutputLocation
> OutputLocationOrErr
=
721 getOutputFileName(InputFile
, Options
);
722 if (!OutputLocationOrErr
) {
723 WithColor::error() << toString(OutputLocationOrErr
.takeError());
726 Options
.LinkOpts
.ResourceDir
= OutputLocationOrErr
->getResourceDir();
728 // Statistics only require different architectures to be processed
729 // sequentially, the link itself can still happen in parallel. Change the
730 // thread pool strategy here instead of modifying LinkOpts.Threads.
731 ThreadPoolStrategy S
= hardware_concurrency(
732 Options
.LinkOpts
.Statistics
? 1 : Options
.LinkOpts
.Threads
);
733 if (Options
.LinkOpts
.Threads
== 0) {
734 // If NumThreads is not specified, create one thread for each input, up to
735 // the number of hardware threads.
736 S
.ThreadsRequested
= DebugMapPtrsOrErr
->size();
739 ThreadPool
Threads(S
);
741 // If there is more than one link to execute, we need to generate
743 const bool NeedsTempFiles
=
744 !Options
.DumpDebugMap
&& (Options
.OutputFile
!= "-") &&
745 (DebugMapPtrsOrErr
->size() != 1 || Options
.LinkOpts
.Update
);
747 std::atomic_char
AllOK(1);
748 SmallVector
<MachOUtils::ArchAndFile
, 4> TempFiles
;
750 std::mutex ErrorHandlerMutex
;
752 // Set up a crash recovery context.
753 CrashRecoveryContext::Enable();
754 CrashRecoveryContext CRC
;
755 CRC
.DumpStackAndCleanupOnFailure
= true;
757 const bool Crashed
= !CRC
.RunSafely([&]() {
758 for (auto &Map
: *DebugMapPtrsOrErr
) {
759 if (Options
.LinkOpts
.Verbose
|| Options
.DumpDebugMap
)
762 if (Options
.DumpDebugMap
)
765 if (!Options
.SymbolMap
.empty())
766 Options
.LinkOpts
.Translator
= SymMapLoader
.Load(InputFile
, *Map
);
768 if (Map
->begin() == Map
->end()) {
769 std::lock_guard
<std::mutex
> Guard(ErrorHandlerMutex
);
771 << "no debug symbols in executable (-arch "
772 << MachOUtils::getArchName(Map
->getTriple().getArchName())
776 // Using a std::shared_ptr rather than std::unique_ptr because move-only
777 // types don't work with std::bind in the ThreadPool implementation.
778 std::shared_ptr
<raw_fd_ostream
> OS
;
780 std::string OutputFile
= OutputLocationOrErr
->DWARFFile
;
781 if (NeedsTempFiles
) {
782 TempFiles
.emplace_back(Map
->getTriple().getArchName().str());
784 auto E
= TempFiles
.back().createTempFile();
786 std::lock_guard
<std::mutex
> Guard(ErrorHandlerMutex
);
787 WithColor::error() << toString(std::move(E
));
788 AllOK
.fetch_and(false);
792 MachOUtils::ArchAndFile
&AF
= TempFiles
.back();
793 OS
= std::make_shared
<raw_fd_ostream
>(AF
.getFD(),
794 /*shouldClose*/ false);
795 OutputFile
= AF
.getPath();
798 OS
= std::make_shared
<raw_fd_ostream
>(
799 Options
.LinkOpts
.NoOutput
? "-" : OutputFile
, EC
,
802 WithColor::error() << OutputFile
<< ": " << EC
.message();
803 AllOK
.fetch_and(false);
808 auto LinkLambda
= [&,
809 OutputFile
](std::shared_ptr
<raw_fd_ostream
> Stream
) {
810 DwarfLinkerForBinary
Linker(*Stream
, BinHolder
, Options
.LinkOpts
,
812 AllOK
.fetch_and(Linker
.link(*Map
));
814 if (flagIsSet(Options
.Verify
, DWARFVerify::Output
) ||
815 (flagIsSet(Options
.Verify
, DWARFVerify::OutputOnValidInput
) &&
816 !Linker
.InputVerificationFailed())) {
817 AllOK
.fetch_and(verifyOutput(OutputFile
,
818 Map
->getTriple().getArchName(),
819 Options
, ErrorHandlerMutex
));
823 // FIXME: The DwarfLinker can have some very deep recursion that can max
824 // out the (significantly smaller) stack when using threads. We don't
825 // want this limitation when we only have a single thread.
826 if (S
.ThreadsRequested
== 1)
829 Threads
.async(LinkLambda
, OS
);
836 (*Repro
)->generate();
841 if (NeedsTempFiles
) {
842 const bool Fat64
= Options
.LinkOpts
.Fat64
;
844 // Universal Mach-O files can't have an archicture slice that starts
845 // beyond the 4GB boundary. "lipo" can create a 64 bit universal
846 // header, but not all tools can parse these files so we want to return
847 // an error if the file can't be encoded as a file with a 32 bit
848 // universal header. To detect this, we check the size of each
849 // architecture's skinny Mach-O file and add up the offsets. If they
850 // exceed 4GB, then we return an error.
852 // First we compute the right offset where the first architecture will
853 // fit followin the 32 bit universal header. The 32 bit universal header
854 // starts with a uint32_t magic and a uint32_t number of architecture
855 // infos. Then it is followed by 5 uint32_t values for each
856 // architecture. So we set the start offset to the right value so we can
857 // calculate the exact offset that the first architecture slice can
859 constexpr uint64_t MagicAndCountSize
= 2 * 4;
860 constexpr uint64_t UniversalArchInfoSize
= 5 * 4;
861 uint64_t FileOffset
=
862 MagicAndCountSize
+ UniversalArchInfoSize
* TempFiles
.size();
863 for (const auto &File
: TempFiles
) {
864 ErrorOr
<vfs::Status
> stat
=
865 Options
.LinkOpts
.VFS
->status(File
.getPath());
868 if (FileOffset
> UINT32_MAX
) {
870 << formatv("the universal binary has a slice with a starting "
871 "offset ({0:x}) that exceeds 4GB and will produce "
872 "an invalid Mach-O file. Use the -fat64 flag to "
873 "generate a universal binary with a 64-bit header "
874 "but note that not all tools support this format.",
878 FileOffset
+= stat
->getSize();
881 if (!MachOUtils::generateUniversalBinary(
882 TempFiles
, OutputLocationOrErr
->DWARFFile
, Options
.LinkOpts
,