llvm-project

Форк
0
889 строк · 30.8 Кб
1
//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// 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 "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/LLVMDriver.h"
41
#include "llvm/Support/Path.h"
42
#include "llvm/Support/TargetSelect.h"
43
#include "llvm/Support/ThreadPool.h"
44
#include "llvm/Support/WithColor.h"
45
#include "llvm/Support/raw_ostream.h"
46
#include "llvm/Support/thread.h"
47
#include "llvm/TargetParser/Triple.h"
48
#include <algorithm>
49
#include <cstdint>
50
#include <cstdlib>
51
#include <string>
52
#include <system_error>
53

54
using namespace llvm;
55
using namespace llvm::dsymutil;
56
using namespace object;
57
using namespace llvm::dwarf_linker;
58

59
namespace {
60
enum ID {
61
  OPT_INVALID = 0, // This is not an option ID.
62
#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
63
#include "Options.inc"
64
#undef OPTION
65
};
66

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"
72
#undef PREFIX
73

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"
78
#undef OPTION
79
};
80

81
class DsymutilOptTable : public opt::GenericOptTable {
82
public:
83
  DsymutilOptTable() : opt::GenericOptTable(InfoTable) {}
84
};
85
} // namespace
86

87
enum class DWARFVerify : uint8_t {
88
  None = 0,
89
  Input = 1 << 0,
90
  Output = 1 << 1,
91
  OutputOnValidInput = 1 << 2,
92
  All = Input | Output,
93
  Auto = Input | OutputOnValidInput,
94
#if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)
95
  Default = Auto
96
#else
97
  Default = None
98
#endif
99
};
100

101
inline bool flagIsSet(DWARFVerify Flags, DWARFVerify SingleFlag) {
102
  return static_cast<uint8_t>(Flags) & static_cast<uint8_t>(SingleFlag);
103
}
104

105
struct DsymutilOptions {
106
  bool DumpDebugMap = false;
107
  bool DumpStab = false;
108
  bool Flat = false;
109
  bool InputIsYAMLDebugMap = false;
110
  bool ForceKeepFunctionForStatic = false;
111
  std::string OutputFile;
112
  std::string Toolchain;
113
  std::string ReproducerPath;
114
  std::vector<std::string> Archs;
115
  std::vector<std::string> InputFiles;
116
  unsigned NumThreads;
117
  DWARFVerify Verify = DWARFVerify::Default;
118
  ReproducerMode ReproMode = ReproducerMode::GenerateOnCrash;
119
  dsymutil::LinkOptions LinkOpts;
120
};
121

122
/// Return a list of input files. This function has logic for dealing with the
123
/// special case where we might have dSYM bundles as input. The function
124
/// returns an error when the directory structure doesn't match that of a dSYM
125
/// bundle.
126
static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args,
127
                                                    bool DsymAsInput) {
128
  std::vector<std::string> InputFiles;
129
  for (auto *File : Args.filtered(OPT_INPUT))
130
    InputFiles.push_back(File->getValue());
131

132
  if (!DsymAsInput)
133
    return InputFiles;
134

135
  // If we are updating, we might get dSYM bundles as input.
136
  std::vector<std::string> Inputs;
137
  for (const auto &Input : InputFiles) {
138
    if (!sys::fs::is_directory(Input)) {
139
      Inputs.push_back(Input);
140
      continue;
141
    }
142

143
    // Make sure that we're dealing with a dSYM bundle.
144
    SmallString<256> BundlePath(Input);
145
    sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
146
    if (!sys::fs::is_directory(BundlePath))
147
      return make_error<StringError>(
148
          Input + " is a directory, but doesn't look like a dSYM bundle.",
149
          inconvertibleErrorCode());
150

151
    // Create a directory iterator to iterate over all the entries in the
152
    // bundle.
153
    std::error_code EC;
154
    sys::fs::directory_iterator DirIt(BundlePath, EC);
155
    sys::fs::directory_iterator DirEnd;
156
    if (EC)
157
      return errorCodeToError(EC);
158

159
    // Add each entry to the list of inputs.
160
    while (DirIt != DirEnd) {
161
      Inputs.push_back(DirIt->path());
162
      DirIt.increment(EC);
163
      if (EC)
164
        return errorCodeToError(EC);
165
    }
166
  }
167
  return Inputs;
168
}
169

170
// Verify that the given combination of options makes sense.
171
static Error verifyOptions(const DsymutilOptions &Options) {
172
  if (Options.LinkOpts.Verbose && Options.LinkOpts.Quiet) {
173
    return make_error<StringError>(
174
        "--quiet and --verbose cannot be specified together",
175
        errc::invalid_argument);
176
  }
177

178
  if (Options.InputFiles.empty()) {
179
    return make_error<StringError>("no input files specified",
180
                                   errc::invalid_argument);
181
  }
182

183
  if (Options.LinkOpts.Update && llvm::is_contained(Options.InputFiles, "-")) {
184
    // FIXME: We cannot use stdin for an update because stdin will be
185
    // consumed by the BinaryHolder during the debugmap parsing, and
186
    // then we will want to consume it again in DwarfLinker. If we
187
    // used a unique BinaryHolder object that could cache multiple
188
    // binaries this restriction would go away.
189
    return make_error<StringError>(
190
        "standard input cannot be used as input for a dSYM update.",
191
        errc::invalid_argument);
192
  }
193

194
  if (!Options.Flat && Options.OutputFile == "-")
195
    return make_error<StringError>(
196
        "cannot emit to standard output without --flat.",
197
        errc::invalid_argument);
198

199
  if (Options.InputFiles.size() > 1 && Options.Flat &&
200
      !Options.OutputFile.empty())
201
    return make_error<StringError>(
202
        "cannot use -o with multiple inputs in flat mode.",
203
        errc::invalid_argument);
204

205
  if (!Options.ReproducerPath.empty() &&
206
      Options.ReproMode != ReproducerMode::Use)
207
    return make_error<StringError>(
208
        "cannot combine --gen-reproducer and --use-reproducer.",
209
        errc::invalid_argument);
210

211
  return Error::success();
212
}
213

214
static Expected<DsymutilAccelTableKind>
215
getAccelTableKind(opt::InputArgList &Args) {
216
  if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) {
217
    StringRef S = Accelerator->getValue();
218
    if (S == "Apple")
219
      return DsymutilAccelTableKind::Apple;
220
    if (S == "Dwarf")
221
      return DsymutilAccelTableKind::Dwarf;
222
    if (S == "Pub")
223
      return DsymutilAccelTableKind::Pub;
224
    if (S == "Default")
225
      return DsymutilAccelTableKind::Default;
226
    if (S == "None")
227
      return DsymutilAccelTableKind::None;
228
    return make_error<StringError>("invalid accelerator type specified: '" + S +
229
                                       "'. Supported values are 'Apple', "
230
                                       "'Dwarf', 'Pub', 'Default' and 'None'.",
231
                                   inconvertibleErrorCode());
232
  }
233
  return DsymutilAccelTableKind::Default;
234
}
235

236
static Expected<DsymutilDWARFLinkerType>
237
getDWARFLinkerType(opt::InputArgList &Args) {
238
  if (opt::Arg *LinkerType = Args.getLastArg(OPT_linker)) {
239
    StringRef S = LinkerType->getValue();
240
    if (S == "classic")
241
      return DsymutilDWARFLinkerType::Classic;
242
    if (S == "parallel")
243
      return DsymutilDWARFLinkerType::Parallel;
244
    return make_error<StringError>("invalid DWARF linker type specified: '" +
245
                                       S +
246
                                       "'. Supported values are 'classic', "
247
                                       "'parallel'.",
248
                                   inconvertibleErrorCode());
249
  }
250

251
  return DsymutilDWARFLinkerType::Classic;
252
}
253

254
static Expected<ReproducerMode> getReproducerMode(opt::InputArgList &Args) {
255
  if (Args.hasArg(OPT_gen_reproducer))
256
    return ReproducerMode::GenerateOnExit;
257
  if (opt::Arg *Reproducer = Args.getLastArg(OPT_reproducer)) {
258
    StringRef S = Reproducer->getValue();
259
    if (S == "GenerateOnExit")
260
      return ReproducerMode::GenerateOnExit;
261
    if (S == "GenerateOnCrash")
262
      return ReproducerMode::GenerateOnCrash;
263
    if (S == "Off")
264
      return ReproducerMode::Off;
265
    return make_error<StringError>(
266
        "invalid reproducer mode: '" + S +
267
            "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "
268
            "'Off'.",
269
        inconvertibleErrorCode());
270
  }
271
  return ReproducerMode::GenerateOnCrash;
272
}
273

274
static Expected<DWARFVerify> getVerifyKind(opt::InputArgList &Args) {
275
  if (Args.hasArg(OPT_verify))
276
    return DWARFVerify::Output;
277
  if (opt::Arg *Verify = Args.getLastArg(OPT_verify_dwarf)) {
278
    StringRef S = Verify->getValue();
279
    if (S == "input")
280
      return DWARFVerify::Input;
281
    if (S == "output")
282
      return DWARFVerify::Output;
283
    if (S == "all")
284
      return DWARFVerify::All;
285
    if (S == "auto")
286
      return DWARFVerify::Auto;
287
    if (S == "none")
288
      return DWARFVerify::None;
289
    return make_error<StringError>("invalid verify type specified: '" + S +
290
                                       "'. Supported values are 'none', "
291
                                       "'input', 'output', 'all' and 'auto'.",
292
                                   inconvertibleErrorCode());
293
  }
294
  return DWARFVerify::Default;
295
}
296

297
/// Parses the command line options into the LinkOptions struct and performs
298
/// some sanity checking. Returns an error in case the latter fails.
299
static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
300
  DsymutilOptions Options;
301

302
  Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
303
  Options.DumpStab = Args.hasArg(OPT_symtab);
304
  Options.Flat = Args.hasArg(OPT_flat);
305
  Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
306

307
  if (Expected<DWARFVerify> Verify = getVerifyKind(Args)) {
308
    Options.Verify = *Verify;
309
  } else {
310
    return Verify.takeError();
311
  }
312

313
  Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);
314
  Options.LinkOpts.VerifyInputDWARF =
315
      flagIsSet(Options.Verify, DWARFVerify::Input);
316
  Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);
317
  Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
318
  Options.LinkOpts.Update = Args.hasArg(OPT_update);
319
  Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
320
  Options.LinkOpts.Quiet = Args.hasArg(OPT_quiet);
321
  Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
322
  Options.LinkOpts.Fat64 = Args.hasArg(OPT_fat64);
323
  Options.LinkOpts.KeepFunctionForStatic =
324
      Args.hasArg(OPT_keep_func_for_static);
325

326
  if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {
327
    Options.ReproMode = ReproducerMode::Use;
328
    Options.ReproducerPath = ReproducerPath->getValue();
329
  } else {
330
    if (Expected<ReproducerMode> ReproMode = getReproducerMode(Args)) {
331
      Options.ReproMode = *ReproMode;
332
    } else {
333
      return ReproMode.takeError();
334
    }
335
  }
336

337
  if (Expected<DsymutilAccelTableKind> AccelKind = getAccelTableKind(Args)) {
338
    Options.LinkOpts.TheAccelTableKind = *AccelKind;
339
  } else {
340
    return AccelKind.takeError();
341
  }
342

343
  if (Expected<DsymutilDWARFLinkerType> DWARFLinkerType =
344
          getDWARFLinkerType(Args)) {
345
    Options.LinkOpts.DWARFLinkerType = *DWARFLinkerType;
346
  } else {
347
    return DWARFLinkerType.takeError();
348
  }
349

350
  if (Expected<std::vector<std::string>> InputFiles =
351
          getInputs(Args, Options.LinkOpts.Update)) {
352
    Options.InputFiles = std::move(*InputFiles);
353
  } else {
354
    return InputFiles.takeError();
355
  }
356

357
  for (auto *Arch : Args.filtered(OPT_arch))
358
    Options.Archs.push_back(Arch->getValue());
359

360
  if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
361
    Options.LinkOpts.PrependPath = OsoPrependPath->getValue();
362

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)});
367
  }
368

369
  if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
370
    Options.OutputFile = OutputFile->getValue();
371

372
  if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
373
    Options.Toolchain = Toolchain->getValue();
374

375
  if (Args.hasArg(OPT_assembly))
376
    Options.LinkOpts.FileType = DWARFLinkerBase::OutputFileType::Assembly;
377

378
  if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
379
    Options.LinkOpts.Threads = atoi(NumThreads->getValue());
380
  else
381
    Options.LinkOpts.Threads = 0; // Use all available hardware threads
382

383
  if (Options.DumpDebugMap || Options.LinkOpts.Verbose)
384
    Options.LinkOpts.Threads = 1;
385

386
  if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path))
387
    Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue();
388

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;
394
    else
395
      return FormatOrErr.takeError();
396
  }
397

398
  Options.LinkOpts.RemarksKeepAll =
399
      !Args.hasArg(OPT_remarks_drop_without_debug);
400

401
  if (opt::Arg *BuildVariantSuffix = Args.getLastArg(OPT_build_variant_suffix))
402
    Options.LinkOpts.BuildVariantSuffix = BuildVariantSuffix->getValue();
403

404
  for (auto *SearchPath : Args.filtered(OPT_dsym_search_path))
405
    Options.LinkOpts.DSYMSearchPaths.push_back(SearchPath->getValue());
406

407
  if (Error E = verifyOptions(Options))
408
    return std::move(E);
409
  return Options;
410
}
411

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");
417
  std::error_code EC;
418
  raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF);
419
  if (EC)
420
    return make_error<StringError>(
421
        "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
422

423
  CFBundleInfo BI = getBundleInfo(Bin);
424

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));
429
    else
430
      BI.IDStr = std::string(BundleID);
431
  }
432

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"
438
     << "\t<dict>\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);
444
  PL << "</string>\n"
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";
451

452
  if (!BI.OmitShortVersion()) {
453
    PL << "\t\t<key>CFBundleShortVersionString</key>\n";
454
    PL << "\t\t<string>";
455
    printHTMLEscaped(BI.ShortVersionStr, PL);
456
    PL << "</string>\n";
457
  }
458

459
  PL << "\t\t<key>CFBundleVersion</key>\n";
460
  PL << "\t\t<string>";
461
  printHTMLEscaped(BI.VersionStr, PL);
462
  PL << "</string>\n";
463

464
  if (!Toolchain.empty()) {
465
    PL << "\t\t<key>Toolchain</key>\n";
466
    PL << "\t\t<string>";
467
    printHTMLEscaped(Toolchain, PL);
468
    PL << "</string>\n";
469
  }
470

471
  PL << "\t</dict>\n"
472
     << "</plist>\n";
473

474
  PL.close();
475
  return Error::success();
476
}
477

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);
485

486
  return Error::success();
487
}
488

489
static bool verifyOutput(StringRef OutputFile, StringRef Arch,
490
                         DsymutilOptions Options, std::mutex &Mutex) {
491

492
  if (OutputFile == "-") {
493
    if (!Options.LinkOpts.Quiet) {
494
      std::lock_guard<std::mutex> Guard(Mutex);
495
      WithColor::warning() << "verification skipped for " << Arch
496
                           << " because writing to stdout.\n";
497
    }
498
    return true;
499
  }
500

501
  if (Options.LinkOpts.NoOutput) {
502
    if (!Options.LinkOpts.Quiet) {
503
      std::lock_guard<std::mutex> Guard(Mutex);
504
      WithColor::warning() << "verification skipped for " << Arch
505
                           << " because --no-output was passed.\n";
506
    }
507
    return true;
508
  }
509

510
  Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
511
  if (!BinOrErr) {
512
    std::lock_guard<std::mutex> Guard(Mutex);
513
    WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
514
    return false;
515
  }
516

517
  Binary &Binary = *BinOrErr.get().getBinary();
518
  if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
519
    std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
520
    if (DICtx->getMaxVersion() > 5) {
521
      if (!Options.LinkOpts.Quiet) {
522
        std::lock_guard<std::mutex> Guard(Mutex);
523
        WithColor::warning() << "verification skipped for " << Arch
524
                             << " because DWARF standard greater than v5 is "
525
                                "not supported yet.\n";
526
      }
527
      return true;
528
    }
529

530
    if (Options.LinkOpts.Verbose) {
531
      std::lock_guard<std::mutex> Guard(Mutex);
532
      errs() << "Verifying DWARF for architecture: " << Arch << "\n";
533
    }
534

535
    std::string Buffer;
536
    raw_string_ostream OS(Buffer);
537

538
    DIDumpOptions DumpOpts;
539
    bool success = DICtx->verify(OS, DumpOpts.noImplicitRecursion());
540
    if (!success) {
541
      std::lock_guard<std::mutex> Guard(Mutex);
542
      errs() << OS.str();
543
      WithColor::error() << "output verification failed for " << Arch << '\n';
544
    }
545
    return success;
546
  }
547

548
  return false;
549
}
550

551
namespace {
552
struct OutputLocation {
553
  OutputLocation(std::string DWARFFile,
554
                 std::optional<std::string> ResourceDir = {})
555
      : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
556
  /// This method is a workaround for older compilers.
557
  std::optional<std::string> getResourceDir() const { return ResourceDir; }
558
  std::string DWARFFile;
559
  std::optional<std::string> ResourceDir;
560
};
561
} // namespace
562

563
static Expected<OutputLocation>
564
getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
565
  if (Options.OutputFile == "-")
566
    return OutputLocation(Options.OutputFile);
567

568
  // When updating, do in place replacement.
569
  if (Options.OutputFile.empty() && Options.LinkOpts.Update)
570
    return OutputLocation(std::string(InputFile));
571

572
  // When dumping the debug map, just return an empty output location. This
573
  // allows us to compute the output location once.
574
  if (Options.DumpDebugMap)
575
    return OutputLocation("");
576

577
  // If a flat dSYM has been requested, things are pretty simple.
578
  if (Options.Flat) {
579
    if (Options.OutputFile.empty()) {
580
      if (InputFile == "-")
581
        return OutputLocation{"a.out.dwarf", {}};
582
      return OutputLocation((InputFile + ".dwarf").str());
583
    }
584

585
    return OutputLocation(Options.OutputFile);
586
  }
587

588
  // We need to create/update a dSYM bundle.
589
  // A bundle hierarchy looks like this:
590
  //   <bundle name>.dSYM/
591
  //       Contents/
592
  //          Info.plist
593
  //          Resources/
594
  //             DWARF/
595
  //                <DWARF file(s)>
596
  std::string DwarfFile =
597
      std::string(InputFile == "-" ? StringRef("a.out") : InputFile);
598
  SmallString<128> Path(Options.OutputFile);
599
  if (Path.empty())
600
    Path = DwarfFile + ".dSYM";
601
  if (!Options.LinkOpts.NoOutput) {
602
    if (auto E = createBundleDir(Path))
603
      return std::move(E);
604
    if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
605
      return std::move(E);
606
  }
607

608
  sys::path::append(Path, "Contents", "Resources");
609
  std::string ResourceDir = std::string(Path);
610
  sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
611
  return OutputLocation(std::string(Path), ResourceDir);
612
}
613

614
int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
615
  // Parse arguments.
616
  DsymutilOptTable T;
617
  unsigned MAI;
618
  unsigned MAC;
619
  ArrayRef<const char *> ArgsArr = ArrayRef(argv + 1, argc - 1);
620
  opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
621

622
  void *P = (void *)(intptr_t)getOutputFileName;
623
  std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
624
  SDKPath = std::string(sys::path::parent_path(SDKPath));
625

626
  for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
627
    WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
628
                         << '\n';
629
  }
630

631
  if (Args.hasArg(OPT_help)) {
632
    T.printHelp(
633
        outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
634
        "manipulate archived DWARF debug symbol files.\n\n"
635
        "dsymutil links the DWARF debug information found in the object files\n"
636
        "for the executable <input file> by using debug symbols information\n"
637
        "contained in its symbol table.\n",
638
        false);
639
    return EXIT_SUCCESS;
640
  }
641

642
  if (Args.hasArg(OPT_version)) {
643
    cl::PrintVersionMessage();
644
    return EXIT_SUCCESS;
645
  }
646

647
  auto OptionsOrErr = getOptions(Args);
648
  if (!OptionsOrErr) {
649
    WithColor::error() << toString(OptionsOrErr.takeError()) << '\n';
650
    return EXIT_FAILURE;
651
  }
652

653
  auto &Options = *OptionsOrErr;
654

655
  InitializeAllTargetInfos();
656
  InitializeAllTargetMCs();
657
  InitializeAllTargets();
658
  InitializeAllAsmPrinters();
659

660
  auto Repro = Reproducer::createReproducer(Options.ReproMode,
661
                                            Options.ReproducerPath, argc, argv);
662
  if (!Repro) {
663
    WithColor::error() << toString(Repro.takeError()) << '\n';
664
    return EXIT_FAILURE;
665
  }
666

667
  Options.LinkOpts.VFS = (*Repro)->getVFS();
668

669
  for (const auto &Arch : Options.Archs)
670
    if (Arch != "*" && Arch != "all" &&
671
        !object::MachOObjectFile::isValidArch(Arch)) {
672
      WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
673
      return EXIT_FAILURE;
674
    }
675

676
  for (auto &InputFile : Options.InputFiles) {
677
    // Dump the symbol table for each input file and requested arch
678
    if (Options.DumpStab) {
679
      if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs,
680
                    Options.LinkOpts.DSYMSearchPaths,
681
                    Options.LinkOpts.PrependPath,
682
                    Options.LinkOpts.BuildVariantSuffix))
683
        return EXIT_FAILURE;
684
      continue;
685
    }
686

687
    auto DebugMapPtrsOrErr = parseDebugMap(
688
        Options.LinkOpts.VFS, InputFile, Options.Archs,
689
        Options.LinkOpts.DSYMSearchPaths, Options.LinkOpts.PrependPath,
690
        Options.LinkOpts.BuildVariantSuffix, Options.LinkOpts.Verbose,
691
        Options.InputIsYAMLDebugMap);
692

693
    if (auto EC = DebugMapPtrsOrErr.getError()) {
694
      WithColor::error() << "cannot parse the debug map for '" << InputFile
695
                         << "': " << EC.message() << '\n';
696
      return EXIT_FAILURE;
697
    }
698

699
    // Remember the number of debug maps that are being processed to decide how
700
    // to name the remark files.
701
    Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();
702

703
    if (Options.LinkOpts.Update) {
704
      // The debug map should be empty. Add one object file corresponding to
705
      // the input file.
706
      for (auto &Map : *DebugMapPtrsOrErr)
707
        Map->addDebugMapObject(InputFile,
708
                               sys::TimePoint<std::chrono::seconds>());
709
    }
710

711
    // Ensure that the debug map is not empty (anymore).
712
    if (DebugMapPtrsOrErr->empty()) {
713
      WithColor::error() << "no architecture to link\n";
714
      return EXIT_FAILURE;
715
    }
716

717
    // Shared a single binary holder for all the link steps.
718
    BinaryHolder BinHolder(Options.LinkOpts.VFS);
719

720
    // Compute the output location and update the resource directory.
721
    Expected<OutputLocation> OutputLocationOrErr =
722
        getOutputFileName(InputFile, Options);
723
    if (!OutputLocationOrErr) {
724
      WithColor::error() << toString(OutputLocationOrErr.takeError());
725
      return EXIT_FAILURE;
726
    }
727
    Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
728

729
    // Statistics only require different architectures to be processed
730
    // sequentially, the link itself can still happen in parallel. Change the
731
    // thread pool strategy here instead of modifying LinkOpts.Threads.
732
    ThreadPoolStrategy S = hardware_concurrency(
733
        Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
734
    if (Options.LinkOpts.Threads == 0) {
735
      // If NumThreads is not specified, create one thread for each input, up to
736
      // the number of hardware threads.
737
      S.ThreadsRequested = DebugMapPtrsOrErr->size();
738
      S.Limit = true;
739
    }
740
    DefaultThreadPool Threads(S);
741

742
    // If there is more than one link to execute, we need to generate
743
    // temporary files.
744
    const bool NeedsTempFiles =
745
        !Options.DumpDebugMap && (Options.OutputFile != "-") &&
746
        (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
747

748
    std::atomic_char AllOK(1);
749
    SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
750

751
    std::mutex ErrorHandlerMutex;
752

753
    // Set up a crash recovery context.
754
    CrashRecoveryContext::Enable();
755
    CrashRecoveryContext CRC;
756
    CRC.DumpStackAndCleanupOnFailure = true;
757

758
    const bool Crashed = !CRC.RunSafely([&]() {
759
      for (auto &Map : *DebugMapPtrsOrErr) {
760
        if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
761
          Map->print(outs());
762

763
        if (Options.DumpDebugMap)
764
          continue;
765

766
        if (Map->begin() == Map->end()) {
767
          if (!Options.LinkOpts.Quiet) {
768
            std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
769
            WithColor::warning()
770
                << "no debug symbols in executable (-arch "
771
                << MachOUtils::getArchName(Map->getTriple().getArchName())
772
                << ")\n";
773
          }
774
        }
775

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;
779

780
        std::string OutputFile = OutputLocationOrErr->DWARFFile;
781
        if (NeedsTempFiles) {
782
          TempFiles.emplace_back(Map->getTriple().getArchName().str());
783

784
          auto E = TempFiles.back().createTempFile();
785
          if (E) {
786
            std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
787
            WithColor::error() << toString(std::move(E));
788
            AllOK.fetch_and(false);
789
            return;
790
          }
791

792
          MachOUtils::ArchAndFile &AF = TempFiles.back();
793
          OS = std::make_shared<raw_fd_ostream>(AF.getFD(),
794
                                                /*shouldClose*/ false);
795
          OutputFile = AF.getPath();
796
        } else {
797
          std::error_code EC;
798
          OS = std::make_shared<raw_fd_ostream>(
799
              Options.LinkOpts.NoOutput ? "-" : OutputFile, EC,
800
              sys::fs::OF_None);
801
          if (EC) {
802
            WithColor::error() << OutputFile << ": " << EC.message();
803
            AllOK.fetch_and(false);
804
            return;
805
          }
806
        }
807

808
        auto LinkLambda = [&,
809
                           OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
810
          DwarfLinkerForBinary Linker(*Stream, BinHolder, Options.LinkOpts,
811
                                      ErrorHandlerMutex);
812
          AllOK.fetch_and(Linker.link(*Map));
813
          Stream->flush();
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));
820
          }
821
        };
822

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)
827
          LinkLambda(OS);
828
        else
829
          Threads.async(LinkLambda, OS);
830
      }
831

832
      Threads.wait();
833
    });
834

835
    if (Crashed)
836
      (*Repro)->generate();
837

838
    if (!AllOK)
839
      return EXIT_FAILURE;
840

841
    if (NeedsTempFiles) {
842
      const bool Fat64 = Options.LinkOpts.Fat64;
843
      if (!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.
851

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
858
        // start at.
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());
866
          if (!stat)
867
            break;
868
          if (FileOffset > UINT32_MAX) {
869
            WithColor::error()
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.",
875
                           FileOffset);
876
            return EXIT_FAILURE;
877
          }
878
          FileOffset += stat->getSize();
879
        }
880
      }
881
      if (!MachOUtils::generateUniversalBinary(
882
              TempFiles, OutputLocationOrErr->DWARFFile, Options.LinkOpts,
883
              SDKPath, Fat64))
884
        return EXIT_FAILURE;
885
    }
886
  }
887

888
  return EXIT_SUCCESS;
889
}
890

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.