diff options
| author | Jonas Devlieghere <jonas@devlieghere.com> | 2018-02-07 13:51:29 +0000 |
|---|---|---|
| committer | Jonas Devlieghere <jonas@devlieghere.com> | 2018-02-07 13:51:29 +0000 |
| commit | a4b9417b52b9cc52a08aca1c23fe9e2c0081e138 (patch) | |
| tree | a89ddac27195bb3a668afbd391598a8ab30597cd | |
| parent | c90d79f80aea31d02449e97327bffb98f1287f7f (diff) | |
| download | bcm5719-llvm-a4b9417b52b9cc52a08aca1c23fe9e2c0081e138.tar.gz bcm5719-llvm-a4b9417b52b9cc52a08aca1c23fe9e2c0081e138.zip | |
[dsymutil] Upstream update feature.
Now that dsymutil can generate accelerator tables, we can upstream the
update logic that, as the name implies, updates the accelerator tables
in an existing dSYM bundle. In combination with `-minimize` this can be
used to remove redundant .debug_(inlines|pubtypes|pubnames).
Differential revision: https://reviews.llvm.org/D42880
llvm-svn: 324480
| -rw-r--r-- | llvm/docs/CommandGuide/dsymutil.rst | 10 | ||||
| -rw-r--r-- | llvm/test/tools/dsymutil/X86/basic-linking-x86.test | 15 | ||||
| -rw-r--r-- | llvm/test/tools/dsymutil/X86/update-one-CU.test | 35 | ||||
| -rw-r--r-- | llvm/test/tools/dsymutil/cmdline.test | 1 | ||||
| -rw-r--r-- | llvm/tools/dsymutil/DebugMap.h | 3 | ||||
| -rw-r--r-- | llvm/tools/dsymutil/DwarfLinker.cpp | 188 | ||||
| -rw-r--r-- | llvm/tools/dsymutil/dsymutil.cpp | 129 | ||||
| -rw-r--r-- | llvm/tools/dsymutil/dsymutil.h | 5 |
8 files changed, 341 insertions, 45 deletions
diff --git a/llvm/docs/CommandGuide/dsymutil.rst b/llvm/docs/CommandGuide/dsymutil.rst index 7813204ef7c..2782da3c302 100644 --- a/llvm/docs/CommandGuide/dsymutil.rst +++ b/llvm/docs/CommandGuide/dsymutil.rst @@ -40,7 +40,9 @@ OPTIONS When used when creating a dSYM file, this option will suppress the emission of the .debug_inlines, .debug_pubnames, and .debug_pubtypes sections since - dsymutil currently has better equivalents: .apple_names and .apple_types. + dsymutil currently has better equivalents: .apple_names and .apple_types. When + used in conjunction with --update option, this option will cause redundant + accelerator tables to be removed. .. option:: --no-odr @@ -72,6 +74,12 @@ OPTIONS Dumps the symbol table found in *executable* or object file(s) and exits. +.. option:: -u, --update + + Update an existing dSYM file to contain the latest accelerator tables and + other DWARF optimizations. This option will rebuild the '.apple_names' and + '.apple_types' hashed accelerator tables. + .. option:: -v, --verbose Display verbose information when linking. diff --git a/llvm/test/tools/dsymutil/X86/basic-linking-x86.test b/llvm/test/tools/dsymutil/X86/basic-linking-x86.test index d9b0ee9b10f..4e6c6f668db 100644 --- a/llvm/test/tools/dsymutil/X86/basic-linking-x86.test +++ b/llvm/test/tools/dsymutil/X86/basic-linking-x86.test @@ -8,6 +8,21 @@ RUN: llvm-dsymutil -f -o - -oso-prepend-path=%p/.. %p/../Inputs/basic-archive.ma RUN: llvm-dsymutil -dump-debug-map -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 | llvm-dsymutil -f -y -o - - | llvm-dwarfdump -a - | FileCheck %s --check-prefix=CHECK --check-prefix=BASIC RUN: llvm-dsymutil -dump-debug-map -oso-prepend-path=%p/.. %p/../Inputs/basic-archive.macho.x86_64 | llvm-dsymutil -f -o - -y - | llvm-dwarfdump -a - | FileCheck %s --check-prefix=CHECK --check-prefix=ARCHIVE +# Update tests +RUN: rm -rf %t.dir +RUN: mkdir -p %t.dir +RUN: cat %p/../Inputs/basic.macho.x86_64 > %t.dir/basic +RUN: llvm-dsymutil -oso-prepend-path=%p/.. %t.dir/basic +RUN: llvm-dwarfdump -a %t.dir/basic.dSYM/Contents/Resources/DWARF/basic | FileCheck %s +RUN: llvm-dsymutil --update %t.dir/basic.dSYM +RUN: llvm-dwarfdump -a %t.dir/basic.dSYM/Contents/Resources/DWARF/basic | FileCheck %s +RUN: llvm-dsymutil -u %t.dir/basic.dSYM +RUN: llvm-dwarfdump -a %t.dir/basic.dSYM/Contents/Resources/DWARF/basic | FileCheck %s +RUN: llvm-dsymutil --update %t.dir/basic.dSYM -o %t.dir/updated.dSYM +RUN: llvm-dwarfdump -a %t.dir/updated.dSYM/Contents/Resources/DWARF/basic | FileCheck %s +RUN: llvm-dsymutil -f -u %t2 -o %t3 +RUN: llvm-dwarfdump -a %t3 | FileCheck %s + CHECK: file format Mach-O 64-bit x86-64 CHECK: debug_info contents diff --git a/llvm/test/tools/dsymutil/X86/update-one-CU.test b/llvm/test/tools/dsymutil/X86/update-one-CU.test new file mode 100644 index 00000000000..aec9b2f44f1 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/update-one-CU.test @@ -0,0 +1,35 @@ +RUN: llvm-dsymutil -oso-prepend-path=%p/.. %p/../Inputs/objc.macho.x86_64 -o %t.dSYM +RUN: llvm-dsymutil -update %t.dSYM +RUN: llvm-dwarfdump -apple-types -apple-objc %t.dSYM/Contents/Resources/DWARF/objc.macho.x86_64 | FileCheck %s + +CHECK: .apple_types contents: +CHECK: Hash 0x2b5e6 [ +CHECK-NEXT: Name@0x145 { +CHECK-NEXT: String: 0x00000066 "A" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x0000012d +CHECK-NEXT: Atom[1]: 0x0013 +CHECK-NEXT: Atom[2]: 0x02 +CHECK-NEXT: Atom[3]: 0x0b87b15a +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: ] + +CHECK: .apple_objc contents: +CHECK: Hash 0x2b5e6 +CHECK-NEXT: Name@0x38 { +CHECK-NEXT: String: 0x00000066 "A" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x00000027 +CHECK-NEXT: ] +CHECK-NEXT: Data 1 [ +CHECK-NEXT: Atom[0]: 0x0000007a +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK: Hash 0x3fa0f4b5 +CHECK-NEXT: Name@0x4c { +CHECK-NEXT: String: 0x0000009d "A(Category)" +CHECK-NEXT: Data 0 [ +CHECK-NEXT: Atom[0]: 0x0000007a +CHECK-NEXT: ] +CHECK-NEXT: } diff --git a/llvm/test/tools/dsymutil/cmdline.test b/llvm/test/tools/dsymutil/cmdline.test index 561f3718ef2..d246a14d32e 100644 --- a/llvm/test/tools/dsymutil/cmdline.test +++ b/llvm/test/tools/dsymutil/cmdline.test @@ -14,6 +14,7 @@ HELP: -num-threads=<n> HELP: -o=<filename> HELP: -oso-prepend-path=<path> HELP: -symtab +HELP: -update HELP: -verbose HELP: -verify HELP: -y diff --git a/llvm/tools/dsymutil/DebugMap.h b/llvm/tools/dsymutil/DebugMap.h index 3b5b437ccff..e64285f3996 100644 --- a/llvm/tools/dsymutil/DebugMap.h +++ b/llvm/tools/dsymutil/DebugMap.h @@ -28,6 +28,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/YAMLTraits.h" @@ -107,7 +108,7 @@ public: DebugMapObject & addDebugMapObject(StringRef ObjectFilePath, sys::TimePoint<std::chrono::seconds> Timestamp, - uint8_t Type); + uint8_t Type = llvm::MachO::N_OSO); const Triple &getTriple() const { return BinaryTriple; } diff --git a/llvm/tools/dsymutil/DwarfLinker.cpp b/llvm/tools/dsymutil/DwarfLinker.cpp index 70a8b7c82e9..87f465dd83d 100644 --- a/llvm/tools/dsymutil/DwarfLinker.cpp +++ b/llvm/tools/dsymutil/DwarfLinker.cpp @@ -100,6 +100,24 @@ namespace dsymutil { namespace { +/// Retrieve the section named \a SecName in \a Obj. +/// +/// To accommodate for platform discrepancies, the name passed should be +/// (for example) 'debug_info' to match either '__debug_info' or '.debug_info'. +/// This function will strip the initial platform-specific characters. +static Optional<object::SectionRef> +getSectionByName(const object::ObjectFile &Obj, StringRef SecName) { + for (const object::SectionRef &Section : Obj.sections()) { + StringRef SectionName; + Section.getName(SectionName); + SectionName = SectionName.substr(SectionName.find_first_not_of("._")); + if (SectionName != SecName) + continue; + return Section; + } + return None; +} + template <typename KeyT, typename ValT> using HalfOpenIntervalMap = IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize, @@ -491,12 +509,48 @@ private: std::string ClangModuleName; }; +/// Check if the DIE at \p Idx is in the scope of a function. +static bool inFunctionScope(CompileUnit &U, unsigned Idx) { + while (Idx) { + if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram) + return true; + Idx = U.getInfo(Idx).ParentIdx; + } + return false; +} + } // end anonymous namespace void CompileUnit::markEverythingAsKept() { - for (auto &I : Info) - // Mark everything that wasn't explicity marked for pruning. + unsigned Idx = 0; + + setHasInterestingContent(); + + for (auto &I : Info) { + // Mark everything that wasn't explicit marked for pruning. I.Keep = !I.Prune; + auto DIE = OrigUnit.getDIEAtIndex(Idx++); + + // Try to guess which DIEs must go to the accelerator tables. We do that + // just for variables, because functions will be handled depending on + // whether they carry a DW_AT_low_pc attribute or not. + if (DIE.getTag() != dwarf::DW_TAG_variable && + DIE.getTag() != dwarf::DW_TAG_constant) + continue; + + Optional<DWARFFormValue> Value; + if (!(Value = DIE.find(dwarf::DW_AT_location))) { + if ((Value = DIE.find(dwarf::DW_AT_const_value)) && + !inFunctionScope(*this, I.ParentIdx)) + I.InDebugMap = true; + continue; + } + if (auto Block = Value->getAsBlock()) { + if (Block->size() > OrigUnit.getAddressByteSize() && + (*Block)[0] == dwarf::DW_OP_addr) + I.InDebugMap = true; + } + } } uint64_t CompileUnit::computeNextUnitOffset() { @@ -668,6 +722,9 @@ public: std::vector<DWARFDebugLine::Row> &Rows, unsigned AdddressSize); + /// Copy over the debug sections that are not modified when updating. + void copyInvariantDebugSection(const object::ObjectFile &Obj, LinkOptions &); + uint32_t getLineSectionSize() const { return LineSectionSize; } /// Emit the .debug_pubnames contribution for \p Unit. @@ -1200,6 +1257,32 @@ void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, MS->EmitLabel(LineEndSym); } +static void emitSectionContents(const object::ObjectFile &Obj, + StringRef SecName, MCStreamer *MS) { + StringRef Contents; + if (auto Sec = getSectionByName(Obj, SecName)) + if (!Sec->getContents(Contents)) + MS->EmitBytes(Contents); +} + +void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj, + LinkOptions &Options) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + emitSectionContents(Obj, "debug_line", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + emitSectionContents(Obj, "debug_loc", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + emitSectionContents(Obj, "debug_ranges", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + emitSectionContents(Obj, "debug_frame", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); + emitSectionContents(Obj, "debug_aranges", MS); +} + /// Emit the pubnames or pubtypes section contribution for \p /// Unit into \p Sec. The data is provided in \p Names. void DwarfStreamer::emitPubSectionForUnit( @@ -1529,8 +1612,8 @@ private: /// it to \p Die. /// \returns the size of the new attribute. unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const DWARFUnit &U); + const DWARFFormValue &Val, const DWARFUnit &U, + AttributesInfo &Info); /// Clone an attribute referencing another DIE and add /// it to \p Die. @@ -2621,12 +2704,18 @@ void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) { unsigned DwarfLinker::DIECloner::cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, - const DWARFUnit &U) { + const DWARFUnit &U, + AttributesInfo &Info) { // Switch everything to out of line strings. const char *String = *Val.getAsCString(); - unsigned Offset = Linker.StringPool.getStringOffset(String); + auto StringEntry = Linker.StringPool.getEntry(String); + if (AttrSpec.Attr == dwarf::DW_AT_name) + Info.Name = StringEntry; + else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || + AttrSpec.Attr == dwarf::DW_AT_linkage_name) + Info.MangledName = StringEntry; Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, - DIEInteger(Offset)); + DIEInteger(StringEntry.getOffset())); return 4; } @@ -2749,6 +2838,14 @@ unsigned DwarfLinker::DIECloner::cloneAddressAttribute( const CompileUnit &Unit, AttributesInfo &Info) { uint64_t Addr = *Val.getAsAddress(); + if (LLVM_UNLIKELY(Linker.Options.Update)) { + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) + Info.HasLowPc = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Addr)); + return Unit.getOrigUnit().getAddressByteSize(); + } + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) { if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine || Die.getTag() == dwarf::DW_TAG_lexical_block) @@ -2789,6 +2886,26 @@ unsigned DwarfLinker::DIECloner::cloneScalarAttribute( AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, AttributesInfo &Info) { uint64_t Value; + + if (LLVM_UNLIKELY(Linker.Options.Update)) { + if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSectionOffset()) + Value = *OptionalValue; + else { + Linker.reportWarning( + "Unsupported scalar attribute form. Dropping attribute.", &InputDIE); + return 0; + } + if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + Info.IsDeclaration = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Value)); + return AttrSize; + } + if (AttrSpec.Attr == dwarf::DW_AT_high_pc && Die.getTag() == dwarf::DW_TAG_compile_unit) { if (Unit.getLowPc() == -1ULL) @@ -2839,7 +2956,7 @@ unsigned DwarfLinker::DIECloner::cloneAttribute( switch (AttrSpec.Form) { case dwarf::DW_FORM_strp: case dwarf::DW_FORM_string: - return cloneStringAttribute(Die, AttrSpec, Val, U); + return cloneStringAttribute(Die, AttrSpec, Val, U, Info); case dwarf::DW_FORM_ref_addr: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: @@ -3108,13 +3225,14 @@ DIE *DwarfLinker::DIECloner::cloneDIE( if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) { Flags |= TF_InFunctionScope; - if (!Info.InDebugMap) + if (!Info.InDebugMap && LLVM_LIKELY(!Options.Update)) Flags |= TF_SkipPC; } bool Copied = false; for (const auto &AttrSpec : Abbrev->attributes()) { - if (shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, + if (LLVM_LIKELY(!Options.Update) && + shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, Flags & TF_SkipPC, Flags & TF_InFunctionScope)) { DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, U.getFormParams()); @@ -3827,13 +3945,18 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) { Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); if (Linker.Options.NoOutput) continue; - // FIXME: for compatibility with the classic dsymutil, we emit - // an empty line table for the unit, even if the unit doesn't - // actually exist in the DIE tree. - Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext); - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - Linker.patchRangesForUnit(*CurrentUnit, DwarfContext); - Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); + + if (LLVM_LIKELY(!Linker.Options.Update)) { + // FIXME: for compatibility with the classic dsymutil, we emit an empty + // line table for the unit, even if the unit doesn't actually exist in + // the DIE tree. + Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext); + Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + Linker.patchRangesForUnit(*CurrentUnit, DwarfContext); + Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); + } else { + Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + } } if (Linker.Options.NoOutput) @@ -3841,7 +3964,8 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) { // Emit all the compile unit's debug information. for (auto &CurrentUnit : CompileUnits) { - Linker.generateUnitRanges(*CurrentUnit); + if (LLVM_LIKELY(!Linker.Options.Update)) + Linker.generateUnitRanges(*CurrentUnit); CurrentUnit->fixupForwardReferences(); Linker.Streamer->emitCompileUnitHeader(*CurrentUnit); if (!CurrentUnit->getOutputUnitDIE()) @@ -3900,7 +4024,8 @@ bool DwarfLinker::link(const DebugMap &Map) { // Look for relocations that correspond to debug map entries. RelocationManager RelocMgr(*this); - if (!RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { + if (LLVM_LIKELY(!Options.Update) && + !RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { if (Options.Verbose) outs() << "No valid relocations found. Skipping.\n"; continue; @@ -3921,9 +4046,10 @@ bool DwarfLinker::link(const DebugMap &Map) { CUDie.dump(outs(), 0, DumpOpts); } - if (!registerModuleReference(CUDie, *CU, ModuleMap)) { - Units.push_back(llvm::make_unique<CompileUnit>(*CU, UnitID++, - !Options.NoODR, "")); + if (!CUDie || LLVM_UNLIKELY(Options.Update) || + !registerModuleReference(CUDie, *CU, ModuleMap)) { + Units.push_back(llvm::make_unique<CompileUnit>( + *CU, UnitID++, !Options.NoODR && !Options.Update, "")); maybeUpdateMaxDwarfVersion(CU->getVersion()); } } @@ -3935,21 +4061,27 @@ bool DwarfLinker::link(const DebugMap &Map) { // Then mark all the DIEs that need to be present in the linked // output and collect some information about them. Note that this - // loop can not be merged with the previous one becaue cross-cu + // loop can not be merged with the previous one because cross-CU // references require the ParentIdx to be setup for every CU in // the object file before calling this. - for (auto &CurrentUnit : Units) - lookForDIEsToKeep(RelocMgr, CurrentUnit->getOrigUnit().getUnitDIE(), *Obj, - *CurrentUnit, 0); + if (LLVM_UNLIKELY(Options.Update)) { + for (auto &CurrentUnit : Units) + CurrentUnit->markEverythingAsKept(); + Streamer->copyInvariantDebugSection(*ErrOrObj, Options); + } else { + for (auto &CurrentUnit : Units) + lookForDIEsToKeep(RelocMgr, CurrentUnit->getOrigUnit().getUnitDIE(), + *Obj, *CurrentUnit, 0); + } // The calls to applyValidRelocs inside cloneDIE will walk the // reloc array again (in the same way findValidRelocsInDebugInfo() // did). We need to reset the NextValidReloc index to the beginning. RelocMgr.resetValidRelocs(); - if (RelocMgr.hasValidRelocs()) + if (RelocMgr.hasValidRelocs() || LLVM_UNLIKELY(Options.Update)) DIECloner(*this, RelocMgr, DIEAlloc, Units, Options) .cloneAllCompileUnits(*DwarfContext); - if (!Options.NoOutput && !Units.empty()) + if (!Options.NoOutput && !Units.empty() && LLVM_LIKELY(!Options.Update)) patchFrameInfoForObject(*Obj, *DwarfContext, Units[0]->getOrigUnit().getAddressByteSize()); diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp index 4a01b1d9db4..dae872fe165 100644 --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -80,10 +80,21 @@ static opt<bool> Minimize( desc("When used when creating a dSYM file, this option will suppress\n" "the emission of the .debug_inlines, .debug_pubnames, and\n" ".debug_pubtypes sections since dsymutil currently has better\n" - "equivalents: .apple_names and .apple_types."), + "equivalents: .apple_names and .apple_types. When used in\n" + "conjunction with --update option, this option will cause redundant\n" + "accelerator tables to be removed."), init(false), cat(DsymCategory)); static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize)); +static opt<bool> Update( + "update", + desc("Updates existing dSYM files to contain the latest accelerator\n" + "tables and other DWARF optimizations. This option will currently\n" + "add the new .apple_names and .apple_types hashed accelerator\n" + "tables."), + init(false), cat(DsymCategory)); +static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update)); + static opt<unsigned> NumThreads( "num-threads", desc("Specifies the maximum number (n) of simultaneous threads to use\n" @@ -230,8 +241,12 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { } static std::string getOutputFileName(llvm::StringRef InputFile) { + // When updating, do in place replacement. + if (OutputFileOpt.empty() && Update) + return InputFile; + + // If a flat dSYM has been requested, things are pretty simple. if (FlatOut) { - // If a flat dSYM has been requested, things are pretty simple. if (OutputFileOpt.empty()) { if (InputFile == "-") return "a.out.dwarf"; @@ -269,6 +284,76 @@ static Expected<sys::fs::TempFile> createTempFile() { return sys::fs::TempFile::create(TmpModel); } +/// Parses the command line options into the LinkOptions struct and performs +/// some sanity checking. Returns an error in case the latter fails. +static Expected<LinkOptions> getOptions() { + LinkOptions Options; + + Options.Verbose = Verbose; + Options.NoOutput = NoOutput; + Options.NoODR = NoODR; + Options.Minimize = Minimize; + Options.Update = Update; + Options.NoTimestamp = NoTimestamp; + Options.PrependPath = OsoPrependPath; + + if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") != + InputFiles.end()) { + // FIXME: We cannot use stdin for an update because stdin will be + // consumed by the BinaryHolder during the debugmap parsing, and + // then we will want to consume it again in DwarfLinker. If we + // used a unique BinaryHolder object that could cache multiple + // binaries this restriction would go away. + return make_error<StringError>( + "error: standard input cannot be used as input for a dSYM update.", + inconvertibleErrorCode()); + } + + return Options; +} + +/// Return a list of input files. This function has logic for dealing with the +/// special case where we might have dSYM bundles as input. The function +/// returns an error when the directory structure doesn't match that of a dSYM +/// bundle. +static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) { + if (!DsymAsInput) + return InputFiles; + + // If we are updating, we might get dSYM bundles as input. + std::vector<std::string> Inputs; + for (const auto &Input : InputFiles) { + if (!llvm::sys::fs::is_directory(Input)) { + Inputs.push_back(Input); + continue; + } + + // Make sure that we're dealing with a dSYM bundle. + std::string dSYMDir = Input + "/Contents/Resources/DWARF"; + if (!llvm::sys::fs::is_directory(dSYMDir)) + return make_error<StringError>( + Input + " is a directory, but doesn't look like a dSYM bundle.", + inconvertibleErrorCode()); + + // Create a directory iterator to iterate over all the entries in the + // bundle. + std::error_code EC; + llvm::sys::fs::directory_iterator DirIt(dSYMDir, EC); + llvm::sys::fs::directory_iterator DirEnd; + if (EC) + return errorCodeToError(EC); + + // Add each entry to the list of inputs. + while (DirIt != DirEnd) { + Inputs.push_back(DirIt->path()); + DirIt.increment(EC); + if (EC) + return errorCodeToError(EC); + } + } + return Inputs; +} + namespace { struct TempFileVector { std::vector<sys::fs::TempFile> Files; @@ -285,7 +370,6 @@ int main(int argc, char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::PrettyStackTraceProgram StackPrinter(argc, argv); llvm::llvm_shutdown_obj Shutdown; - LinkOptions Options; void *P = (void *)(intptr_t)getOutputFileName; std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P); SDKPath = llvm::sys::path::parent_path(SDKPath); @@ -308,24 +392,29 @@ int main(int argc, char **argv) { return 0; } - Options.Verbose = Verbose; - Options.NoOutput = NoOutput; - Options.NoODR = NoODR; - Options.Minimize = Minimize; - Options.NoTimestamp = NoTimestamp; - Options.PrependPath = OsoPrependPath; + auto OptionsOrErr = getOptions(); + if (!OptionsOrErr) { + errs() << "error: " << toString(OptionsOrErr.takeError()); + return 1; + } llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); + auto InputsOrErr = getInputs(OptionsOrErr->Update); + if (!InputsOrErr) { + errs() << "error: " << toString(InputsOrErr.takeError()) << '\n'; + return 1; + } + if (!FlatOut && OutputFileOpt == "-") { llvm::errs() << "error: cannot emit to standard output without --flat\n"; return 1; } - if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) { + if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) { llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n"; return 1; } @@ -337,7 +426,7 @@ int main(int argc, char **argv) { return 1; } - for (auto &InputFile : InputFiles) { + for (auto &InputFile : *InputsOrErr) { // Dump the symbol table for each input file and requested arch if (DumpStab) { if (!dumpStab(InputFile, ArchFlags, OsoPrependPath)) @@ -354,6 +443,15 @@ int main(int argc, char **argv) { return 1; } + if (OptionsOrErr->Update) { + // The debug map should be empty. Add one object file corresponding to + // the input file. + for (auto &Map : *DebugMapPtrsOrErr) + Map->addDebugMapObject(InputFile, + llvm::sys::TimePoint<std::chrono::seconds>()); + } + + // Ensure that the debug map is not empty (anymore). if (DebugMapPtrsOrErr->empty()) { llvm::errs() << "error: no architecture to link\n"; return 1; @@ -369,7 +467,10 @@ int main(int argc, char **argv) { // If there is more than one link to execute, we need to generate // temporary files. - bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1; + bool NeedsTempFiles = + !DumpDebugMap && (OutputFileOpt != "-") && + (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update); + llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles; TempFileVector TempFileStore; std::atomic_char AllOK(1); @@ -412,7 +513,7 @@ int main(int argc, char **argv) { auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream) { - AllOK.fetch_and(linkDwarf(*Stream, *Map, Options)); + AllOK.fetch_and(linkDwarf(*Stream, *Map, *OptionsOrErr)); Stream->flush(); if (Verify && !NoOutput) AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName())); @@ -434,7 +535,7 @@ int main(int argc, char **argv) { if (NeedsTempFiles && !MachOUtils::generateUniversalBinary( - TempFiles, getOutputFileName(InputFile), Options, SDKPath)) + TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath)) return 1; } diff --git a/llvm/tools/dsymutil/dsymutil.h b/llvm/tools/dsymutil/dsymutil.h index 7a08189754a..d056947527c 100644 --- a/llvm/tools/dsymutil/dsymutil.h +++ b/llvm/tools/dsymutil/dsymutil.h @@ -37,7 +37,10 @@ struct LinkOptions { bool NoOutput = false; /// Do not unique types according to ODR - bool NoODR; + bool NoODR = false; + + /// Update + bool Update = false; /// Minimize bool Minimize = false; |

