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 "LinkUtils.h"
18 #include "MachOUtils.h"
19 #include "Reproducer.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/ADT/Triple.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/InitLLVM.h"
40 #include "llvm/Support/Path.h"
41 #include "llvm/Support/TargetSelect.h"
42 #include "llvm/Support/ThreadPool.h"
43 #include "llvm/Support/WithColor.h"
44 #include "llvm/Support/raw_ostream.h"
45 #include "llvm/Support/thread.h"
50 #include <system_error>
53 using namespace llvm::dsymutil
;
54 using namespace object
;
58 OPT_INVALID
= 0, // This is not an option ID.
59 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
60 HELPTEXT, METAVAR, VALUES) \
62 #include "Options.inc"
66 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
67 #include "Options.inc"
70 const opt::OptTable::Info InfoTable
[] = {
71 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
72 HELPTEXT, METAVAR, VALUES) \
74 PREFIX, NAME, HELPTEXT, \
75 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
76 PARAM, FLAGS, OPT_##GROUP, \
77 OPT_##ALIAS, ALIASARGS, VALUES},
78 #include "Options.inc"
82 class DsymutilOptTable
: public opt::OptTable
{
84 DsymutilOptTable() : OptTable(InfoTable
) {}
88 enum class DWARFVerify
: uint8_t {
95 inline bool flagIsSet(DWARFVerify Flags
, DWARFVerify SingleFlag
) {
96 return static_cast<uint8_t>(Flags
) & static_cast<uint8_t>(SingleFlag
);
99 struct DsymutilOptions
{
100 bool DumpDebugMap
= false;
101 bool DumpStab
= false;
103 bool InputIsYAMLDebugMap
= false;
104 bool PaperTrailWarnings
= false;
105 bool ForceKeepFunctionForStatic
= false;
106 std::string SymbolMap
;
107 std::string OutputFile
;
108 std::string Toolchain
;
109 std::string ReproducerPath
;
110 std::vector
<std::string
> Archs
;
111 std::vector
<std::string
> InputFiles
;
113 DWARFVerify Verify
= DWARFVerify::None
;
114 ReproducerMode ReproMode
= ReproducerMode::GenerateOnCrash
;
115 dsymutil::LinkOptions LinkOpts
;
118 /// Return a list of input files. This function has logic for dealing with the
119 /// special case where we might have dSYM bundles as input. The function
120 /// returns an error when the directory structure doesn't match that of a dSYM
122 static Expected
<std::vector
<std::string
>> getInputs(opt::InputArgList
&Args
,
124 std::vector
<std::string
> InputFiles
;
125 for (auto *File
: Args
.filtered(OPT_INPUT
))
126 InputFiles
.push_back(File
->getValue());
131 // If we are updating, we might get dSYM bundles as input.
132 std::vector
<std::string
> Inputs
;
133 for (const auto &Input
: InputFiles
) {
134 if (!sys::fs::is_directory(Input
)) {
135 Inputs
.push_back(Input
);
139 // Make sure that we're dealing with a dSYM bundle.
140 SmallString
<256> BundlePath(Input
);
141 sys::path::append(BundlePath
, "Contents", "Resources", "DWARF");
142 if (!sys::fs::is_directory(BundlePath
))
143 return make_error
<StringError
>(
144 Input
+ " is a directory, but doesn't look like a dSYM bundle.",
145 inconvertibleErrorCode());
147 // Create a directory iterator to iterate over all the entries in the
150 sys::fs::directory_iterator
DirIt(BundlePath
, EC
);
151 sys::fs::directory_iterator DirEnd
;
153 return errorCodeToError(EC
);
155 // Add each entry to the list of inputs.
156 while (DirIt
!= DirEnd
) {
157 Inputs
.push_back(DirIt
->path());
160 return errorCodeToError(EC
);
166 // Verify that the given combination of options makes sense.
167 static Error
verifyOptions(const DsymutilOptions
&Options
) {
168 if (Options
.InputFiles
.empty()) {
169 return make_error
<StringError
>("no input files specified",
170 errc::invalid_argument
);
173 if (Options
.LinkOpts
.Update
&& llvm::is_contained(Options
.InputFiles
, "-")) {
174 // FIXME: We cannot use stdin for an update because stdin will be
175 // consumed by the BinaryHolder during the debugmap parsing, and
176 // then we will want to consume it again in DwarfLinker. If we
177 // used a unique BinaryHolder object that could cache multiple
178 // binaries this restriction would go away.
179 return make_error
<StringError
>(
180 "standard input cannot be used as input for a dSYM update.",
181 errc::invalid_argument
);
184 if (!Options
.Flat
&& Options
.OutputFile
== "-")
185 return make_error
<StringError
>(
186 "cannot emit to standard output without --flat.",
187 errc::invalid_argument
);
189 if (Options
.InputFiles
.size() > 1 && Options
.Flat
&&
190 !Options
.OutputFile
.empty())
191 return make_error
<StringError
>(
192 "cannot use -o with multiple inputs in flat mode.",
193 errc::invalid_argument
);
195 if (Options
.PaperTrailWarnings
&& Options
.InputIsYAMLDebugMap
)
196 return make_error
<StringError
>(
197 "paper trail warnings are not supported for YAML input.",
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
<DwarfLinkerAccelTableKind
>
210 getAccelTableKind(opt::InputArgList
&Args
) {
211 if (opt::Arg
*Accelerator
= Args
.getLastArg(OPT_accelerator
)) {
212 StringRef S
= Accelerator
->getValue();
214 return DwarfLinkerAccelTableKind::Apple
;
216 return DwarfLinkerAccelTableKind::Dwarf
;
218 return DwarfLinkerAccelTableKind::Pub
;
220 return DwarfLinkerAccelTableKind::Default
;
222 return DwarfLinkerAccelTableKind::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 DwarfLinkerAccelTableKind::Default
;
231 static Expected
<ReproducerMode
> getReproducerMode(opt::InputArgList
&Args
) {
232 if (Args
.hasArg(OPT_gen_reproducer
))
233 return ReproducerMode::GenerateOnExit
;
234 if (opt::Arg
*Reproducer
= Args
.getLastArg(OPT_reproducer
)) {
235 StringRef S
= Reproducer
->getValue();
236 if (S
== "GenerateOnExit")
237 return ReproducerMode::GenerateOnExit
;
238 if (S
== "GenerateOnCrash")
239 return ReproducerMode::GenerateOnCrash
;
241 return ReproducerMode::Use
;
243 return ReproducerMode::Off
;
244 return make_error
<StringError
>(
245 "invalid reproducer mode: '" + S
+
246 "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "
248 inconvertibleErrorCode());
250 return ReproducerMode::GenerateOnCrash
;
253 static Expected
<DWARFVerify
> getVerifyKind(opt::InputArgList
&Args
) {
254 if (Args
.hasArg(OPT_verify
))
255 return DWARFVerify::Output
;
256 if (opt::Arg
*Verify
= Args
.getLastArg(OPT_verify_dwarf
)) {
257 StringRef S
= Verify
->getValue();
259 return DWARFVerify::Input
;
261 return DWARFVerify::Output
;
263 return DWARFVerify::All
;
265 return DWARFVerify::None
;
266 return make_error
<StringError
>(
267 "invalid verify type specified: '" + S
+
268 "'. Supported values are 'input', 'output', 'all' and 'none'.",
269 inconvertibleErrorCode());
271 return DWARFVerify::None
;
274 /// Parses the command line options into the LinkOptions struct and performs
275 /// some sanity checking. Returns an error in case the latter fails.
276 static Expected
<DsymutilOptions
> getOptions(opt::InputArgList
&Args
) {
277 DsymutilOptions Options
;
279 Options
.DumpDebugMap
= Args
.hasArg(OPT_dump_debug_map
);
280 Options
.DumpStab
= Args
.hasArg(OPT_symtab
);
281 Options
.Flat
= Args
.hasArg(OPT_flat
);
282 Options
.InputIsYAMLDebugMap
= Args
.hasArg(OPT_yaml_input
);
283 Options
.PaperTrailWarnings
= Args
.hasArg(OPT_papertrail
);
285 if (Expected
<DWARFVerify
> Verify
= getVerifyKind(Args
)) {
286 Options
.Verify
= *Verify
;
288 return Verify
.takeError();
291 Options
.LinkOpts
.NoODR
= Args
.hasArg(OPT_no_odr
);
292 Options
.LinkOpts
.VerifyInputDWARF
=
293 flagIsSet(Options
.Verify
, DWARFVerify::Input
);
294 Options
.LinkOpts
.NoOutput
= Args
.hasArg(OPT_no_output
);
295 Options
.LinkOpts
.NoTimestamp
= Args
.hasArg(OPT_no_swiftmodule_timestamp
);
296 Options
.LinkOpts
.Update
= Args
.hasArg(OPT_update
);
297 Options
.LinkOpts
.Verbose
= Args
.hasArg(OPT_verbose
);
298 Options
.LinkOpts
.Statistics
= Args
.hasArg(OPT_statistics
);
299 Options
.LinkOpts
.KeepFunctionForStatic
=
300 Args
.hasArg(OPT_keep_func_for_static
);
302 if (opt::Arg
*ReproducerPath
= Args
.getLastArg(OPT_use_reproducer
)) {
303 Options
.ReproMode
= ReproducerMode::Use
;
304 Options
.ReproducerPath
= ReproducerPath
->getValue();
306 if (Expected
<ReproducerMode
> ReproMode
= getReproducerMode(Args
)) {
307 Options
.ReproMode
= *ReproMode
;
309 return ReproMode
.takeError();
313 if (Expected
<DwarfLinkerAccelTableKind
> AccelKind
= getAccelTableKind(Args
)) {
314 Options
.LinkOpts
.TheAccelTableKind
= *AccelKind
;
316 return AccelKind
.takeError();
319 if (opt::Arg
*SymbolMap
= Args
.getLastArg(OPT_symbolmap
))
320 Options
.SymbolMap
= SymbolMap
->getValue();
322 if (Args
.hasArg(OPT_symbolmap
))
323 Options
.LinkOpts
.Update
= true;
325 if (Expected
<std::vector
<std::string
>> InputFiles
=
326 getInputs(Args
, Options
.LinkOpts
.Update
)) {
327 Options
.InputFiles
= std::move(*InputFiles
);
329 return InputFiles
.takeError();
332 for (auto *Arch
: Args
.filtered(OPT_arch
))
333 Options
.Archs
.push_back(Arch
->getValue());
335 if (opt::Arg
*OsoPrependPath
= Args
.getLastArg(OPT_oso_prepend_path
))
336 Options
.LinkOpts
.PrependPath
= OsoPrependPath
->getValue();
338 for (const auto &Arg
: Args
.getAllArgValues(OPT_object_prefix_map
)) {
339 auto Split
= StringRef(Arg
).split('=');
340 Options
.LinkOpts
.ObjectPrefixMap
.insert(
341 {std::string(Split
.first
), std::string(Split
.second
)});
344 if (opt::Arg
*OutputFile
= Args
.getLastArg(OPT_output
))
345 Options
.OutputFile
= OutputFile
->getValue();
347 if (opt::Arg
*Toolchain
= Args
.getLastArg(OPT_toolchain
))
348 Options
.Toolchain
= Toolchain
->getValue();
350 if (Args
.hasArg(OPT_assembly
))
351 Options
.LinkOpts
.FileType
= OutputFileType::Assembly
;
353 if (opt::Arg
*NumThreads
= Args
.getLastArg(OPT_threads
))
354 Options
.LinkOpts
.Threads
= atoi(NumThreads
->getValue());
356 Options
.LinkOpts
.Threads
= 0; // Use all available hardware threads
358 if (Options
.DumpDebugMap
|| Options
.LinkOpts
.Verbose
)
359 Options
.LinkOpts
.Threads
= 1;
361 if (getenv("RC_DEBUG_OPTIONS"))
362 Options
.PaperTrailWarnings
= true;
364 if (opt::Arg
*RemarksPrependPath
= Args
.getLastArg(OPT_remarks_prepend_path
))
365 Options
.LinkOpts
.RemarksPrependPath
= RemarksPrependPath
->getValue();
367 if (opt::Arg
*RemarksOutputFormat
=
368 Args
.getLastArg(OPT_remarks_output_format
)) {
369 if (Expected
<remarks::Format
> FormatOrErr
=
370 remarks::parseFormat(RemarksOutputFormat
->getValue()))
371 Options
.LinkOpts
.RemarksFormat
= *FormatOrErr
;
373 return FormatOrErr
.takeError();
376 if (Error E
= verifyOptions(Options
))
381 static Error
createPlistFile(StringRef Bin
, StringRef BundleRoot
,
382 StringRef Toolchain
) {
383 // Create plist file to write to.
384 SmallString
<128> InfoPlist(BundleRoot
);
385 sys::path::append(InfoPlist
, "Contents/Info.plist");
387 raw_fd_ostream
PL(InfoPlist
, EC
, sys::fs::OF_TextWithCRLF
);
389 return make_error
<StringError
>(
390 "cannot create Plist: " + toString(errorCodeToError(EC
)), EC
);
392 CFBundleInfo BI
= getBundleInfo(Bin
);
394 if (BI
.IDStr
.empty()) {
395 StringRef BundleID
= *sys::path::rbegin(BundleRoot
);
396 if (sys::path::extension(BundleRoot
) == ".dSYM")
397 BI
.IDStr
= std::string(sys::path::stem(BundleID
));
399 BI
.IDStr
= std::string(BundleID
);
402 // Print out information to the plist file.
403 PL
<< "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
404 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
405 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
406 << "<plist version=\"1.0\">\n"
408 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
409 << "\t\t<string>English</string>\n"
410 << "\t\t<key>CFBundleIdentifier</key>\n"
411 << "\t\t<string>com.apple.xcode.dsym.";
412 printHTMLEscaped(BI
.IDStr
, PL
);
414 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
415 << "\t\t<string>6.0</string>\n"
416 << "\t\t<key>CFBundlePackageType</key>\n"
417 << "\t\t<string>dSYM</string>\n"
418 << "\t\t<key>CFBundleSignature</key>\n"
419 << "\t\t<string>\?\?\?\?</string>\n";
421 if (!BI
.OmitShortVersion()) {
422 PL
<< "\t\t<key>CFBundleShortVersionString</key>\n";
423 PL
<< "\t\t<string>";
424 printHTMLEscaped(BI
.ShortVersionStr
, PL
);
428 PL
<< "\t\t<key>CFBundleVersion</key>\n";
429 PL
<< "\t\t<string>";
430 printHTMLEscaped(BI
.VersionStr
, PL
);
433 if (!Toolchain
.empty()) {
434 PL
<< "\t\t<key>Toolchain</key>\n";
435 PL
<< "\t\t<string>";
436 printHTMLEscaped(Toolchain
, PL
);
444 return Error::success();
447 static Error
createBundleDir(StringRef BundleBase
) {
448 SmallString
<128> Bundle(BundleBase
);
449 sys::path::append(Bundle
, "Contents", "Resources", "DWARF");
450 if (std::error_code EC
=
451 create_directories(Bundle
.str(), true, sys::fs::perms::all_all
))
452 return make_error
<StringError
>(
453 "cannot create bundle: " + toString(errorCodeToError(EC
)), EC
);
455 return Error::success();
458 static bool verifyOutput(StringRef OutputFile
, StringRef Arch
, bool Verbose
) {
459 if (OutputFile
== "-") {
460 WithColor::warning() << "verification skipped for " << Arch
461 << "because writing to stdout.\n";
465 Expected
<OwningBinary
<Binary
>> BinOrErr
= createBinary(OutputFile
);
467 WithColor::error() << OutputFile
<< ": " << toString(BinOrErr
.takeError());
471 Binary
&Binary
= *BinOrErr
.get().getBinary();
472 if (auto *Obj
= dyn_cast
<MachOObjectFile
>(&Binary
)) {
473 raw_ostream
&os
= Verbose
? errs() : nulls();
474 os
<< "Verifying DWARF for architecture: " << Arch
<< "\n";
475 std::unique_ptr
<DWARFContext
> DICtx
= DWARFContext::create(*Obj
);
476 DIDumpOptions DumpOpts
;
477 bool success
= DICtx
->verify(os
, DumpOpts
.noImplicitRecursion());
479 WithColor::error() << "output verification failed for " << Arch
<< '\n';
487 struct OutputLocation
{
488 OutputLocation(std::string DWARFFile
, Optional
<std::string
> ResourceDir
= {})
489 : DWARFFile(DWARFFile
), ResourceDir(ResourceDir
) {}
490 /// This method is a workaround for older compilers.
491 Optional
<std::string
> getResourceDir() const { return ResourceDir
; }
492 std::string DWARFFile
;
493 Optional
<std::string
> ResourceDir
;
497 static Expected
<OutputLocation
>
498 getOutputFileName(StringRef InputFile
, const DsymutilOptions
&Options
) {
499 if (Options
.OutputFile
== "-")
500 return OutputLocation(Options
.OutputFile
);
502 // When updating, do in place replacement.
503 if (Options
.OutputFile
.empty() &&
504 (Options
.LinkOpts
.Update
|| !Options
.SymbolMap
.empty()))
505 return OutputLocation(std::string(InputFile
));
507 // When dumping the debug map, just return an empty output location. This
508 // allows us to compute the output location once.
509 if (Options
.DumpDebugMap
)
510 return OutputLocation("");
512 // If a flat dSYM has been requested, things are pretty simple.
514 if (Options
.OutputFile
.empty()) {
515 if (InputFile
== "-")
516 return OutputLocation
{"a.out.dwarf", {}};
517 return OutputLocation((InputFile
+ ".dwarf").str());
520 return OutputLocation(Options
.OutputFile
);
523 // We need to create/update a dSYM bundle.
524 // A bundle hierarchy looks like this:
525 // <bundle name>.dSYM/
531 std::string DwarfFile
=
532 std::string(InputFile
== "-" ? StringRef("a.out") : InputFile
);
533 SmallString
<128> Path(Options
.OutputFile
);
535 Path
= DwarfFile
+ ".dSYM";
536 if (!Options
.LinkOpts
.NoOutput
) {
537 if (auto E
= createBundleDir(Path
))
539 if (auto E
= createPlistFile(DwarfFile
, Path
, Options
.Toolchain
))
543 sys::path::append(Path
, "Contents", "Resources");
544 std::string ResourceDir
= std::string(Path
.str());
545 sys::path::append(Path
, "DWARF", sys::path::filename(DwarfFile
));
546 return OutputLocation(std::string(Path
.str()), ResourceDir
);
549 int dsymutil_main(int argc
, char **argv
) {
550 InitLLVM
X(argc
, argv
);
556 ArrayRef
<const char *> ArgsArr
= makeArrayRef(argv
+ 1, argc
- 1);
557 opt::InputArgList Args
= T
.ParseArgs(ArgsArr
, MAI
, MAC
);
559 void *P
= (void *)(intptr_t)getOutputFileName
;
560 std::string SDKPath
= sys::fs::getMainExecutable(argv
[0], P
);
561 SDKPath
= std::string(sys::path::parent_path(SDKPath
));
563 for (auto *Arg
: Args
.filtered(OPT_UNKNOWN
)) {
564 WithColor::warning() << "ignoring unknown option: " << Arg
->getSpelling()
568 if (Args
.hasArg(OPT_help
)) {
570 outs(), (std::string(argv
[0]) + " [options] <input files>").c_str(),
571 "manipulate archived DWARF debug symbol files.\n\n"
572 "dsymutil links the DWARF debug information found in the object files\n"
573 "for the executable <input file> by using debug symbols information\n"
574 "contained in its symbol table.\n",
579 if (Args
.hasArg(OPT_version
)) {
580 cl::PrintVersionMessage();
584 auto OptionsOrErr
= getOptions(Args
);
586 WithColor::error() << toString(OptionsOrErr
.takeError());
590 auto &Options
= *OptionsOrErr
;
592 InitializeAllTargetInfos();
593 InitializeAllTargetMCs();
594 InitializeAllTargets();
595 InitializeAllAsmPrinters();
597 auto Repro
= Reproducer::createReproducer(Options
.ReproMode
,
598 Options
.ReproducerPath
, argc
, argv
);
600 WithColor::error() << toString(Repro
.takeError());
604 Options
.LinkOpts
.VFS
= (*Repro
)->getVFS();
606 for (const auto &Arch
: Options
.Archs
)
607 if (Arch
!= "*" && Arch
!= "all" &&
608 !object::MachOObjectFile::isValidArch(Arch
)) {
609 WithColor::error() << "unsupported cpu architecture: '" << Arch
<< "'\n";
613 SymbolMapLoader
SymMapLoader(Options
.SymbolMap
);
615 for (auto &InputFile
: Options
.InputFiles
) {
616 // Dump the symbol table for each input file and requested arch
617 if (Options
.DumpStab
) {
618 if (!dumpStab(Options
.LinkOpts
.VFS
, InputFile
, Options
.Archs
,
619 Options
.LinkOpts
.PrependPath
))
624 auto DebugMapPtrsOrErr
=
625 parseDebugMap(Options
.LinkOpts
.VFS
, InputFile
, Options
.Archs
,
626 Options
.LinkOpts
.PrependPath
, Options
.PaperTrailWarnings
,
627 Options
.LinkOpts
.Verbose
, Options
.InputIsYAMLDebugMap
);
629 if (auto EC
= DebugMapPtrsOrErr
.getError()) {
630 WithColor::error() << "cannot parse the debug map for '" << InputFile
631 << "': " << EC
.message() << '\n';
635 // Remember the number of debug maps that are being processed to decide how
636 // to name the remark files.
637 Options
.LinkOpts
.NumDebugMaps
= DebugMapPtrsOrErr
->size();
639 if (Options
.LinkOpts
.Update
) {
640 // The debug map should be empty. Add one object file corresponding to
642 for (auto &Map
: *DebugMapPtrsOrErr
)
643 Map
->addDebugMapObject(InputFile
,
644 sys::TimePoint
<std::chrono::seconds
>());
647 // Ensure that the debug map is not empty (anymore).
648 if (DebugMapPtrsOrErr
->empty()) {
649 WithColor::error() << "no architecture to link\n";
653 // Shared a single binary holder for all the link steps.
654 BinaryHolder
BinHolder(Options
.LinkOpts
.VFS
);
656 // Compute the output location and update the resource directory.
657 Expected
<OutputLocation
> OutputLocationOrErr
=
658 getOutputFileName(InputFile
, Options
);
659 if (!OutputLocationOrErr
) {
660 WithColor::error() << toString(OutputLocationOrErr
.takeError());
663 Options
.LinkOpts
.ResourceDir
= OutputLocationOrErr
->getResourceDir();
665 // Statistics only require different architectures to be processed
666 // sequentially, the link itself can still happen in parallel. Change the
667 // thread pool strategy here instead of modifying LinkOpts.Threads.
668 ThreadPoolStrategy S
= hardware_concurrency(
669 Options
.LinkOpts
.Statistics
? 1 : Options
.LinkOpts
.Threads
);
670 if (Options
.LinkOpts
.Threads
== 0) {
671 // If NumThreads is not specified, create one thread for each input, up to
672 // the number of hardware threads.
673 S
.ThreadsRequested
= DebugMapPtrsOrErr
->size();
676 ThreadPool
Threads(S
);
678 // If there is more than one link to execute, we need to generate
680 const bool NeedsTempFiles
=
681 !Options
.DumpDebugMap
&& (Options
.OutputFile
!= "-") &&
682 (DebugMapPtrsOrErr
->size() != 1 || Options
.LinkOpts
.Update
);
683 bool VerifyOutput
= flagIsSet(Options
.Verify
, DWARFVerify::Output
);
684 if (VerifyOutput
&& Options
.LinkOpts
.NoOutput
) {
686 << "skipping output verification because --no-output was passed\n";
687 VerifyOutput
= false;
690 // Set up a crash recovery context.
691 CrashRecoveryContext::Enable();
692 CrashRecoveryContext CRC
;
693 CRC
.DumpStackAndCleanupOnFailure
= true;
695 std::atomic_char
AllOK(1);
696 SmallVector
<MachOUtils::ArchAndFile
, 4> TempFiles
;
698 const bool Crashed
= !CRC
.RunSafely([&]() {
699 for (auto &Map
: *DebugMapPtrsOrErr
) {
700 if (Options
.LinkOpts
.Verbose
|| Options
.DumpDebugMap
)
703 if (Options
.DumpDebugMap
)
706 if (!Options
.SymbolMap
.empty())
707 Options
.LinkOpts
.Translator
= SymMapLoader
.Load(InputFile
, *Map
);
709 if (Map
->begin() == Map
->end())
711 << "no debug symbols in executable (-arch "
712 << MachOUtils::getArchName(Map
->getTriple().getArchName())
715 // Using a std::shared_ptr rather than std::unique_ptr because move-only
716 // types don't work with std::bind in the ThreadPool implementation.
717 std::shared_ptr
<raw_fd_ostream
> OS
;
719 std::string OutputFile
= OutputLocationOrErr
->DWARFFile
;
720 if (NeedsTempFiles
) {
721 TempFiles
.emplace_back(Map
->getTriple().getArchName().str());
723 auto E
= TempFiles
.back().createTempFile();
725 WithColor::error() << toString(std::move(E
));
726 AllOK
.fetch_and(false);
730 auto &TempFile
= *(TempFiles
.back().File
);
731 OS
= std::make_shared
<raw_fd_ostream
>(TempFile
.FD
,
732 /*shouldClose*/ false);
733 OutputFile
= TempFile
.TmpName
;
736 OS
= std::make_shared
<raw_fd_ostream
>(
737 Options
.LinkOpts
.NoOutput
? "-" : OutputFile
, EC
,
740 WithColor::error() << OutputFile
<< ": " << EC
.message();
741 AllOK
.fetch_and(false);
746 auto LinkLambda
= [&,
747 OutputFile
](std::shared_ptr
<raw_fd_ostream
> Stream
,
748 LinkOptions Options
) {
750 linkDwarf(*Stream
, BinHolder
, *Map
, std::move(Options
)));
753 AllOK
.fetch_and(verifyOutput(
754 OutputFile
, Map
->getTriple().getArchName(), Options
.Verbose
));
758 // FIXME: The DwarfLinker can have some very deep recursion that can max
759 // out the (significantly smaller) stack when using threads. We don't
760 // want this limitation when we only have a single thread.
761 if (S
.ThreadsRequested
== 1)
762 LinkLambda(OS
, Options
.LinkOpts
);
764 Threads
.async(LinkLambda
, OS
, Options
.LinkOpts
);
771 (*Repro
)->generate();
776 if (NeedsTempFiles
) {
777 // Universal Mach-O files can't have an archicture slice that starts
778 // beyond the 4GB boundary. "lipo" can creeate a 64 bit universal header,
779 // but not all tools can parse these files so we want to return an error
780 // if the file can't be encoded as a file with a 32 bit universal header.
781 // To detect this, we check the size of each architecture's skinny Mach-O
782 // file and add up the offsets. If they exceed 4GB, then we return an
785 // First we compute the right offset where the first architecture will fit
786 // followin the 32 bit universal header. The 32 bit universal header
787 // starts with a uint32_t magic and a uint32_t number of architecture
788 // infos. Then it is followed by 5 uint32_t values for each architecture.
789 // So we set the start offset to the right value so we can calculate the
790 // exact offset that the first architecture slice can start at.
791 constexpr uint64_t MagicAndCountSize
= 2 * 4;
792 constexpr uint64_t UniversalArchInfoSize
= 5 * 4;
793 uint64_t FileOffset
= MagicAndCountSize
+
794 UniversalArchInfoSize
* TempFiles
.size();
795 for (const auto &File
: TempFiles
) {
796 ErrorOr
<vfs::Status
> stat
= Options
.LinkOpts
.VFS
->status(File
.path());
799 FileOffset
+= stat
->getSize();
800 if (FileOffset
> UINT32_MAX
) {
801 WithColor::error() << "the univesral binary has a slice with an "
802 "offset exceeds 4GB and will produce an invalid Mach-O file.";
806 if (!MachOUtils::generateUniversalBinary(TempFiles
,
807 OutputLocationOrErr
->DWARFFile
,
808 Options
.LinkOpts
, SDKPath
))