blob: ec8d0507b08a4178d8a05fdf19c7b7c323484a5f [file] [log] [blame]
Eugene Zelenko66f724e2017-11-01 21:16:06 +00001//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
Frederic Riss31e081e2014-12-12 17:31:24 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
Jonas Devlieghere956dada2018-10-23 00:32:22 +000010// This program is a utility that aims to be a dropin replacement for Darwin's
11// dsymutil.
Frederic Riss31e081e2014-12-12 17:31:24 +000012//===----------------------------------------------------------------------===//
13
Jonas Devlieghereac330ef2017-10-31 13:54:15 +000014#include "dsymutil.h"
Jonas Devlieghere4d3e6c12018-06-29 16:50:41 +000015#include "BinaryHolder.h"
Jonas Devliegherebdc984c2017-11-30 10:25:28 +000016#include "CFBundle.h"
Frederic Riss31e081e2014-12-12 17:31:24 +000017#include "DebugMap.h"
Jonas Devlieghere928fea22018-06-27 16:13:40 +000018#include "LinkUtils.h"
Frederic Riss7a425782015-08-05 18:27:44 +000019#include "MachOUtils.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000020#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/SmallVector.h"
Jonas Devliegheree22dca52018-05-30 17:47:11 +000022#include "llvm/ADT/StringExtras.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000023#include "llvm/ADT/StringRef.h"
24#include "llvm/ADT/Triple.h"
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +000025#include "llvm/DebugInfo/DIContext.h"
26#include "llvm/DebugInfo/DWARF/DWARFContext.h"
27#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
28#include "llvm/Object/Binary.h"
Frederic Riss1b709aa2015-08-05 22:33:28 +000029#include "llvm/Object/MachO.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000030#include "llvm/Support/CommandLine.h"
Frederic Rissaf511be2015-08-06 21:05:06 +000031#include "llvm/Support/FileSystem.h"
Rui Ueyama0b9d56a2018-04-13 18:26:06 +000032#include "llvm/Support/InitLLVM.h"
Frederic Riss31e081e2014-12-12 17:31:24 +000033#include "llvm/Support/ManagedStatic.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000034#include "llvm/Support/Path.h"
Frederic Riss2929a7a2015-02-28 00:29:11 +000035#include "llvm/Support/TargetSelect.h"
Jonas Devlieghereac330ef2017-10-31 13:54:15 +000036#include "llvm/Support/ThreadPool.h"
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +000037#include "llvm/Support/WithColor.h"
Jonas Devlieghereac330ef2017-10-31 13:54:15 +000038#include "llvm/Support/raw_ostream.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000039#include "llvm/Support/thread.h"
40#include <algorithm>
Frederic Riss0dbce2b2015-10-09 15:04:05 +000041#include <cstdint>
Eugene Zelenko66f724e2017-11-01 21:16:06 +000042#include <cstdlib>
Frederic Riss31e081e2014-12-12 17:31:24 +000043#include <string>
Eugene Zelenko66f724e2017-11-01 21:16:06 +000044#include <system_error>
Frederic Riss31e081e2014-12-12 17:31:24 +000045
Rafael Espindola5feaa632017-11-15 20:55:53 +000046using namespace llvm;
Eugene Zelenko66f724e2017-11-01 21:16:06 +000047using namespace llvm::cl;
Frederic Riss31e081e2014-12-12 17:31:24 +000048using namespace llvm::dsymutil;
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +000049using namespace object;
Frederic Riss31e081e2014-12-12 17:31:24 +000050
Eugene Zelenko66f724e2017-11-01 21:16:06 +000051static OptionCategory DsymCategory("Specific Options");
Frederic Rissba72b702015-07-29 22:29:46 +000052static opt<bool> Help("h", desc("Alias for -help"), Hidden);
Frederic Riss3134ceb2015-07-29 22:29:50 +000053static opt<bool> Version("v", desc("Alias for -version"), Hidden);
Frederic Rissba72b702015-07-29 22:29:46 +000054
Frederic Rissd353c912015-07-31 20:22:20 +000055static list<std::string> InputFiles(Positional, OneOrMore,
56 desc("<input files>"), cat(DsymCategory));
Frederic Riss31e081e2014-12-12 17:31:24 +000057
Frederic Riss79533822015-06-03 16:57:07 +000058static opt<std::string>
59 OutputFileOpt("o",
60 desc("Specify the output file. default: <input file>.dwarf"),
Frederic Rissba72b702015-07-29 22:29:46 +000061 value_desc("filename"), cat(DsymCategory));
Jonas Devlieghere20767d12019-01-07 23:27:25 +000062static alias OutputFileOptA("out", desc("Alias for -o"),
63 aliasopt(OutputFileOpt));
Frederic Riss77f4ca12015-02-28 00:29:03 +000064
Frederic Riss79533822015-06-03 16:57:07 +000065static opt<std::string> OsoPrependPath(
66 "oso-prepend-path",
67 desc("Specify a directory to prepend to the paths of object files."),
Frederic Rissba72b702015-07-29 22:29:46 +000068 value_desc("path"), cat(DsymCategory));
Frederic Riss31e081e2014-12-12 17:31:24 +000069
Jonas Devliegherea5d860c2018-07-09 16:58:48 +000070static opt<bool> Assembly(
71 "S",
72 desc("Output textual assembly instead of a binary dSYM companion file."),
73 init(false), cat(DsymCategory), cl::Hidden);
74
Frederic Rissa8a34c52015-08-31 00:29:09 +000075static opt<bool> DumpStab(
76 "symtab",
77 desc("Dumps the symbol table found in executable or object file(s) and\n"
78 "exits."),
79 init(false), cat(DsymCategory));
80static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
81
Frederic Risse88f2f32015-08-06 21:05:01 +000082static opt<bool> FlatOut("flat",
83 desc("Produce a flat dSYM file (not a bundle)."),
84 init(false), cat(DsymCategory));
85static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
86
Jonas Devlieghere2fa8e762018-01-30 19:54:16 +000087static opt<bool> Minimize(
88 "minimize",
Jonas Devlieghere7c767bd2018-07-25 23:01:38 +000089 desc("When used when creating a dSYM file with Apple accelerator tables,\n"
90 "this option will suppress the emission of the .debug_inlines, \n"
91 ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n"
92 "has better equivalents: .apple_names and .apple_types. When used in\n"
Jonas Devlieghere61693462018-02-08 10:48:54 +000093 "conjunction with --update option, this option will cause redundant\n"
94 "accelerator tables to be removed."),
Jonas Devlieghere2fa8e762018-01-30 19:54:16 +000095 init(false), cat(DsymCategory));
96static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize));
97
Jonas Devlieghere61693462018-02-08 10:48:54 +000098static opt<bool> Update(
99 "update",
100 desc("Updates existing dSYM files to contain the latest accelerator\n"
Jonas Devlieghere7c767bd2018-07-25 23:01:38 +0000101 "tables and other DWARF optimizations."),
Jonas Devlieghere61693462018-02-08 10:48:54 +0000102 init(false), cat(DsymCategory));
103static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
104
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000105static opt<std::string> SymbolMap(
106 "symbol-map",
107 desc("Updates the existing dSYMs inplace using symbol map specified."),
108 value_desc("bcsymbolmap"), cat(DsymCategory));
109
Jonas Devlieghere7c767bd2018-07-25 23:01:38 +0000110static cl::opt<AccelTableKind> AcceleratorTable(
111 "accelerator", cl::desc("Output accelerator tables."),
112 cl::values(clEnumValN(AccelTableKind::Default, "Default",
113 "Default for input."),
114 clEnumValN(AccelTableKind::Apple, "Apple", "Apple"),
115 clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")),
116 cl::init(AccelTableKind::Default), cat(DsymCategory));
117
Jonas Devlieghereef7a2062017-11-01 17:15:29 +0000118static opt<unsigned> NumThreads(
119 "num-threads",
Jonas Devlieghereac330ef2017-10-31 13:54:15 +0000120 desc("Specifies the maximum number (n) of simultaneous threads to use\n"
121 "when linking multiple architectures."),
122 value_desc("n"), init(0), cat(DsymCategory));
Jonas Devlieghereef7a2062017-11-01 17:15:29 +0000123static alias NumThreadsA("j", desc("Alias for --num-threads"),
124 aliasopt(NumThreads));
Jonas Devlieghereac330ef2017-10-31 13:54:15 +0000125
Frederic Rissba72b702015-07-29 22:29:46 +0000126static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
127 cat(DsymCategory));
Frederic Riss31e081e2014-12-12 17:31:24 +0000128
Frederic Riss79533822015-06-03 16:57:07 +0000129static opt<bool>
130 NoOutput("no-output",
131 desc("Do the link in memory, but do not emit the result file."),
Frederic Rissba72b702015-07-29 22:29:46 +0000132 init(false), cat(DsymCategory));
Eugene Zelenko66f724e2017-11-01 21:16:06 +0000133
Jonas Devliegheref16734c2017-10-13 14:41:23 +0000134static opt<bool>
135 NoTimestamp("no-swiftmodule-timestamp",
136 desc("Don't check timestamp for swiftmodule files."),
137 init(false), cat(DsymCategory));
Eugene Zelenko66f724e2017-11-01 21:16:06 +0000138
Frederic Riss1b709aa2015-08-05 22:33:28 +0000139static list<std::string> ArchFlags(
140 "arch",
141 desc("Link DWARF debug information only for specified CPU architecture\n"
142 "types. This option can be specified multiple times, once for each\n"
Jonas Devlieghere2f759d42017-11-02 18:44:54 +0000143 "desired architecture. All CPU architectures will be linked by\n"
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000144 "default."),
145 value_desc("arch"), ZeroOrMore, cat(DsymCategory));
Frederic Riss1b709aa2015-08-05 22:33:28 +0000146
Frederic Riss309fcf82015-07-21 22:41:43 +0000147static opt<bool>
148 NoODR("no-odr",
149 desc("Do not use ODR (One Definition Rule) for type uniquing."),
Frederic Rissba72b702015-07-29 22:29:46 +0000150 init(false), cat(DsymCategory));
Frederic Riss309fcf82015-07-21 22:41:43 +0000151
Frederic Riss7745dac2015-06-03 16:57:12 +0000152static opt<bool> DumpDebugMap(
153 "dump-debug-map",
154 desc("Parse and dump the debug map to standard output. Not DWARF link "
155 "will take place."),
Frederic Rissba72b702015-07-29 22:29:46 +0000156 init(false), cat(DsymCategory));
Frederic Riss696c93c2015-06-03 20:29:24 +0000157
158static opt<bool> InputIsYAMLDebugMap(
159 "y", desc("Treat the input file is a YAML debug map rather than a binary."),
Frederic Rissba72b702015-07-29 22:29:46 +0000160 init(false), cat(DsymCategory));
Frederic Riss31e081e2014-12-12 17:31:24 +0000161
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +0000162static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."),
163 cat(DsymCategory));
164
Jonas Devliegheree5edcce2018-03-08 10:39:12 +0000165static opt<std::string>
166 Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."),
167 cat(DsymCategory));
168
Jonas Devlieghere67eb8fd2018-04-02 10:40:43 +0000169static opt<bool>
170 PaperTrailWarnings("papertrail",
171 desc("Embed warnings in the linked DWARF debug info."),
172 cat(DsymCategory));
173
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000174static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
Frederic Rissaf511be2015-08-06 21:05:06 +0000175 if (NoOutput)
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000176 return Error::success();
Frederic Rissaf511be2015-08-06 21:05:06 +0000177
178 // Create plist file to write to.
179 llvm::SmallString<128> InfoPlist(BundleRoot);
180 llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
181 std::error_code EC;
182 llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000183 if (EC)
184 return make_error<StringError>(
185 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
Frederic Rissaf511be2015-08-06 21:05:06 +0000186
Jonas Devliegherebdc984c2017-11-30 10:25:28 +0000187 CFBundleInfo BI = getBundleInfo(Bin);
Frederic Rissaf511be2015-08-06 21:05:06 +0000188
Jonas Devliegherebdc984c2017-11-30 10:25:28 +0000189 if (BI.IDStr.empty()) {
190 llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
191 if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
192 BI.IDStr = llvm::sys::path::stem(BundleID);
193 else
194 BI.IDStr = BundleID;
195 }
Frederic Rissaf511be2015-08-06 21:05:06 +0000196
197 // Print out information to the plist file.
198 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
199 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
200 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
201 << "<plist version=\"1.0\">\n"
202 << "\t<dict>\n"
203 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
204 << "\t\t<string>English</string>\n"
205 << "\t\t<key>CFBundleIdentifier</key>\n"
Jonas Devliegherebdc984c2017-11-30 10:25:28 +0000206 << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
Frederic Rissaf511be2015-08-06 21:05:06 +0000207 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
208 << "\t\t<string>6.0</string>\n"
209 << "\t\t<key>CFBundlePackageType</key>\n"
210 << "\t\t<string>dSYM</string>\n"
211 << "\t\t<key>CFBundleSignature</key>\n"
Jonas Devliegherebdc984c2017-11-30 10:25:28 +0000212 << "\t\t<string>\?\?\?\?</string>\n";
213
Jonas Devliegheree22dca52018-05-30 17:47:11 +0000214 if (!BI.OmitShortVersion()) {
215 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
216 PL << "\t\t<string>";
Jonas Devlieghere7eeba252018-05-31 17:01:42 +0000217 printHTMLEscaped(BI.ShortVersionStr, PL);
Jonas Devliegheree22dca52018-05-30 17:47:11 +0000218 PL << "</string>\n";
219 }
Jonas Devliegherebdc984c2017-11-30 10:25:28 +0000220
Jonas Devliegheree22dca52018-05-30 17:47:11 +0000221 PL << "\t\t<key>CFBundleVersion</key>\n";
222 PL << "\t\t<string>";
Jonas Devlieghere7eeba252018-05-31 17:01:42 +0000223 printHTMLEscaped(BI.VersionStr, PL);
Jonas Devliegheree22dca52018-05-30 17:47:11 +0000224 PL << "</string>\n";
Jonas Devliegheree5edcce2018-03-08 10:39:12 +0000225
Jonas Devliegheree22dca52018-05-30 17:47:11 +0000226 if (!Toolchain.empty()) {
227 PL << "\t\t<key>Toolchain</key>\n";
228 PL << "\t\t<string>";
Jonas Devlieghere7eeba252018-05-31 17:01:42 +0000229 printHTMLEscaped(Toolchain, PL);
Jonas Devliegheree22dca52018-05-30 17:47:11 +0000230 PL << "</string>\n";
231 }
Jonas Devliegheree5edcce2018-03-08 10:39:12 +0000232
233 PL << "\t</dict>\n"
Frederic Rissaf511be2015-08-06 21:05:06 +0000234 << "</plist>\n";
235
236 PL.close();
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000237 return Error::success();
Frederic Rissaf511be2015-08-06 21:05:06 +0000238}
239
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000240static Error createBundleDir(llvm::StringRef BundleBase) {
Frederic Rissaf511be2015-08-06 21:05:06 +0000241 if (NoOutput)
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000242 return Error::success();
Frederic Rissaf511be2015-08-06 21:05:06 +0000243
244 llvm::SmallString<128> Bundle(BundleBase);
245 llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000246 if (std::error_code EC =
247 create_directories(Bundle.str(), true, llvm::sys::fs::perms::all_all))
248 return make_error<StringError>(
249 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
250
251 return Error::success();
Frederic Rissaf511be2015-08-06 21:05:06 +0000252}
253
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +0000254static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
255 if (OutputFile == "-") {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000256 WithColor::warning() << "verification skipped for " << Arch
257 << "because writing to stdout.\n";
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +0000258 return true;
259 }
260
261 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
262 if (!BinOrErr) {
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000263 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +0000264 return false;
265 }
266
267 Binary &Binary = *BinOrErr.get().getBinary();
268 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
269 raw_ostream &os = Verbose ? errs() : nulls();
270 os << "Verifying DWARF for architecture: " << Arch << "\n";
271 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
272 DIDumpOptions DumpOpts;
273 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
274 if (!success)
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000275 WithColor::error() << "verification failed for " << Arch << '\n';
Jonas Devlieghere5eb1a172017-12-07 11:17:19 +0000276 return success;
277 }
278
279 return false;
280}
281
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000282static Expected<std::string> getOutputFileName(llvm::StringRef InputFile) {
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000283 if (OutputFileOpt == "-")
284 return OutputFileOpt;
285
Jonas Devlieghere61693462018-02-08 10:48:54 +0000286 // When updating, do in place replacement.
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000287 if (OutputFileOpt.empty() && (Update || !SymbolMap.empty()))
Jonas Devlieghere61693462018-02-08 10:48:54 +0000288 return InputFile;
289
290 // If a flat dSYM has been requested, things are pretty simple.
Frederic Rissaf511be2015-08-06 21:05:06 +0000291 if (FlatOut) {
Frederic Rissaf511be2015-08-06 21:05:06 +0000292 if (OutputFileOpt.empty()) {
293 if (InputFile == "-")
294 return "a.out.dwarf";
295 return (InputFile + ".dwarf").str();
296 }
297
298 return OutputFileOpt;
Frederic Risscca89d32015-08-05 18:27:34 +0000299 }
Frederic Rissaf511be2015-08-06 21:05:06 +0000300
301 // We need to create/update a dSYM bundle.
302 // A bundle hierarchy looks like this:
303 // <bundle name>.dSYM/
304 // Contents/
305 // Info.plist
306 // Resources/
307 // DWARF/
308 // <DWARF file(s)>
309 std::string DwarfFile =
310 InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
311 llvm::SmallString<128> BundleDir(OutputFileOpt);
312 if (BundleDir.empty())
313 BundleDir = DwarfFile + ".dSYM";
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000314 if (auto E = createBundleDir(BundleDir))
315 return std::move(E);
316 if (auto E = createPlistFile(DwarfFile, BundleDir))
317 return std::move(E);
Frederic Rissaf511be2015-08-06 21:05:06 +0000318
319 llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
320 llvm::sys::path::filename(DwarfFile));
321 return BundleDir.str();
Frederic Risscca89d32015-08-05 18:27:34 +0000322}
323
Jonas Devlieghere61693462018-02-08 10:48:54 +0000324/// Parses the command line options into the LinkOptions struct and performs
325/// some sanity checking. Returns an error in case the latter fails.
326static Expected<LinkOptions> getOptions() {
327 LinkOptions Options;
328
329 Options.Verbose = Verbose;
330 Options.NoOutput = NoOutput;
331 Options.NoODR = NoODR;
332 Options.Minimize = Minimize;
333 Options.Update = Update;
334 Options.NoTimestamp = NoTimestamp;
335 Options.PrependPath = OsoPrependPath;
Jonas Devlieghere7c767bd2018-07-25 23:01:38 +0000336 Options.TheAccelTableKind = AcceleratorTable;
Jonas Devlieghere61693462018-02-08 10:48:54 +0000337
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000338 if (!SymbolMap.empty())
339 Options.Update = true;
340
Jonas Devliegherea5d860c2018-07-09 16:58:48 +0000341 if (Assembly)
342 Options.FileType = OutputFileType::Assembly;
343
Jonas Devlieghere61693462018-02-08 10:48:54 +0000344 if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") !=
345 InputFiles.end()) {
346 // FIXME: We cannot use stdin for an update because stdin will be
347 // consumed by the BinaryHolder during the debugmap parsing, and
348 // then we will want to consume it again in DwarfLinker. If we
349 // used a unique BinaryHolder object that could cache multiple
350 // binaries this restriction would go away.
351 return make_error<StringError>(
Jonas Devliegherecc46e392018-03-13 15:47:38 +0000352 "standard input cannot be used as input for a dSYM update.",
Jonas Devlieghere61693462018-02-08 10:48:54 +0000353 inconvertibleErrorCode());
354 }
355
Jonas Devlieghere2f039a02018-04-03 18:01:18 +0000356 if (NumThreads == 0)
357 Options.Threads = llvm::thread::hardware_concurrency();
358 if (DumpDebugMap || Verbose)
359 Options.Threads = 1;
360
Jonas Devlieghere61693462018-02-08 10:48:54 +0000361 return Options;
362}
363
364/// Return a list of input files. This function has logic for dealing with the
365/// special case where we might have dSYM bundles as input. The function
366/// returns an error when the directory structure doesn't match that of a dSYM
367/// bundle.
368static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) {
369 if (!DsymAsInput)
370 return InputFiles;
371
372 // If we are updating, we might get dSYM bundles as input.
373 std::vector<std::string> Inputs;
374 for (const auto &Input : InputFiles) {
375 if (!llvm::sys::fs::is_directory(Input)) {
376 Inputs.push_back(Input);
377 continue;
378 }
379
380 // Make sure that we're dealing with a dSYM bundle.
Jonas Devlieghere0648dd92018-02-08 16:31:42 +0000381 SmallString<256> BundlePath(Input);
382 sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
383 if (!llvm::sys::fs::is_directory(BundlePath))
Jonas Devlieghere61693462018-02-08 10:48:54 +0000384 return make_error<StringError>(
385 Input + " is a directory, but doesn't look like a dSYM bundle.",
386 inconvertibleErrorCode());
387
388 // Create a directory iterator to iterate over all the entries in the
389 // bundle.
390 std::error_code EC;
Jonas Devlieghere0648dd92018-02-08 16:31:42 +0000391 llvm::sys::fs::directory_iterator DirIt(BundlePath, EC);
Jonas Devlieghere61693462018-02-08 10:48:54 +0000392 llvm::sys::fs::directory_iterator DirEnd;
393 if (EC)
394 return errorCodeToError(EC);
395
396 // Add each entry to the list of inputs.
397 while (DirIt != DirEnd) {
398 Inputs.push_back(DirIt->path());
399 DirIt.increment(EC);
400 if (EC)
401 return errorCodeToError(EC);
402 }
403 }
404 return Inputs;
405}
406
Frederic Riss31e081e2014-12-12 17:31:24 +0000407int main(int argc, char **argv) {
Rui Ueyama0b9d56a2018-04-13 18:26:06 +0000408 InitLLVM X(argc, argv);
409
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000410 void *P = (void *)(intptr_t)getOutputFileName;
411 std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
Frederic Rissa642d2c2015-10-08 22:35:53 +0000412 SDKPath = llvm::sys::path::parent_path(SDKPath);
Frederic Riss31e081e2014-12-12 17:31:24 +0000413
Jonas Devlieghere2071b7a2018-05-24 11:36:57 +0000414 HideUnrelatedOptions({&DsymCategory, &ColorCategory});
Frederic Rissba72b702015-07-29 22:29:46 +0000415 llvm::cl::ParseCommandLineOptions(
416 argc, argv,
417 "manipulate archived DWARF debug symbol files.\n\n"
418 "dsymutil links the DWARF debug information found in the object files\n"
419 "for the executable <input file> by using debug symbols information\n"
420 "contained in its symbol table.\n");
421
Rafael Espindola14fde142017-09-07 23:30:48 +0000422 if (Help) {
Frederic Rissba72b702015-07-29 22:29:46 +0000423 PrintHelpMessage();
Rafael Espindola14fde142017-09-07 23:30:48 +0000424 return 0;
425 }
Frederic Riss696c93c2015-06-03 20:29:24 +0000426
Frederic Riss3134ceb2015-07-29 22:29:50 +0000427 if (Version) {
428 llvm::cl::PrintVersionMessage();
429 return 0;
430 }
431
Jonas Devlieghere61693462018-02-08 10:48:54 +0000432 auto OptionsOrErr = getOptions();
433 if (!OptionsOrErr) {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000434 WithColor::error() << toString(OptionsOrErr.takeError());
Jonas Devlieghere61693462018-02-08 10:48:54 +0000435 return 1;
436 }
Frederic Riss2929a7a2015-02-28 00:29:11 +0000437
438 llvm::InitializeAllTargetInfos();
439 llvm::InitializeAllTargetMCs();
440 llvm::InitializeAllTargets();
441 llvm::InitializeAllAsmPrinters();
Frederic Rissd9a80bd2015-02-28 00:29:07 +0000442
Jonas Devlieghere61693462018-02-08 10:48:54 +0000443 auto InputsOrErr = getInputs(OptionsOrErr->Update);
444 if (!InputsOrErr) {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000445 WithColor::error() << toString(InputsOrErr.takeError()) << '\n';
Jonas Devlieghere61693462018-02-08 10:48:54 +0000446 return 1;
447 }
448
Frederic Risse88f2f32015-08-06 21:05:01 +0000449 if (!FlatOut && OutputFileOpt == "-") {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000450 WithColor::error() << "cannot emit to standard output without --flat\n";
Frederic Risse88f2f32015-08-06 21:05:01 +0000451 return 1;
452 }
453
Jonas Devlieghere61693462018-02-08 10:48:54 +0000454 if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000455 WithColor::error() << "cannot use -o with multiple inputs in flat mode\n";
Frederic Riss31e081e2014-12-12 17:31:24 +0000456 return 1;
457 }
458
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000459 if (InputFiles.size() > 1 && !SymbolMap.empty() &&
460 !llvm::sys::fs::is_directory(SymbolMap)) {
461 WithColor::error() << "when unobfuscating multiple files, --symbol-map "
462 << "needs to point to a directory.\n";
463 return 1;
464 }
465
Jonas Devlieghere67eb8fd2018-04-02 10:40:43 +0000466 if (getenv("RC_DEBUG_OPTIONS"))
467 PaperTrailWarnings = true;
468
469 if (PaperTrailWarnings && InputIsYAMLDebugMap)
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000470 WithColor::warning()
471 << "Paper trail warnings are not supported for YAML input";
Jonas Devlieghere67eb8fd2018-04-02 10:40:43 +0000472
Frederic Riss1b709aa2015-08-05 22:33:28 +0000473 for (const auto &Arch : ArchFlags)
474 if (Arch != "*" && Arch != "all" &&
475 !llvm::object::MachOObjectFile::isValidArch(Arch)) {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000476 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000477 return 1;
Frederic Riss1b709aa2015-08-05 22:33:28 +0000478 }
479
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000480 SymbolMapLoader SymMapLoader(SymbolMap);
481
Jonas Devlieghere61693462018-02-08 10:48:54 +0000482 for (auto &InputFile : *InputsOrErr) {
Frederic Rissa8a34c52015-08-31 00:29:09 +0000483 // Dump the symbol table for each input file and requested arch
484 if (DumpStab) {
485 if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000486 return 1;
Frederic Rissa8a34c52015-08-31 00:29:09 +0000487 continue;
488 }
489
Jonas Devlieghere67eb8fd2018-04-02 10:40:43 +0000490 auto DebugMapPtrsOrErr =
491 parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings,
492 Verbose, InputIsYAMLDebugMap);
Frederic Riss31e081e2014-12-12 17:31:24 +0000493
Frederic Riss7a425782015-08-05 18:27:44 +0000494 if (auto EC = DebugMapPtrsOrErr.getError()) {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000495 WithColor::error() << "cannot parse the debug map for '" << InputFile
496 << "': " << EC.message() << '\n';
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000497 return 1;
Frederic Rissd353c912015-07-31 20:22:20 +0000498 }
Frederic Riss31e081e2014-12-12 17:31:24 +0000499
Jonas Devlieghere61693462018-02-08 10:48:54 +0000500 if (OptionsOrErr->Update) {
501 // The debug map should be empty. Add one object file corresponding to
502 // the input file.
503 for (auto &Map : *DebugMapPtrsOrErr)
504 Map->addDebugMapObject(InputFile,
505 llvm::sys::TimePoint<std::chrono::seconds>());
506 }
507
508 // Ensure that the debug map is not empty (anymore).
Frederic Riss1b709aa2015-08-05 22:33:28 +0000509 if (DebugMapPtrsOrErr->empty()) {
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000510 WithColor::error() << "no architecture to link\n";
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000511 return 1;
Frederic Riss1b709aa2015-08-05 22:33:28 +0000512 }
513
Jonas Devlieghere4d3e6c12018-06-29 16:50:41 +0000514 // Shared a single binary holder for all the link steps.
Jonas Devlieghere9f33bbfe2018-06-29 16:51:52 +0000515 BinaryHolder BinHolder;
Jonas Devlieghere4d3e6c12018-06-29 16:50:41 +0000516
Jonas Devlieghere2f039a02018-04-03 18:01:18 +0000517 NumThreads =
518 std::min<unsigned>(OptionsOrErr->Threads, DebugMapPtrsOrErr->size());
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000519 llvm::ThreadPool Threads(NumThreads);
Jonas Devlieghereac330ef2017-10-31 13:54:15 +0000520
Frederic Riss7a425782015-08-05 18:27:44 +0000521 // If there is more than one link to execute, we need to generate
522 // temporary files.
Jonas Devlieghere61693462018-02-08 10:48:54 +0000523 bool NeedsTempFiles =
524 !DumpDebugMap && (OutputFileOpt != "-") &&
525 (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update);
526
Jonas Devlieghere58d4a392018-07-29 14:56:15 +0000527 llvm::SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000528 std::atomic_char AllOK(1);
Frederic Riss7a425782015-08-05 18:27:44 +0000529 for (auto &Map : *DebugMapPtrsOrErr) {
530 if (Verbose || DumpDebugMap)
531 Map->print(llvm::outs());
Frederic Rissd353c912015-07-31 20:22:20 +0000532
Frederic Riss7a425782015-08-05 18:27:44 +0000533 if (DumpDebugMap)
534 continue;
Frederic Rissd353c912015-07-31 20:22:20 +0000535
Jonas Devlieghere20767d12019-01-07 23:27:25 +0000536 if (!SymbolMap.empty())
537 OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map);
538
Frederic Riss909125a2015-08-25 18:19:43 +0000539 if (Map->begin() == Map->end())
Jonas Devlieghere5baab4c2018-04-14 21:36:42 +0000540 WithColor::warning()
541 << "no debug symbols in executable (-arch "
542 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
Frederic Riss909125a2015-08-25 18:19:43 +0000543
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000544 // Using a std::shared_ptr rather than std::unique_ptr because move-only
545 // types don't work with std::bind in the ThreadPool implementation.
546 std::shared_ptr<raw_fd_ostream> OS;
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000547
548 Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
549 if (!OutputFileOrErr) {
550 WithColor::error() << toString(OutputFileOrErr.takeError());
551 return 1;
552 }
553
554 std::string OutputFile = *OutputFileOrErr;
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000555 if (NeedsTempFiles) {
Jonas Devlieghere58d4a392018-07-29 14:56:15 +0000556 TempFiles.emplace_back(Map->getTriple().getArchName().str());
557
558 auto E = TempFiles.back().createTempFile();
559 if (E) {
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000560 WithColor::error() << toString(std::move(E));
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000561 return 1;
562 }
Jonas Devlieghere58d4a392018-07-29 14:56:15 +0000563
564 auto &TempFile = *(TempFiles.back().File);
565 OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
566 /*shouldClose*/ false);
567 OutputFile = TempFile.TmpName;
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000568 } else {
Rafael Espindola5feaa632017-11-15 20:55:53 +0000569 std::error_code EC;
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000570 OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
571 sys::fs::F_None);
Rafael Espindola5feaa632017-11-15 20:55:53 +0000572 if (EC) {
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000573 WithColor::error() << OutputFile << ": " << EC.message();
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000574 return 1;
Rafael Espindola5feaa632017-11-15 20:55:53 +0000575 }
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000576 }
577
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000578 auto LinkLambda = [&,
579 OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
Jonas Devlieghere4d3e6c12018-06-29 16:50:41 +0000580 AllOK.fetch_and(linkDwarf(*Stream, BinHolder, *Map, *OptionsOrErr));
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000581 Stream->flush();
582 if (Verify && !NoOutput)
583 AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
Jonas Devlieghereac330ef2017-10-31 13:54:15 +0000584 };
585
586 // FIXME: The DwarfLinker can have some very deep recursion that can max
587 // out the (significantly smaller) stack when using threads. We don't
588 // want this limitation when we only have a single thread.
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000589 if (NumThreads == 1)
590 LinkLambda(OS);
591 else
592 Threads.async(LinkLambda, OS);
Frederic Riss7a425782015-08-05 18:27:44 +0000593 }
594
Jonas Devlieghere4cac1c42017-12-13 18:03:04 +0000595 Threads.wait();
596
597 if (!AllOK)
598 return 1;
Jonas Devlieghereac330ef2017-10-31 13:54:15 +0000599
Jonas Devlieghere956dada2018-10-23 00:32:22 +0000600 if (NeedsTempFiles) {
601 Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
602 if (!OutputFileOrErr) {
603 WithColor::error() << toString(OutputFileOrErr.takeError());
604 return 1;
605 }
606 if (!MachOUtils::generateUniversalBinary(TempFiles, *OutputFileOrErr,
607 *OptionsOrErr, SDKPath))
608 return 1;
609 }
Frederic Riss77f4ca12015-02-28 00:29:03 +0000610 }
Frederic Riss31e081e2014-12-12 17:31:24 +0000611
Rafael Espindolaebc0ffe2017-11-17 17:33:09 +0000612 return 0;
Frederic Riss31e081e2014-12-12 17:31:24 +0000613}