blob: 60ba14325549424a5d5cec2ff08e57f373f108de [file] [log] [blame]
Inna Palantff3f07a2019-07-11 16:15:26 -07001//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
2//
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -08003// 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
Inna Palantff3f07a2019-07-11 16:15:26 -07006//
7//===----------------------------------------------------------------------===//
8//
9// This program is a utility that aims to be a dropin replacement for Darwin's
10// dsymutil.
11//===----------------------------------------------------------------------===//
12
13#include "dsymutil.h"
14#include "BinaryHolder.h"
15#include "CFBundle.h"
16#include "DebugMap.h"
17#include "LinkUtils.h"
18#include "MachOUtils.h"
19#include "llvm/ADT/SmallString.h"
20#include "llvm/ADT/SmallVector.h"
21#include "llvm/ADT/StringExtras.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/ADT/Triple.h"
24#include "llvm/DebugInfo/DIContext.h"
25#include "llvm/DebugInfo/DWARF/DWARFContext.h"
26#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
27#include "llvm/Object/Binary.h"
28#include "llvm/Object/MachO.h"
29#include "llvm/Support/CommandLine.h"
30#include "llvm/Support/FileSystem.h"
31#include "llvm/Support/InitLLVM.h"
32#include "llvm/Support/ManagedStatic.h"
33#include "llvm/Support/Path.h"
34#include "llvm/Support/TargetSelect.h"
35#include "llvm/Support/ThreadPool.h"
36#include "llvm/Support/WithColor.h"
37#include "llvm/Support/raw_ostream.h"
38#include "llvm/Support/thread.h"
39#include <algorithm>
40#include <cstdint>
41#include <cstdlib>
42#include <string>
43#include <system_error>
44
45using namespace llvm;
46using namespace llvm::cl;
47using namespace llvm::dsymutil;
48using namespace object;
49
50static OptionCategory DsymCategory("Specific Options");
51static opt<bool> Help("h", desc("Alias for -help"), Hidden);
52static opt<bool> Version("v", desc("Alias for -version"), Hidden);
53
54static list<std::string> InputFiles(Positional, OneOrMore,
55 desc("<input files>"), cat(DsymCategory));
56
57static opt<std::string>
58 OutputFileOpt("o",
59 desc("Specify the output file. default: <input file>.dwarf"),
60 value_desc("filename"), cat(DsymCategory));
61static alias OutputFileOptA("out", desc("Alias for -o"),
62 aliasopt(OutputFileOpt));
63
64static opt<std::string> OsoPrependPath(
65 "oso-prepend-path",
66 desc("Specify a directory to prepend to the paths of object files."),
67 value_desc("path"), cat(DsymCategory));
68
69static opt<bool> Assembly(
70 "S",
71 desc("Output textual assembly instead of a binary dSYM companion file."),
72 init(false), cat(DsymCategory), cl::Hidden);
73
74static opt<bool> DumpStab(
75 "symtab",
76 desc("Dumps the symbol table found in executable or object file(s) and\n"
77 "exits."),
78 init(false), cat(DsymCategory));
79static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
80
81static opt<bool> FlatOut("flat",
82 desc("Produce a flat dSYM file (not a bundle)."),
83 init(false), cat(DsymCategory));
84static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
85
86static opt<bool> Minimize(
87 "minimize",
88 desc("When used when creating a dSYM file with Apple accelerator tables,\n"
89 "this option will suppress the emission of the .debug_inlines, \n"
90 ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n"
91 "has better equivalents: .apple_names and .apple_types. When used in\n"
92 "conjunction with --update option, this option will cause redundant\n"
93 "accelerator tables to be removed."),
94 init(false), cat(DsymCategory));
95static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize));
96
97static opt<bool> Update(
98 "update",
99 desc("Updates existing dSYM files to contain the latest accelerator\n"
100 "tables and other DWARF optimizations."),
101 init(false), cat(DsymCategory));
102static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
103
104static opt<std::string> SymbolMap(
105 "symbol-map",
106 desc("Updates the existing dSYMs inplace using symbol map specified."),
107 value_desc("bcsymbolmap"), cat(DsymCategory));
108
109static cl::opt<AccelTableKind> AcceleratorTable(
110 "accelerator", cl::desc("Output accelerator tables."),
111 cl::values(clEnumValN(AccelTableKind::Default, "Default",
112 "Default for input."),
113 clEnumValN(AccelTableKind::Apple, "Apple", "Apple"),
114 clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")),
115 cl::init(AccelTableKind::Default), cat(DsymCategory));
116
117static opt<unsigned> NumThreads(
118 "num-threads",
119 desc("Specifies the maximum number (n) of simultaneous threads to use\n"
120 "when linking multiple architectures."),
121 value_desc("n"), init(0), cat(DsymCategory));
122static alias NumThreadsA("j", desc("Alias for --num-threads"),
123 aliasopt(NumThreads));
124
125static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
126 cat(DsymCategory));
127
128static opt<bool>
129 NoOutput("no-output",
130 desc("Do the link in memory, but do not emit the result file."),
131 init(false), cat(DsymCategory));
132
133static opt<bool>
134 NoTimestamp("no-swiftmodule-timestamp",
135 desc("Don't check timestamp for swiftmodule files."),
136 init(false), cat(DsymCategory));
137
138static list<std::string> ArchFlags(
139 "arch",
140 desc("Link DWARF debug information only for specified CPU architecture\n"
141 "types. This option can be specified multiple times, once for each\n"
142 "desired architecture. All CPU architectures will be linked by\n"
143 "default."),
144 value_desc("arch"), ZeroOrMore, cat(DsymCategory));
145
146static opt<bool>
147 NoODR("no-odr",
148 desc("Do not use ODR (One Definition Rule) for type uniquing."),
149 init(false), cat(DsymCategory));
150
151static opt<bool> DumpDebugMap(
152 "dump-debug-map",
153 desc("Parse and dump the debug map to standard output. Not DWARF link "
154 "will take place."),
155 init(false), cat(DsymCategory));
156
157static opt<bool> InputIsYAMLDebugMap(
158 "y", desc("Treat the input file is a YAML debug map rather than a binary."),
159 init(false), cat(DsymCategory));
160
161static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."),
162 cat(DsymCategory));
163
164static opt<std::string>
165 Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."),
166 cat(DsymCategory));
167
168static opt<bool>
169 PaperTrailWarnings("papertrail",
170 desc("Embed warnings in the linked DWARF debug info."),
171 cat(DsymCategory));
172
173static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
174 if (NoOutput)
175 return Error::success();
176
177 // Create plist file to write to.
178 llvm::SmallString<128> InfoPlist(BundleRoot);
179 llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
180 std::error_code EC;
181 llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
182 if (EC)
183 return make_error<StringError>(
184 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
185
186 CFBundleInfo BI = getBundleInfo(Bin);
187
188 if (BI.IDStr.empty()) {
189 llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
190 if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
191 BI.IDStr = llvm::sys::path::stem(BundleID);
192 else
193 BI.IDStr = BundleID;
194 }
195
196 // Print out information to the plist file.
197 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
198 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
199 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
200 << "<plist version=\"1.0\">\n"
201 << "\t<dict>\n"
202 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
203 << "\t\t<string>English</string>\n"
204 << "\t\t<key>CFBundleIdentifier</key>\n"
205 << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
206 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
207 << "\t\t<string>6.0</string>\n"
208 << "\t\t<key>CFBundlePackageType</key>\n"
209 << "\t\t<string>dSYM</string>\n"
210 << "\t\t<key>CFBundleSignature</key>\n"
211 << "\t\t<string>\?\?\?\?</string>\n";
212
213 if (!BI.OmitShortVersion()) {
214 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
215 PL << "\t\t<string>";
216 printHTMLEscaped(BI.ShortVersionStr, PL);
217 PL << "</string>\n";
218 }
219
220 PL << "\t\t<key>CFBundleVersion</key>\n";
221 PL << "\t\t<string>";
222 printHTMLEscaped(BI.VersionStr, PL);
223 PL << "</string>\n";
224
225 if (!Toolchain.empty()) {
226 PL << "\t\t<key>Toolchain</key>\n";
227 PL << "\t\t<string>";
228 printHTMLEscaped(Toolchain, PL);
229 PL << "</string>\n";
230 }
231
232 PL << "\t</dict>\n"
233 << "</plist>\n";
234
235 PL.close();
236 return Error::success();
237}
238
239static Error createBundleDir(llvm::StringRef BundleBase) {
240 if (NoOutput)
241 return Error::success();
242
243 llvm::SmallString<128> Bundle(BundleBase);
244 llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
245 if (std::error_code EC =
246 create_directories(Bundle.str(), true, llvm::sys::fs::perms::all_all))
247 return make_error<StringError>(
248 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
249
250 return Error::success();
251}
252
253static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
254 if (OutputFile == "-") {
255 WithColor::warning() << "verification skipped for " << Arch
256 << "because writing to stdout.\n";
257 return true;
258 }
259
260 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
261 if (!BinOrErr) {
262 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
263 return false;
264 }
265
266 Binary &Binary = *BinOrErr.get().getBinary();
267 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
268 raw_ostream &os = Verbose ? errs() : nulls();
269 os << "Verifying DWARF for architecture: " << Arch << "\n";
270 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
271 DIDumpOptions DumpOpts;
272 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
273 if (!success)
274 WithColor::error() << "verification failed for " << Arch << '\n';
275 return success;
276 }
277
278 return false;
279}
280
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800281namespace {
282struct OutputLocation {
283 OutputLocation(std::string DWARFFile,
284 llvm::Optional<std::string> ResourceDir = {})
285 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
286 /// This method is a workaround for older compilers.
287 llvm::Optional<std::string> getResourceDir() const { return ResourceDir; }
288 std::string DWARFFile;
289 llvm::Optional<std::string> ResourceDir;
290};
291}
292
293static Expected<OutputLocation> getOutputFileName(llvm::StringRef InputFile) {
Inna Palantff3f07a2019-07-11 16:15:26 -0700294 if (OutputFileOpt == "-")
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800295 return OutputLocation(OutputFileOpt);
Inna Palantff3f07a2019-07-11 16:15:26 -0700296
297 // When updating, do in place replacement.
298 if (OutputFileOpt.empty() && (Update || !SymbolMap.empty()))
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800299 return OutputLocation(InputFile);
Inna Palantff3f07a2019-07-11 16:15:26 -0700300
301 // If a flat dSYM has been requested, things are pretty simple.
302 if (FlatOut) {
303 if (OutputFileOpt.empty()) {
304 if (InputFile == "-")
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800305 return OutputLocation{"a.out.dwarf", {}};
306 return OutputLocation((InputFile + ".dwarf").str());
Inna Palantff3f07a2019-07-11 16:15:26 -0700307 }
308
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800309 return OutputLocation(OutputFileOpt);
Inna Palantff3f07a2019-07-11 16:15:26 -0700310 }
311
312 // We need to create/update a dSYM bundle.
313 // A bundle hierarchy looks like this:
314 // <bundle name>.dSYM/
315 // Contents/
316 // Info.plist
317 // Resources/
318 // DWARF/
319 // <DWARF file(s)>
320 std::string DwarfFile =
321 InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800322 llvm::SmallString<128> Path(OutputFileOpt);
323 if (Path.empty())
324 Path = DwarfFile + ".dSYM";
325 if (auto E = createBundleDir(Path))
Inna Palantff3f07a2019-07-11 16:15:26 -0700326 return std::move(E);
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800327 if (auto E = createPlistFile(DwarfFile, Path))
Inna Palantff3f07a2019-07-11 16:15:26 -0700328 return std::move(E);
329
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800330 llvm::sys::path::append(Path, "Contents", "Resources");
331 std::string ResourceDir = Path.str();
332 llvm::sys::path::append(Path, "DWARF", llvm::sys::path::filename(DwarfFile));
333 return OutputLocation(Path.str(), ResourceDir);
Inna Palantff3f07a2019-07-11 16:15:26 -0700334}
335
336/// Parses the command line options into the LinkOptions struct and performs
337/// some sanity checking. Returns an error in case the latter fails.
338static Expected<LinkOptions> getOptions() {
339 LinkOptions Options;
340
341 Options.Verbose = Verbose;
342 Options.NoOutput = NoOutput;
343 Options.NoODR = NoODR;
344 Options.Minimize = Minimize;
345 Options.Update = Update;
346 Options.NoTimestamp = NoTimestamp;
347 Options.PrependPath = OsoPrependPath;
348 Options.TheAccelTableKind = AcceleratorTable;
349
350 if (!SymbolMap.empty())
351 Options.Update = true;
352
353 if (Assembly)
354 Options.FileType = OutputFileType::Assembly;
355
356 if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") !=
357 InputFiles.end()) {
358 // FIXME: We cannot use stdin for an update because stdin will be
359 // consumed by the BinaryHolder during the debugmap parsing, and
360 // then we will want to consume it again in DwarfLinker. If we
361 // used a unique BinaryHolder object that could cache multiple
362 // binaries this restriction would go away.
363 return make_error<StringError>(
364 "standard input cannot be used as input for a dSYM update.",
365 inconvertibleErrorCode());
366 }
367
368 if (NumThreads == 0)
369 Options.Threads = llvm::thread::hardware_concurrency();
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800370 else
371 Options.Threads = NumThreads;
Inna Palantff3f07a2019-07-11 16:15:26 -0700372 if (DumpDebugMap || Verbose)
373 Options.Threads = 1;
374
375 return Options;
376}
377
378/// Return a list of input files. This function has logic for dealing with the
379/// special case where we might have dSYM bundles as input. The function
380/// returns an error when the directory structure doesn't match that of a dSYM
381/// bundle.
382static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) {
383 if (!DsymAsInput)
384 return InputFiles;
385
386 // If we are updating, we might get dSYM bundles as input.
387 std::vector<std::string> Inputs;
388 for (const auto &Input : InputFiles) {
389 if (!llvm::sys::fs::is_directory(Input)) {
390 Inputs.push_back(Input);
391 continue;
392 }
393
394 // Make sure that we're dealing with a dSYM bundle.
395 SmallString<256> BundlePath(Input);
396 sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
397 if (!llvm::sys::fs::is_directory(BundlePath))
398 return make_error<StringError>(
399 Input + " is a directory, but doesn't look like a dSYM bundle.",
400 inconvertibleErrorCode());
401
402 // Create a directory iterator to iterate over all the entries in the
403 // bundle.
404 std::error_code EC;
405 llvm::sys::fs::directory_iterator DirIt(BundlePath, EC);
406 llvm::sys::fs::directory_iterator DirEnd;
407 if (EC)
408 return errorCodeToError(EC);
409
410 // Add each entry to the list of inputs.
411 while (DirIt != DirEnd) {
412 Inputs.push_back(DirIt->path());
413 DirIt.increment(EC);
414 if (EC)
415 return errorCodeToError(EC);
416 }
417 }
418 return Inputs;
419}
420
421int main(int argc, char **argv) {
422 InitLLVM X(argc, argv);
423
424 void *P = (void *)(intptr_t)getOutputFileName;
425 std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
426 SDKPath = llvm::sys::path::parent_path(SDKPath);
427
428 HideUnrelatedOptions({&DsymCategory, &ColorCategory});
429 llvm::cl::ParseCommandLineOptions(
430 argc, argv,
431 "manipulate archived DWARF debug symbol files.\n\n"
432 "dsymutil links the DWARF debug information found in the object files\n"
433 "for the executable <input file> by using debug symbols information\n"
434 "contained in its symbol table.\n");
435
436 if (Help) {
437 PrintHelpMessage();
438 return 0;
439 }
440
441 if (Version) {
442 llvm::cl::PrintVersionMessage();
443 return 0;
444 }
445
446 auto OptionsOrErr = getOptions();
447 if (!OptionsOrErr) {
448 WithColor::error() << toString(OptionsOrErr.takeError());
449 return 1;
450 }
451
452 llvm::InitializeAllTargetInfos();
453 llvm::InitializeAllTargetMCs();
454 llvm::InitializeAllTargets();
455 llvm::InitializeAllAsmPrinters();
456
457 auto InputsOrErr = getInputs(OptionsOrErr->Update);
458 if (!InputsOrErr) {
459 WithColor::error() << toString(InputsOrErr.takeError()) << '\n';
460 return 1;
461 }
462
463 if (!FlatOut && OutputFileOpt == "-") {
464 WithColor::error() << "cannot emit to standard output without --flat\n";
465 return 1;
466 }
467
468 if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) {
469 WithColor::error() << "cannot use -o with multiple inputs in flat mode\n";
470 return 1;
471 }
472
473 if (InputFiles.size() > 1 && !SymbolMap.empty() &&
474 !llvm::sys::fs::is_directory(SymbolMap)) {
475 WithColor::error() << "when unobfuscating multiple files, --symbol-map "
476 << "needs to point to a directory.\n";
477 return 1;
478 }
479
480 if (getenv("RC_DEBUG_OPTIONS"))
481 PaperTrailWarnings = true;
482
483 if (PaperTrailWarnings && InputIsYAMLDebugMap)
484 WithColor::warning()
485 << "Paper trail warnings are not supported for YAML input";
486
487 for (const auto &Arch : ArchFlags)
488 if (Arch != "*" && Arch != "all" &&
489 !llvm::object::MachOObjectFile::isValidArch(Arch)) {
490 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
491 return 1;
492 }
493
494 SymbolMapLoader SymMapLoader(SymbolMap);
495
496 for (auto &InputFile : *InputsOrErr) {
497 // Dump the symbol table for each input file and requested arch
498 if (DumpStab) {
499 if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
500 return 1;
501 continue;
502 }
503
504 auto DebugMapPtrsOrErr =
505 parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings,
506 Verbose, InputIsYAMLDebugMap);
507
508 if (auto EC = DebugMapPtrsOrErr.getError()) {
509 WithColor::error() << "cannot parse the debug map for '" << InputFile
510 << "': " << EC.message() << '\n';
511 return 1;
512 }
513
514 if (OptionsOrErr->Update) {
515 // The debug map should be empty. Add one object file corresponding to
516 // the input file.
517 for (auto &Map : *DebugMapPtrsOrErr)
518 Map->addDebugMapObject(InputFile,
519 llvm::sys::TimePoint<std::chrono::seconds>());
520 }
521
522 // Ensure that the debug map is not empty (anymore).
523 if (DebugMapPtrsOrErr->empty()) {
524 WithColor::error() << "no architecture to link\n";
525 return 1;
526 }
527
528 // Shared a single binary holder for all the link steps.
529 BinaryHolder BinHolder;
530
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800531 unsigned ThreadCount =
Inna Palantff3f07a2019-07-11 16:15:26 -0700532 std::min<unsigned>(OptionsOrErr->Threads, DebugMapPtrsOrErr->size());
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800533 llvm::ThreadPool Threads(ThreadCount);
Inna Palantff3f07a2019-07-11 16:15:26 -0700534
535 // If there is more than one link to execute, we need to generate
536 // temporary files.
537 bool NeedsTempFiles =
538 !DumpDebugMap && (OutputFileOpt != "-") &&
539 (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update);
540
541 llvm::SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
542 std::atomic_char AllOK(1);
543 for (auto &Map : *DebugMapPtrsOrErr) {
544 if (Verbose || DumpDebugMap)
545 Map->print(llvm::outs());
546
547 if (DumpDebugMap)
548 continue;
549
550 if (!SymbolMap.empty())
551 OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map);
552
553 if (Map->begin() == Map->end())
554 WithColor::warning()
555 << "no debug symbols in executable (-arch "
556 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
557
558 // Using a std::shared_ptr rather than std::unique_ptr because move-only
559 // types don't work with std::bind in the ThreadPool implementation.
560 std::shared_ptr<raw_fd_ostream> OS;
561
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800562 Expected<OutputLocation> OutputLocationOrErr =
563 getOutputFileName(InputFile);
564 if (!OutputLocationOrErr) {
565 WithColor::error() << toString(OutputLocationOrErr.takeError());
Inna Palantff3f07a2019-07-11 16:15:26 -0700566 return 1;
567 }
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800568 OptionsOrErr->ResourceDir = OutputLocationOrErr->getResourceDir();
Inna Palantff3f07a2019-07-11 16:15:26 -0700569
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800570 std::string OutputFile = OutputLocationOrErr->DWARFFile;
Inna Palantff3f07a2019-07-11 16:15:26 -0700571 if (NeedsTempFiles) {
572 TempFiles.emplace_back(Map->getTriple().getArchName().str());
573
574 auto E = TempFiles.back().createTempFile();
575 if (E) {
576 WithColor::error() << toString(std::move(E));
577 return 1;
578 }
579
580 auto &TempFile = *(TempFiles.back().File);
581 OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
582 /*shouldClose*/ false);
583 OutputFile = TempFile.TmpName;
584 } else {
585 std::error_code EC;
586 OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
587 sys::fs::F_None);
588 if (EC) {
589 WithColor::error() << OutputFile << ": " << EC.message();
590 return 1;
591 }
592 }
593
594 auto LinkLambda = [&,
595 OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
596 AllOK.fetch_and(linkDwarf(*Stream, BinHolder, *Map, *OptionsOrErr));
597 Stream->flush();
598 if (Verify && !NoOutput)
599 AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
600 };
601
602 // FIXME: The DwarfLinker can have some very deep recursion that can max
603 // out the (significantly smaller) stack when using threads. We don't
604 // want this limitation when we only have a single thread.
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800605 if (ThreadCount == 1)
Inna Palantff3f07a2019-07-11 16:15:26 -0700606 LinkLambda(OS);
607 else
608 Threads.async(LinkLambda, OS);
609 }
610
611 Threads.wait();
612
613 if (!AllOK)
614 return 1;
615
616 if (NeedsTempFiles) {
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800617 Expected<OutputLocation> OutputLocationOrErr = getOutputFileName(InputFile);
618 if (!OutputLocationOrErr) {
619 WithColor::error() << toString(OutputLocationOrErr.takeError());
Inna Palantff3f07a2019-07-11 16:15:26 -0700620 return 1;
621 }
Chih-Hung Hsieh43f06942019-12-19 15:01:08 -0800622 if (!MachOUtils::generateUniversalBinary(TempFiles,
623 OutputLocationOrErr->DWARFFile,
Inna Palantff3f07a2019-07-11 16:15:26 -0700624 *OptionsOrErr, SDKPath))
625 return 1;
626 }
627 }
628
629 return 0;
630}