Index: test/tools/dsymutil/basic-linking.test =================================================================== --- test/tools/dsymutil/basic-linking.test +++ test/tools/dsymutil/basic-linking.test @@ -1,7 +1,7 @@ RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 | FileCheck %s RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic-lto.macho.x86_64 | FileCheck %s --check-prefix=CHECK-LTO RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK-ARCHIVE -RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 %p/Inputs/basic-lto.macho.x86_64 %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LTO --check-prefix=CHECK-ARCHIVE +RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 %p/Inputs/basic-lto.macho.x86_64 %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LTO --check-prefix=CHECK-ARCHIVE This test check the basic Dwarf linking process through the debug dumps. @@ -11,17 +11,24 @@ CHECK-NEXT: TAG_compile_unit CHECK-NOT: TAG CHECK: AT_name {{.*}}basic1.c -CHECK-NOT: Found valid debug map entry -CHECK: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0 -CHECK-NEXT: DW_TAG_subprogram -CHECK-NEXT: DW_AT_name{{.*}}"main" CHECK: DEBUG MAP OBJECT: {{.*}}basic2.macho.x86_64.o CHECK: Input compilation unit: CHECK-NEXT: TAG_compile_unit CHECK-NOT: TAG CHECK: AT_name {{.*}}basic2.c + +CHECK: DEBUG MAP OBJECT: {{.*}}basic3.macho.x86_64.o +CHECK: Input compilation unit: +CHECK-NEXT: TAG_compile_unit +CHECK-NOT: TAG +CHECK: AT_name {{.*}}basic3.c + CHECK-NOT: Found valid debug map entry +CHECK: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0 +CHECK-NEXT: DW_TAG_subprogram +CHECK-NEXT: DW_AT_name{{.*}}"main" + CHECK: Found valid debug map entry: _private_int 0000000000000560 => 0000000100001008 CHECK-NEXT: DW_TAG_variable CHECK-NEXT: DW_AT_name {{.*}}"private_int" @@ -38,11 +45,6 @@ CHECK-NEXT: DW_TAG_subprogram CHECK-NEXT: DW_AT_name {{.*}}"inc" -CHECK: DEBUG MAP OBJECT: {{.*}}basic3.macho.x86_64.o -CHECK: Input compilation unit: -CHECK-NEXT: TAG_compile_unit -CHECK-NOT: TAG -CHECK: AT_name {{.*}}basic3.c CHECK-NOT: Found valid debug map entry CHECK: Found valid debug map entry: _val ffffffffffffffff => 0000000100001004 CHECK-NEXT: DW_TAG_variable @@ -104,16 +106,24 @@ CHECK-ARCHIVE-NEXT: TAG_compile_unit CHECK-ARCHIVE-NOT: TAG CHECK-ARCHIVE: AT_name {{.*}}basic1.c -CHECK-ARCHIVE-NOT: Found valid debug map entry -CHECK-ARCHIVE: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0 -CHECK-ARCHIVE-NEXT: DW_TAG_subprogram -CHECK-ARCHIVE-NEXT: DW_AT_name{{.*}}"main" CHECK-ARCHIVE: DEBUG MAP OBJECT: {{.*}}libbasic.a(basic2.macho.x86_64.o) CHECK-ARCHIVE: Input compilation unit: CHECK-ARCHIVE-NEXT: TAG_compile_unit CHECK-ARCHIVE-NOT: TAG CHECK-ARCHIVE: AT_name {{.*}}basic2.c + +CHECK-ARCHIVE: DEBUG MAP OBJECT: {{.*}}libbasic.a(basic3.macho.x86_64.o) +CHECK-ARCHIVE: Input compilation unit: +CHECK-ARCHIVE-NEXT: TAG_compile_unit +CHECK-ARCHIVE-NOT: TAG +CHECK-ARCHIVE: AT_name {{.*}}basic3.c + +CHECK-ARCHIVE-NOT: Found valid debug map entry +CHECK-ARCHIVE: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0 +CHECK-ARCHIVE-NEXT: DW_TAG_subprogram +CHECK-ARCHIVE-NEXT: DW_AT_name{{.*}}"main" + CHECK-ARCHIVE-NOT: Found valid debug map entry CHECK-ARCHIVE: Found valid debug map entry: _private_int 0000000000000560 => 0000000100001004 CHECK-ARCHIVE-NEXT: DW_TAG_variable @@ -131,11 +141,6 @@ CHECK-ARCHIVE-NEXT: DW_TAG_subprogram CHECK-ARCHIVE-NEXT: DW_AT_name {{.*}}"inc" -CHECK-ARCHIVE: DEBUG MAP OBJECT: {{.*}}libbasic.a(basic3.macho.x86_64.o) -CHECK-ARCHIVE: Input compilation unit: -CHECK-ARCHIVE-NEXT: TAG_compile_unit -CHECK-ARCHIVE-NOT: TAG -CHECK-ARCHIVE: AT_name {{.*}}basic3.c CHECK-ARCHIVE-NOT: Found valid debug map entry CHECK-ARCHIVE: Found valid debug map entry: _val ffffffffffffffff => 0000000100001008 CHECK-ARCHIVE-NEXT: DW_TAG_variable Index: tools/dsymutil/DebugMap.h =================================================================== --- tools/dsymutil/DebugMap.h +++ tools/dsymutil/DebugMap.h @@ -102,6 +102,8 @@ const_iterator end() const { return Objects.end(); } + unsigned getNumberOfObjects() const { return Objects.size(); } + /// This function adds an DebugMapObject to the list owned by this /// debug map. DebugMapObject & Index: tools/dsymutil/DwarfLinker.cpp =================================================================== --- tools/dsymutil/DwarfLinker.cpp +++ tools/dsymutil/DwarfLinker.cpp @@ -13,6 +13,7 @@ #include "NonRelocatableStringpool.h" #include "dsymutil.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/DenseSet.h" @@ -75,6 +76,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ThreadPool.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" @@ -100,6 +102,20 @@ namespace { +/// Helper for making strong types. +template class StrongType : public T { +public: + template + explicit StrongType(Args... A) : T(std::forward(A)...) {} +}; + +/// It's very easy to introduce bugs by passing the wrong string pool. By using +/// strong types the interface enforces that the right kind of pool is used. +struct UniqueTag {}; +struct OffsetsTag {}; +using UniquingStringPool = StrongType; +using OffsetsStringPool = StrongType; + /// Small helper that resolves and caches file paths. This helps reduce the /// number of calls to realpath which is expensive. We assume the input are /// files, and cache the realpath of their parent. This way we can quickly @@ -281,7 +297,7 @@ /// dsymutil-classic functionality. PointerIntPair getChildDeclContext(DeclContext &Context, const DWARFDie &DIE, - CompileUnit &Unit, NonRelocatableStringpool &StringPool, + CompileUnit &Unit, UniquingStringPool &StringPool, bool InClangModule); DeclContext &getRoot() { return Root; } @@ -1400,6 +1416,12 @@ } namespace { +/// This map is keyed by the entry PC of functions in that debug object and the +/// associated value is a pair storing the corresponding end PC and the offset +/// to apply to get the linked address. +using RangesTy = std::map>; + +using UnitListTy = std::vector>; /// The core of the Dwarf linking logic. /// @@ -1418,20 +1440,15 @@ class DwarfLinker { public: DwarfLinker(raw_fd_ostream &OutFile, const LinkOptions &Options) - : OutFile(OutFile), Options(Options), BinHolder(Options.Verbose) {} + : OutFile(OutFile), Options(Options) {} /// Link the contents of the DebugMap. bool link(const DebugMap &); - void reportWarning(const Twine &Warning, const DWARFDie *DIE = nullptr) const; + void reportWarning(const Twine &Warning, const DebugMapObject &DMO, + const DWARFDie *DIE = nullptr) const; private: - /// Called at the start of a debug object link. - void startDebugObject(DWARFContext &, DebugMapObject &); - - /// Called at the end of a debug object link. - void endDebugObject(); - /// Remembers the newest DWARF version we've seen in a unit. void maybeUpdateMaxDwarfVersion(unsigned Version) { if (MaxDwarfVersion < Version) @@ -1455,7 +1472,7 @@ } }; - DwarfLinker &Linker; + const DwarfLinker &Linker; /// The valid relocations for the current DebugMapObject. /// This vector is sorted by relocation offset. @@ -1498,6 +1515,37 @@ bool isLittleEndian); }; + /// Keeps track of data associated with one object during linking. + struct LinkContext { + DebugMapObject &DMO; + BinaryHolder BinaryHolder; + const object::ObjectFile *ObjectFile; + RelocationManager RelocMgr; + std::unique_ptr DwarfContext; + RangesTy Ranges; + UnitListTy CompileUnits; + + LinkContext(const DebugMap &Map, DwarfLinker &Linker, DebugMapObject &DMO, + bool Verbose = false) + : DMO(DMO), BinaryHolder(Verbose), RelocMgr(Linker) { + auto ErrOrObj = Linker.loadObject(BinaryHolder, DMO, Map); + ObjectFile = ErrOrObj ? &*ErrOrObj : nullptr; + DwarfContext = ObjectFile ? DWARFContext::create(*ObjectFile) : nullptr; + } + + /// Clear compile units and ranges. + void Clear() { + CompileUnits.clear(); + Ranges.clear(); + } + }; + + /// Called at the start of a debug object link. + void startDebugObject(LinkContext &Context); + + /// Called at the end of a debug object link. + void endDebugObject(LinkContext &Context); + /// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries. /// /// @{ @@ -1505,7 +1553,8 @@ /// keep. Store that information in \p CU's DIEInfo. /// /// The return value indicates whether the DIE is incomplete. - bool lookForDIEsToKeep(RelocationManager &RelocMgr, const DWARFDie &DIE, + bool lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, + UnitListTy &Units, const DWARFDie &DIE, const DebugMapObject &DMO, CompileUnit &CU, unsigned Flags); @@ -1516,14 +1565,23 @@ /// pointing to the module, and a DW_AT_gnu_dwo_id with the module /// hash. bool registerModuleReference(const DWARFDie &CUDie, const DWARFUnit &Unit, - DebugMap &ModuleMap, unsigned Indent = 0); + DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, + OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPoolStringPool, + DeclContextTree &ODRContexts, unsigned &UnitID, + unsigned Indent = 0); /// Recursively add the debug info in this clang module .pcm /// file (and all the modules imported by it in a bottom-up fashion) /// to Units. Error loadClangModule(StringRef Filename, StringRef ModulePath, StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, unsigned Indent = 0); + DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, unsigned &UnitID, + unsigned Indent = 0); /// Flags passed to DwarfLinker::lookForDIEsToKeep enum TravesalFlags { @@ -1536,12 +1594,14 @@ }; /// Mark the passed DIE as well as all the ones it depends on as kept. - void keepDIEAndDependencies(RelocationManager &RelocMgr, const DWARFDie &DIE, + void keepDIEAndDependencies(RelocationManager &RelocMgr, RangesTy &Ranges, + UnitListTy &Units, const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo, const DebugMapObject &DMO, CompileUnit &CU, bool UseODR); - unsigned shouldKeepDIE(RelocationManager &RelocMgr, const DWARFDie &DIE, + unsigned shouldKeepDIE(RelocationManager &RelocMgr, RangesTy &Ranges, + const DWARFDie &DIE, const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); @@ -1550,7 +1610,8 @@ CompileUnit::DIEInfo &MyInfo, unsigned Flags); unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, CompileUnit &Unit, + RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); @@ -1591,13 +1652,17 @@ /// applied to the entry point of the function to get the linked address. /// \param Die the output DIE to use, pass NULL to create one. /// \returns the root of the cloned tree or null if nothing was selected. - DIE *cloneDIE(const DWARFDie &InputDIE, CompileUnit &U, int64_t PCOffset, - uint32_t OutOffset, unsigned Flags, DIE *Die = nullptr); + DIE *cloneDIE(const DWARFDie &InputDIE, const DebugMapObject &DMO, + CompileUnit &U, OffsetsStringPool &StringPool, + int64_t PCOffset, uint32_t OutOffset, unsigned Flags, + DIE *Die = nullptr); /// Construct the output DIE tree by cloning the DIEs we /// chose to keep above. If there are no valid relocs, then there's /// nothing to clone/emit. - void cloneAllCompileUnits(DWARFContext &DwarfContext); + void cloneAllCompileUnits(DWARFContext &DwarfContext, + const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool); private: using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; @@ -1634,7 +1699,9 @@ }; /// Helper for cloneDIE. - unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, CompileUnit &U, + unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, + const DebugMapObject &DMO, CompileUnit &U, + OffsetsStringPool &StringPool, const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &AttrInfo); @@ -1644,6 +1711,7 @@ /// \returns the size of the new attribute. unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, const DWARFUnit &U, + OffsetsStringPool &StringPool, AttributesInfo &Info); /// Clone an attribute referencing another DIE and add @@ -1653,6 +1721,7 @@ AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val, + const DebugMapObject &DMO, CompileUnit &Unit); /// Clone an attribute referencing another DIE and add @@ -1672,7 +1741,8 @@ /// Clone a scalar attribute and add it to \p Die. /// \returns the size of the new attribute. unsigned cloneScalarAttribute(DIE &Die, const DWARFDie &InputDIE, - CompileUnit &U, AttributeSpec AttrSpec, + const DebugMapObject &DMO, CompileUnit &U, + AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, AttributesInfo &Info); @@ -1681,17 +1751,19 @@ /// already there. /// \returns is a name was found. bool getDIENames(const DWARFDie &Die, AttributesInfo &Info, - bool StripTemplate = false); + OffsetsStringPool &StringPool, bool StripTemplate = false); /// Create a copy of abbreviation Abbrev. void copyAbbrev(const DWARFAbbreviationDeclaration &Abbrev, bool hasODR); uint32_t hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, + const DebugMapObject &DMO, int RecurseDepth = 0); /// Helper for cloneDIE. void addObjCAccelerator(CompileUnit &Unit, const DIE *Die, - DwarfStringPoolEntryRef Name, bool SkipPubSection); + DwarfStringPoolEntryRef Name, + OffsetsStringPool &StringPool, bool SkipPubSection); }; /// Assign an abbreviation number to \p Abbrev @@ -1699,7 +1771,8 @@ /// Compute and emit debug_ranges section for \p Unit, and /// patch the attributes referencing it. - void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf) const; + void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf, + const DebugMapObject &DMO) const; /// Generate and emit the DW_AT_ranges attribute for a compile_unit if it had /// one. @@ -1708,14 +1781,15 @@ /// Extract the line tables from the original dwarf, extract the relevant /// parts according to the linked function ranges and emit the result in the /// debug_line section. - void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf); + void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf, + RangesTy &Ranges, const DebugMapObject &DMO); /// Emit the accelerator entries for \p Unit. void emitAcceleratorEntriesForUnit(CompileUnit &Unit); /// Patch the frame info for an object file and emit it. - void patchFrameInfoForObject(const DebugMapObject &, DWARFContext &, - unsigned AddressSize); + void patchFrameInfoForObject(const DebugMapObject &, RangesTy &Ranges, + DWARFContext &, unsigned AddressSize); /// FoldingSet that uniques the abbreviations. FoldingSet AbbreviationsSet; @@ -1735,9 +1809,6 @@ BumpPtrAllocator DIEAlloc; /// @} - /// ODR Contexts for that link. - DeclContextTree ODRContexts; - /// \defgroup Helpers Various helper methods. /// /// @{ @@ -1745,42 +1816,19 @@ /// Attempt to load a debug object from disk. ErrorOr loadObject(BinaryHolder &BinaryHolder, - DebugMapObject &Obj, + const DebugMapObject &Obj, const DebugMap &Map); /// @} raw_fd_ostream &OutFile; LinkOptions Options; - BinaryHolder BinHolder; std::unique_ptr Streamer; uint64_t OutputDebugInfoSize; - - /// A unique ID that identifies each compile unit. - unsigned UnitID; - unsigned MaxDwarfVersion = 0; - /// The units of the current debug map object. - std::vector> Units; - - /// The debug map object currently under consideration. - DebugMapObject *CurrentDebugObject; - - /// The Dwarf string pool. - NonRelocatableStringpool StringPool; - - /// This map is keyed by the entry PC of functions in that - /// debug object and the associated value is a pair storing the - /// corresponding end PC and the offset to apply to get the linked - /// address. - /// - /// See startDebugObject() for a more complete description of its use. - std::map> Ranges; - - /// The CIEs that have been emitted in the output - /// section. The actual CIE data serves a the key to this StringMap, - /// this takes care of comparing the semantics of CIEs defined in - /// different object files. + /// The CIEs that have been emitted in the output section. The actual CIE + /// data serves a the key to this StringMap, this takes care of comparing the + /// semantics of CIEs defined in different object files. StringMap EmittedCIEs; /// Offset of the last CIE that has been emitted in the output @@ -1819,7 +1867,7 @@ /// The resulting DIE might be in another CompileUnit which is stored into \p /// ReferencedCU. \returns null if resolving fails for any reason. static DWARFDie -resolveDIEReference(const DwarfLinker &Linker, +resolveDIEReference(const DwarfLinker &Linker, const DebugMapObject &DMO, std::vector> &Units, const DWARFFormValue &RefValue, const DWARFUnit &Unit, const DWARFDie &DIE, CompileUnit *&RefCU) { @@ -1834,7 +1882,7 @@ return RefDie; } - Linker.reportWarning("could not find referenced DIE", &DIE); + Linker.reportWarning("could not find referenced DIE", DMO, &DIE); return DWARFDie(); } @@ -1883,7 +1931,7 @@ PointerIntPair DeclContextTree::getChildDeclContext( DeclContext &Context, const DWARFDie &DIE, CompileUnit &U, - NonRelocatableStringpool &StringPool, bool InClangModule) { + UniquingStringPool &StringPool, bool InClangModule) { unsigned Tag = DIE.getTag(); // FIXME: dsymutil-classic compat: We should bail out here if we @@ -2049,6 +2097,7 @@ bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die, AttributesInfo &Info, + OffsetsStringPool &StringPool, bool StripTemplate) { // This function will be called on DIEs having low_pcs and // ranges. As getting the name might be more expansive, filter out @@ -2060,17 +2109,17 @@ // short name. if (!Info.MangledName) if (const char *MangledName = Die.getName(DINameKind::LinkageName)) - Info.MangledName = Linker.StringPool.getEntry(MangledName); + Info.MangledName = StringPool.getEntry(MangledName); if (!Info.Name) if (const char *Name = Die.getName(DINameKind::ShortName)) - Info.Name = Linker.StringPool.getEntry(Name); + Info.Name = StringPool.getEntry(Name); if (StripTemplate && Info.Name && Info.MangledName != Info.Name) { // FIXME: dsymutil compatibility. This is wrong for operator< auto Split = Info.Name.getString().split('<'); if (!Split.second.empty()) - Info.NameWithoutTemplate = Linker.StringPool.getEntry(Split.first); + Info.NameWithoutTemplate = StringPool.getEntry(Split.first); } return Info.Name || Info.MangledName; @@ -2078,11 +2127,9 @@ /// Report a warning to the user, optionally including information about a /// specific \p DIE related to the warning. -void DwarfLinker::reportWarning(const Twine &Warning, +void DwarfLinker::reportWarning(const Twine &Warning, const DebugMapObject &DMO, const DWARFDie *DIE) const { - StringRef Context = ""; - if (CurrentDebugObject) - Context = CurrentDebugObject->getObjectFilename(); + StringRef Context = DMO.getObjectFilename(); warn(Warning, Context); if (!Options.Verbose || !DIE) @@ -2113,7 +2160,7 @@ /// (i.e., forward declarations that are children of a DW_TAG_module). static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx, CompileUnit &CU, DeclContext *CurrentDeclContext, - NonRelocatableStringpool &StringPool, + UniquingStringPool &StringPool, DeclContextTree &Contexts, bool InImportedModule = false) { unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); @@ -2185,34 +2232,35 @@ llvm_unreachable("Invalid Tag"); } -void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) { +void DwarfLinker::startDebugObject(LinkContext &Context) { // Iterate over the debug map entries and put all the ones that are - // functions (because they have a size) into the Ranges map. This - // map is very similar to the FunctionRanges that are stored in each - // unit, with 2 notable differences: - // - obviously this one is global, while the other ones are per-unit. - // - this one contains not only the functions described in the DIE - // tree, but also the ones that are only in the debug map. - // The latter information is required to reproduce dsymutil's logic - // while linking line tables. The cases where this information - // matters look like bugs that need to be investigated, but for now - // we need to reproduce dsymutil's behavior. + // functions (because they have a size) into the Ranges map. This map is + // very similar to the FunctionRanges that are stored in each unit, with 2 + // notable differences: + // + // 1. Obviously this one is global, while the other ones are per-unit. + // + // 2. This one contains not only the functions described in the DIE + // tree, but also the ones that are only in the debug map. + // + // The latter information is required to reproduce dsymutil's logic while + // linking line tables. The cases where this information matters look like + // bugs that need to be investigated, but for now we need to reproduce + // dsymutil's behavior. // FIXME: Once we understood exactly if that information is needed, // maybe totally remove this (or try to use it to do a real // -gline-tables-only on Darwin. - for (const auto &Entry : Obj.symbols()) { + for (const auto &Entry : Context.DMO.symbols()) { const auto &Mapping = Entry.getValue(); if (Mapping.Size && Mapping.ObjectAddress) - Ranges[*Mapping.ObjectAddress] = std::make_pair( + Context.Ranges[*Mapping.ObjectAddress] = std::make_pair( *Mapping.ObjectAddress + Mapping.Size, int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); } } -void DwarfLinker::endDebugObject() { - Units.clear(); - Ranges.clear(); - +void DwarfLinker::endDebugObject(LinkContext &Context) { + Context.Clear(); for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) (*I)->~DIEBlock(); for (auto I = DIELocs.begin(), E = DIELocs.end(); I != E; ++I) @@ -2266,14 +2314,16 @@ if (isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc), Obj.getArch())) { SkipNext = true; - Linker.reportWarning(" unsupported relocation in debug_info section."); + Linker.reportWarning(" unsupported relocation in debug_info section.", + DMO); continue; } unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); uint64_t Offset64 = Reloc.getOffset(); if ((RelocSize != 4 && RelocSize != 8)) { - Linker.reportWarning(" unsupported relocation in debug_info section."); + Linker.reportWarning(" unsupported relocation in debug_info section.", + DMO); continue; } uint32_t Offset = Offset64; @@ -2298,7 +2348,7 @@ Expected SymbolName = Sym->getName(); if (!SymbolName) { consumeError(SymbolName.takeError()); - Linker.reportWarning("error getting relocation symbol name."); + Linker.reportWarning("error getting relocation symbol name.", DMO); continue; } if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) @@ -2321,8 +2371,8 @@ if (auto *MachOObj = dyn_cast(&Obj)) findValidRelocsMachO(Section, *MachOObj, DMO); else - Linker.reportWarning(Twine("unsupported object file type: ") + - Obj.getFileName()); + Linker.reportWarning( + Twine("unsupported object file type: ") + Obj.getFileName(), DMO); if (ValidRelocs.empty()) return false; @@ -2466,11 +2516,10 @@ /// Check if a function describing DIE should be kept. /// \returns updated TraversalFlags. -unsigned DwarfLinker::shouldKeepSubprogramDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, - CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, - unsigned Flags) { +unsigned DwarfLinker::shouldKeepSubprogramDIE( + RelocationManager &RelocMgr, RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); Flags |= TF_InFunctionScope; @@ -2516,7 +2565,8 @@ Optional HighPc = DIE.getHighPC(*LowPc); if (!HighPc) { - reportWarning("Function without high_pc. Range will be discarded.\n", &DIE); + reportWarning("Function without high_pc. Range will be discarded.\n", DMO, + &DIE); return Flags; } @@ -2529,7 +2579,9 @@ /// Check if a DIE should be kept. /// \returns updated TraversalFlags. unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, CompileUnit &Unit, + RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, + CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags) { switch (DIE.getTag()) { @@ -2538,7 +2590,8 @@ return shouldKeepVariableDIE(RelocMgr, DIE, Unit, MyInfo, Flags); case dwarf::DW_TAG_subprogram: case dwarf::DW_TAG_label: - return shouldKeepSubprogramDIE(RelocMgr, DIE, Unit, MyInfo, Flags); + return shouldKeepSubprogramDIE(RelocMgr, Ranges, DIE, DMO, Unit, MyInfo, + Flags); case dwarf::DW_TAG_imported_module: case dwarf::DW_TAG_imported_declaration: case dwarf::DW_TAG_imported_unit: @@ -2560,6 +2613,7 @@ /// TraversalFlags to inform it that it's not doing the primary DIE /// tree walk. void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, + RangesTy &Ranges, UnitListTy &Units, const DWARFDie &Die, CompileUnit::DIEInfo &MyInfo, const DebugMapObject &DMO, @@ -2576,7 +2630,8 @@ unsigned AncestorIdx = MyInfo.ParentIdx; while (!CU.getInfo(AncestorIdx).Keep) { unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, Unit.getDIEAtIndex(AncestorIdx), DMO, CU, + lookForDIEsToKeep(RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), + DMO, CU, TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; } @@ -2600,8 +2655,8 @@ Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit); CompileUnit *ReferencedCU; - if (auto RefDie = - resolveDIEReference(*this, Units, Val, Unit, Die, ReferencedCU)) { + if (auto RefDie = resolveDIEReference(*this, DMO, Units, Val, Unit, Die, + ReferencedCU)) { uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie); CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() && @@ -2626,7 +2681,7 @@ Info.Prune = false; unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, RefDie, DMO, *ReferencedCU, + lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, DMO, *ReferencedCU, TF_Keep | TF_DependencyWalk | ODRFlag); // The incomplete property is propagated if the current DIE is complete @@ -2657,6 +2712,7 @@ /// /// The return value indicates whether the DIE is incomplete. bool DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, + RangesTy &Ranges, UnitListTy &Units, const DWARFDie &Die, const DebugMapObject &DMO, CompileUnit &CU, unsigned Flags) { @@ -2675,12 +2731,13 @@ // We must not call shouldKeepDIE while called from keepDIEAndDependencies, // because it would screw up the relocation finding logic. if (!(Flags & TF_DependencyWalk)) - Flags = shouldKeepDIE(RelocMgr, Die, CU, MyInfo, Flags); + Flags = shouldKeepDIE(RelocMgr, Ranges, Die, DMO, CU, MyInfo, Flags); // If it is a newly kept DIE mark it as well as all its dependencies as kept. if (!AlreadyKept && (Flags & TF_Keep)) { bool UseOdr = (Flags & TF_DependencyWalk) ? (Flags & TF_ODR) : CU.hasODR(); - keepDIEAndDependencies(RelocMgr, Die, MyInfo, DMO, CU, UseOdr); + keepDIEAndDependencies(RelocMgr, Ranges, Units, Die, MyInfo, DMO, CU, + UseOdr); } // The TF_ParentWalk flag tells us that we are currently walking up // the parent chain of a required DIE, and we don't want to mark all @@ -2696,7 +2753,8 @@ bool Incomplete = false; for (auto Child : Die.children()) { - Incomplete |= lookForDIEsToKeep(RelocMgr, Child, DMO, CU, Flags); + Incomplete |= + lookForDIEsToKeep(RelocMgr, Ranges, Units, Child, DMO, CU, Flags); // If any of the members are incomplete we propagate the incompleteness. if (!MyInfo.Incomplete && Incomplete && @@ -2737,27 +2795,30 @@ } } -unsigned DwarfLinker::DIECloner::cloneStringAttribute(DIE &Die, - AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const DWARFUnit &U, - AttributesInfo &Info) { +unsigned DwarfLinker::DIECloner::cloneStringAttribute( + DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, + const DWARFUnit &U, OffsetsStringPool &StringPool, AttributesInfo &Info) { // Switch everything to out of line strings. const char *String = *Val.getAsCString(); - auto StringEntry = Linker.StringPool.getEntry(String); + auto StringEntry = StringPool.getEntry(String); + + // Update attributes info. 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(StringEntry.getOffset())); + return 4; } unsigned DwarfLinker::DIECloner::cloneDieReferenceAttribute( DIE &Die, const DWARFDie &InputDIE, AttributeSpec AttrSpec, - unsigned AttrSize, const DWARFFormValue &Val, CompileUnit &Unit) { + unsigned AttrSize, const DWARFFormValue &Val, const DebugMapObject &DMO, + CompileUnit &Unit) { const DWARFUnit &U = Unit.getOrigUnit(); uint32_t Ref = *Val.getAsReference(); DIE *NewRefDie = nullptr; @@ -2765,7 +2826,7 @@ DeclContext *Ctxt = nullptr; DWARFDie RefDie = - resolveDIEReference(Linker, CompileUnits, Val, U, InputDIE, RefUnit); + resolveDIEReference(Linker, DMO, CompileUnits, Val, U, InputDIE, RefUnit); // If the referenced DIE is not found, drop the attribute. if (!RefDie || AttrSpec.Attr == dwarf::DW_AT_sibling) @@ -2917,9 +2978,9 @@ } unsigned DwarfLinker::DIECloner::cloneScalarAttribute( - DIE &Die, const DWARFDie &InputDIE, CompileUnit &Unit, - AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, - AttributesInfo &Info) { + DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, + CompileUnit &Unit, AttributeSpec AttrSpec, const DWARFFormValue &Val, + unsigned AttrSize, AttributesInfo &Info) { uint64_t Value; if (LLVM_UNLIKELY(Linker.Options.Update)) { @@ -2931,7 +2992,8 @@ Value = *OptionalValue; else { Linker.reportWarning( - "Unsupported scalar attribute form. Dropping attribute.", &InputDIE); + "Unsupported scalar attribute form. Dropping attribute.", DMO, + &InputDIE); return 0; } if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) @@ -2955,7 +3017,8 @@ Value = *OptionalValue; else { Linker.reportWarning( - "Unsupported scalar attribute form. Dropping attribute.", &InputDIE); + "Unsupported scalar attribute form. Dropping attribute.", DMO, + &InputDIE); return 0; } PatchLocation Patch = @@ -2982,22 +3045,22 @@ /// value \p Val, and add it to \p Die. /// \returns the size of the cloned attribute. unsigned DwarfLinker::DIECloner::cloneAttribute( - DIE &Die, const DWARFDie &InputDIE, CompileUnit &Unit, - const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &Info) { + DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, + CompileUnit &Unit, OffsetsStringPool &StringPool, const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info) { const DWARFUnit &U = Unit.getOrigUnit(); switch (AttrSpec.Form) { case dwarf::DW_FORM_strp: case dwarf::DW_FORM_string: - return cloneStringAttribute(Die, AttrSpec, Val, U, Info); + return cloneStringAttribute(Die, AttrSpec, Val, U, StringPool, Info); case dwarf::DW_FORM_ref_addr: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: case dwarf::DW_FORM_ref4: case dwarf::DW_FORM_ref8: return cloneDieReferenceAttribute(Die, InputDIE, AttrSpec, AttrSize, Val, - Unit); + DMO, Unit); case dwarf::DW_FORM_block: case dwarf::DW_FORM_block1: case dwarf::DW_FORM_block2: @@ -3015,11 +3078,12 @@ case dwarf::DW_FORM_sec_offset: case dwarf::DW_FORM_flag: case dwarf::DW_FORM_flag_present: - return cloneScalarAttribute(Die, InputDIE, Unit, AttrSpec, Val, AttrSize, - Info); + return cloneScalarAttribute(Die, InputDIE, DMO, Unit, AttrSpec, Val, + AttrSize, Info); default: Linker.reportWarning( - "Unsupported attribute form in cloneAttribute. Dropping.", &InputDIE); + "Unsupported attribute form in cloneAttribute. Dropping.", DMO, + &InputDIE); } return 0; @@ -3111,6 +3175,7 @@ void DwarfLinker::DIECloner::addObjCAccelerator(CompileUnit &Unit, const DIE *Die, DwarfStringPoolEntryRef Name, + OffsetsStringPool &StringPool, bool SkipPubSection) { assert(isObjCSelector(Name.getString()) && "not an objc selector"); // Objective C method or class function. @@ -3125,28 +3190,25 @@ return; StringRef Selector(SelectorStart.data(), SelectorStart.size() - 1); - Unit.addNameAccelerator(Die, Linker.StringPool.getEntry(Selector), - SkipPubSection); + Unit.addNameAccelerator(Die, StringPool.getEntry(Selector), SkipPubSection); // Add an entry for the class name that points to this // method/class function. StringRef ClassName(ClassNameStart.data(), FirstSpace); - Unit.addObjCAccelerator(Die, Linker.StringPool.getEntry(ClassName), - SkipPubSection); + Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassName), SkipPubSection); if (ClassName[ClassName.size() - 1] == ')') { size_t OpenParens = ClassName.find('('); if (OpenParens != StringRef::npos) { StringRef ClassNameNoCategory(ClassName.data(), OpenParens); - Unit.addObjCAccelerator( - Die, Linker.StringPool.getEntry(ClassNameNoCategory), SkipPubSection); + Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassNameNoCategory), + SkipPubSection); std::string MethodNameNoCategory(Name.getString().data(), OpenParens + 2); // FIXME: The missing space here may be a bug, but // dsymutil-classic also does it this way. MethodNameNoCategory.append(SelectorStart); - Unit.addNameAccelerator(Die, - Linker.StringPool.getEntry(MethodNameNoCategory), + Unit.addNameAccelerator(Die, StringPool.getEntry(MethodNameNoCategory), SkipPubSection); } } @@ -3177,9 +3239,11 @@ } DIE *DwarfLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE, - CompileUnit &Unit, int64_t PCOffset, - uint32_t OutOffset, unsigned Flags, - DIE *Die) { + const DebugMapObject &DMO, + CompileUnit &Unit, + OffsetsStringPool &StringPool, + int64_t PCOffset, uint32_t OutOffset, + unsigned Flags, DIE *Die) { DWARFUnit &U = Unit.getOrigUnit(); unsigned Idx = U.getDIEIndex(InputDIE); CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); @@ -3284,8 +3348,8 @@ Val.extractValue(Data, &Offset, U.getFormParams(), &U); AttrSize = Offset - AttrSize; - OutOffset += - cloneAttribute(*Die, InputDIE, Unit, Val, AttrSpec, AttrSize, AttrInfo); + OutOffset += cloneAttribute(*Die, InputDIE, DMO, Unit, StringPool, Val, + AttrSpec, AttrSize, AttrInfo); } // Look for accelerator entries. @@ -3295,7 +3359,7 @@ // accelerator tables too. For now stick with dsymutil's behavior. if ((Info.InDebugMap || AttrInfo.HasLowPc || AttrInfo.HasRanges) && Tag != dwarf::DW_TAG_compile_unit && - getDIENames(InputDIE, AttrInfo, + getDIENames(InputDIE, AttrInfo, StringPool, Tag != dwarf::DW_TAG_inlined_subroutine)) { if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name) Unit.addNameAccelerator(Die, AttrInfo.MangledName, @@ -3308,16 +3372,17 @@ Tag == dwarf::DW_TAG_inlined_subroutine); } if (AttrInfo.Name && isObjCSelector(AttrInfo.Name.getString())) - addObjCAccelerator(Unit, Die, AttrInfo.Name, /* SkipPubSection =*/true); + addObjCAccelerator(Unit, Die, AttrInfo.Name, StringPool, + /* SkipPubSection =*/true); } else if (Tag == dwarf::DW_TAG_namespace) { if (!AttrInfo.Name) - AttrInfo.Name = Linker.StringPool.getEntry("(anonymous namespace)"); + AttrInfo.Name = StringPool.getEntry("(anonymous namespace)"); Unit.addNamespaceAccelerator(Die, AttrInfo.Name); } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && - getDIENames(InputDIE, AttrInfo) && AttrInfo.Name && + getDIENames(InputDIE, AttrInfo, StringPool) && AttrInfo.Name && AttrInfo.Name.getString()[0]) { - uint32_t Hash = hashFullyQualifiedName(InputDIE, Unit); + uint32_t Hash = hashFullyQualifiedName(InputDIE, Unit, DMO); uint64_t RuntimeLang = dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class)) .getValueOr(0); @@ -3358,7 +3423,8 @@ // Recursively clone children. for (auto Child : InputDIE.children()) { - if (DIE *Clone = cloneDIE(Child, Unit, PCOffset, OutOffset, Flags)) { + if (DIE *Clone = cloneDIE(Child, DMO, Unit, StringPool, PCOffset, OutOffset, + Flags)) { Die->addChild(Clone); OutOffset = Clone->getOffset() + Clone->getSize(); } @@ -3375,7 +3441,8 @@ /// and emit them in the output file. Update the relevant attributes /// to point at the new entries. void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, - DWARFContext &OrigDwarf) const { + DWARFContext &OrigDwarf, + const DebugMapObject &DMO) const { DWARFDebugRangeList RangeList; const auto &FunctionRanges = Unit.getFunctionRanges(); unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); @@ -3407,7 +3474,7 @@ CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); if (CurrRange == InvalidRange || CurrRange.start() > First.StartAddress + OrigLowPc) { - reportWarning("no mapping for range."); + reportWarning("no mapping for range.", DMO); continue; } } @@ -3479,7 +3546,9 @@ /// recreate a relocated version of these for the address ranges that /// are present in the binary. void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, - DWARFContext &OrigDwarf) { + DWARFContext &OrigDwarf, + RangesTy &Ranges, + const DebugMapObject &DMO) { DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); if (!StmtList) @@ -3592,7 +3661,7 @@ LineTable.Prologue.getVersion() > 5 || LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || LineTable.Prologue.OpcodeBase > 13) - reportWarning("line table parameters mismatch. Cannot emit."); + reportWarning("line table parameters mismatch. Cannot emit.", DMO); else { uint32_t PrologueEnd = *StmtList + 10 + LineTable.Prologue.PrologueLength; // DWARF v5 has an extra 2 bytes of information before the header_length @@ -3647,6 +3716,7 @@ /// be considered as black boxes and moved as is. The only thing to do /// is to patch the addresses in the headers. void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, + RangesTy &Ranges, DWARFContext &OrigDwarf, unsigned AddrSize) { StringRef FrameData = OrigDwarf.getDWARFObj().getDebugFrameSection(); @@ -3664,7 +3734,7 @@ uint32_t EntryOffset = InputOffset; uint32_t InitialLength = Data.getU32(&InputOffset); if (InitialLength == 0xFFFFFFFF) - return reportWarning("Dwarf64 bits no supported"); + return reportWarning("Dwarf64 bits no supported", DMO); uint32_t CIEId = Data.getU32(&InputOffset); if (CIEId == 0xFFFFFFFF) { @@ -3696,7 +3766,7 @@ // Have we already emitted a corresponding CIE? StringRef CIEData = LocalCIES[CIEId]; if (CIEData.empty()) - return reportWarning("Inconsistent debug_frame content. Dropping."); + return reportWarning("Inconsistent debug_frame content. Dropping.", DMO); // Look if we already emitted a CIE that corresponds to the // referenced one (the CIE data is the key of that lookup). @@ -3740,9 +3810,8 @@ Linker.AssignAbbrev(Copy); } -uint32_t DwarfLinker::DIECloner::hashFullyQualifiedName(DWARFDie DIE, - CompileUnit &U, - int RecurseDepth) { +uint32_t DwarfLinker::DIECloner::hashFullyQualifiedName( + DWARFDie DIE, CompileUnit &U, const DebugMapObject &DMO, int RecurseDepth) { const char *Name = nullptr; DWARFUnit *OrigUnit = &U.getOrigUnit(); CompileUnit *CU = &U; @@ -3760,7 +3829,7 @@ break; CompileUnit *RefCU; - if (auto RefDIE = resolveDIEReference(Linker, CompileUnits, *Ref, + if (auto RefDIE = resolveDIEReference(Linker, DMO, CompileUnits, *Ref, U.getOrigUnit(), DIE, RefCU)) { CU = RefCU; OrigUnit = &RefCU->getOrigUnit(); @@ -3779,9 +3848,10 @@ return djbHash(Name ? Name : "", djbHash(RecurseDepth ? "" : "::")); DWARFDie Die = OrigUnit->getDIEAtIndex(CU->getInfo(Idx).ParentIdx); - return djbHash((Name ? Name : ""), - djbHash((Name ? "::" : ""), - hashFullyQualifiedName(Die, *CU, ++RecurseDepth))); + return djbHash( + (Name ? Name : ""), + djbHash((Name ? "::" : ""), + hashFullyQualifiedName(Die, *CU, DMO, ++RecurseDepth))); } static uint64_t getDwoId(const DWARFDie &CUDie, const DWARFUnit &Unit) { @@ -3792,10 +3862,11 @@ return 0; } -bool DwarfLinker::registerModuleReference(const DWARFDie &CUDie, - const DWARFUnit &Unit, - DebugMap &ModuleMap, - unsigned Indent) { +bool DwarfLinker::registerModuleReference( + const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, + const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, + UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, + unsigned &UnitID, unsigned Indent) { std::string PCMfile = dwarf::toString( CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); if (PCMfile.empty()) @@ -3807,7 +3878,7 @@ std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); if (Name.empty()) { - reportWarning("Anonymous module skeleton CU for " + PCMfile); + reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO); return true; } @@ -3824,7 +3895,8 @@ if (Options.Verbose && (Cached->second != DwoId)) reportWarning(Twine("hash mismatch: this object file was built against a " "different version of the module ") + - PCMfile); + PCMfile, + DMO); if (Options.Verbose) outs() << " [cached].\n"; return true; @@ -3835,8 +3907,9 @@ // Cyclic dependencies are disallowed by Clang, but we still // shouldn't run into an infinite loop, so mark it as processed now. ClangModules.insert({PCMfile, DwoId}); - if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, - Indent + 2)) { + if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, + Ranges, StringPool, UniquingStringPool, + ODRContexts, UnitID, Indent + 2)) { consumeError(std::move(E)); return false; } @@ -3844,23 +3917,28 @@ } ErrorOr -DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, +DwarfLinker::loadObject(BinaryHolder &BinaryHolder, const DebugMapObject &Obj, const DebugMap &Map) { auto ErrOrObjs = BinaryHolder.GetObjectFiles(Obj.getObjectFilename(), Obj.getTimestamp()); if (std::error_code EC = ErrOrObjs.getError()) { - reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message()); + reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message(), Obj); return EC; } auto ErrOrObj = BinaryHolder.Get(Map.getTriple()); if (std::error_code EC = ErrOrObj.getError()) - reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message()); + reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message(), Obj); return ErrOrObj; } Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, unsigned Indent) { + DebugMap &ModuleMap, + const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool, + UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, + unsigned &UnitID, unsigned Indent) { SmallString<80> Path(Options.PrependPath); if (sys::path::is_relative(Filename)) sys::path::append(Path, ModulePath, Filename); @@ -3872,7 +3950,7 @@ auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap); if (!ErrOrObj) { // Try and emit more helpful warnings by applying some heuristics. - StringRef ObjFile = CurrentDebugObject->getObjectFilename(); + StringRef ObjFile = DMO.getObjectFilename(); bool isClangModule = sys::path::extension(Filename).equals(".pcm"); bool isArchive = ObjFile.endswith(")"); if (isClangModule) { @@ -3915,7 +3993,9 @@ // Recursively get all modules imported by this one. auto CUDie = CU->getUnitDIE(false); - if (!registerModuleReference(CUDie, *CU, ModuleMap, Indent)) { + if (!registerModuleReference(CUDie, *CU, ModuleMap, DMO, Ranges, StringPool, + UniquingStringPool, ODRContexts, UnitID, + Indent)) { if (Unit) { std::string Err = (Filename + @@ -3933,7 +4013,8 @@ reportWarning( Twine("hash mismatch: this object file was built against a " "different version of the module ") + - Filename); + Filename, + DMO); // Update the cache entry with the DwoId of the module loaded from disk. ClangModules[Filename] = PCMDwoId; } @@ -3942,8 +4023,8 @@ Unit = llvm::make_unique(*CU, UnitID++, !Options.NoODR, ModuleName); Unit->setHasInterestingContent(); - analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), StringPool, - ODRContexts); + analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), + UniquingStringPool, ODRContexts); // Keep everything. Unit->markEverythingAsKept(); } @@ -3958,11 +4039,13 @@ std::vector> CompileUnits; CompileUnits.push_back(std::move(Unit)); DIECloner(*this, RelocMgr, DIEAlloc, CompileUnits, Options) - .cloneAllCompileUnits(*DwarfContext); + .cloneAllCompileUnits(*DwarfContext, DMO, Ranges, StringPool); return Error::success(); } -void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) { +void DwarfLinker::DIECloner::cloneAllCompileUnits( + DWARFContext &DwarfContext, const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool) { if (!Linker.Streamer) return; @@ -3973,7 +4056,7 @@ // Clone the InputDIE into your Unit DIE in our compile unit since it // already has a DIE inside of it. CurrentUnit->createOutputDIE(); - cloneDIE(InputDIE, *CurrentUnit, 0 /* PC offset */, + cloneDIE(InputDIE, DMO, *CurrentUnit, StringPool, 0 /* PC offset */, 11 /* Unit Header size */, 0, CurrentUnit->getOutputUnitDIE()); } Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); @@ -3984,9 +4067,9 @@ // 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.patchLineTableForUnit(*CurrentUnit, DwarfContext, Ranges, DMO); Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - Linker.patchRangesForUnit(*CurrentUnit, DwarfContext); + Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); } else { Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); @@ -4015,19 +4098,39 @@ // Size of the DIEs (and headers) generated for the linked output. OutputDebugInfoSize = 0; // A unique ID that identifies each compile unit. - UnitID = 0; + unsigned UnitID = 0; DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath()); + // First populate the data structure we need for each iteration of the + // parallel loop. + unsigned NumObjects = Map.getNumberOfObjects(); + std::vector ObjectContexts; + ObjectContexts.reserve(NumObjects); for (const auto &Obj : Map.objects()) { - CurrentDebugObject = Obj.get(); + ObjectContexts.emplace_back(Map, *this, *Obj.get(), Options.Verbose); + } + + // This Dwarf string pool which is only used for uniquing. This one should + // never be used for offsets as its not thread-safe or predictable. + UniquingStringPool UniquingStringPool; + // This Dwarf string pool which is used for emission. It must be used + // serially as the order of calling getStringOffset matters for + // reproducibility. + OffsetsStringPool OffsetsStringPool; + + // ODR Contexts for the link. + DeclContextTree ODRContexts; + + for (LinkContext &LinkContext : ObjectContexts) { if (Options.Verbose) - outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; + outs() << "DEBUG MAP OBJECT: " << LinkContext.DMO.getObjectFilename() + << "\n"; // N_AST objects (swiftmodule files) should get dumped directly into the // appropriate DWARF section. - if (Obj->getType() == MachO::N_AST) { - StringRef File = Obj->getObjectFilename(); + if (LinkContext.DMO.getType() == MachO::N_AST) { + StringRef File = LinkContext.DMO.getObjectFilename(); auto ErrorOrMem = MemoryBuffer::getFile(File); if (!ErrorOrMem) { errs() << "Warning: Could not open " << File << "\n"; @@ -4038,11 +4141,12 @@ errs() << "Warning: " << errc.message() << "\n"; continue; } - if (!Options.NoTimestamp && Stat.getLastModificationTime() != - sys::TimePoint<>(Obj->getTimestamp())) { + if (!Options.NoTimestamp && + Stat.getLastModificationTime() != + sys::TimePoint<>(LinkContext.DMO.getTimestamp())) { errs() << "Warning: Timestamp mismatch for " << File << ": " << Stat.getLastModificationTime() << " and " - << sys::TimePoint<>(Obj->getTimestamp()) << "\n"; + << sys::TimePoint<>(LinkContext.DMO.getTimestamp()) << "\n"; continue; } @@ -4052,25 +4156,32 @@ continue; } - auto ErrOrObj = loadObject(BinHolder, *Obj, Map); - if (!ErrOrObj) + if (!LinkContext.ObjectFile) continue; // Look for relocations that correspond to debug map entries. - RelocationManager RelocMgr(*this); if (LLVM_LIKELY(!Options.Update) && - !RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { + !LinkContext.RelocMgr.findValidRelocsInDebugInfo( + *LinkContext.ObjectFile, LinkContext.DMO)) { if (Options.Verbose) outs() << "No valid relocations found. Skipping.\n"; + + // Clear this ObjFile entry as a signal to other loops that we should not + // process this iteration. + LinkContext.ObjectFile = nullptr; continue; } // Setup access to the debug info. - auto DwarfContext = DWARFContext::create(*ErrOrObj); - startDebugObject(*DwarfContext, *Obj); + if (!LinkContext.DwarfContext) + continue; + + startDebugObject(LinkContext); // In a first phase, just read in the debug info and load all clang modules. - for (const auto &CU : DwarfContext->compile_units()) { + LinkContext.CompileUnits.reserve( + LinkContext.DwarfContext->getNumCompileUnits()); + for (const auto &CU : LinkContext.DwarfContext->compile_units()) { auto CUDie = CU->getUnitDIE(false); if (Options.Verbose) { outs() << "Input compilation unit:"; @@ -4081,58 +4192,115 @@ } if (!CUDie || LLVM_UNLIKELY(Options.Update) || - !registerModuleReference(CUDie, *CU, ModuleMap)) { - Units.push_back(llvm::make_unique( + !registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, + LinkContext.Ranges, OffsetsStringPool, + UniquingStringPool, ODRContexts, UnitID)) { + LinkContext.CompileUnits.push_back(llvm::make_unique( *CU, UnitID++, !Options.NoODR && !Options.Update, "")); maybeUpdateMaxDwarfVersion(CU->getVersion()); } } + } - // Now build the DIE parent links that we will use during the next phase. - for (auto &CurrentUnit : Units) - analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, - *CurrentUnit, &ODRContexts.getRoot(), StringPool, - ODRContexts); - - // 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 because cross-CU - // references require the ParentIdx to be setup for every CU in - // the object file before calling this. - 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); + ThreadPool pool(2); + + // These variables manage the list of processed object files. + // The mutex and condition variable are to ensure that this is thread safe. + std::mutex ProcessedFilesMutex; + std::condition_variable ProcessedFilesConditionVariable; + BitVector ProcessedFiles(NumObjects, false); + + // Now do analyzeContextInfo in parallel as it is particularly expensive. + pool.async([&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + auto &LinkContext = ObjectContexts[i]; + + if (!LinkContext.ObjectFile) { + std::unique_lock LockGuard(ProcessedFilesMutex); + ProcessedFiles.set(i); + ProcessedFilesConditionVariable.notify_one(); + continue; + } + + // Now build the DIE parent links that we will use during the next phase. + for (auto &CurrentUnit : LinkContext.CompileUnits) + analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, + *CurrentUnit, &ODRContexts.getRoot(), + UniquingStringPool, ODRContexts); + + std::unique_lock LockGuard(ProcessedFilesMutex); + ProcessedFiles.set(i); + ProcessedFilesConditionVariable.notify_one(); } + }); + + // And then the remaining work in serial again. + // Note, although this loop runs in serial, it can run in parallel with + // the analyzeContextInfo loop so long as we process files with indices >= + // than those processed by analyzeContextInfo. + pool.async([&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + { + std::unique_lock LockGuard(ProcessedFilesMutex); + if (!ProcessedFiles[i]) { + ProcessedFilesConditionVariable.wait( + LockGuard, [&]() { return ProcessedFiles[i]; }); + } + } - // 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() || LLVM_UNLIKELY(Options.Update)) - DIECloner(*this, RelocMgr, DIEAlloc, Units, Options) - .cloneAllCompileUnits(*DwarfContext); - if (!Options.NoOutput && !Units.empty() && LLVM_LIKELY(!Options.Update)) - patchFrameInfoForObject(*Obj, *DwarfContext, - Units[0]->getOrigUnit().getAddressByteSize()); - - // Clean-up before starting working on the next object. - endDebugObject(); - } + auto &LinkContext = ObjectContexts[i]; + if (!LinkContext.ObjectFile) + continue; - // Emit everything that's global. - if (!Options.NoOutput) { - Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion); - Streamer->emitStrings(StringPool); - Streamer->emitAppleNames(AppleNames); - Streamer->emitAppleNamespaces(AppleNamespaces); - Streamer->emitAppleTypes(AppleTypes); - Streamer->emitAppleObjc(AppleObjc); - } + // 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 because + // cross-cu references require the ParentIdx to be setup for every CU in + // the object file before calling this. + if (LLVM_UNLIKELY(Options.Update)) { + for (auto &CurrentUnit : LinkContext.CompileUnits) + CurrentUnit->markEverythingAsKept(); + Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile, Options); + } else { + for (auto &CurrentUnit : LinkContext.CompileUnits) + lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges, + LinkContext.CompileUnits, + CurrentUnit->getOrigUnit().getUnitDIE(), + LinkContext.DMO, *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. + LinkContext.RelocMgr.resetValidRelocs(); + if (LinkContext.RelocMgr.hasValidRelocs() || + LLVM_UNLIKELY(Options.Update)) + DIECloner(*this, LinkContext.RelocMgr, DIEAlloc, + LinkContext.CompileUnits, Options) + .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO, + LinkContext.Ranges, OffsetsStringPool); + if (!Options.NoOutput && !LinkContext.CompileUnits.empty() && + LLVM_LIKELY(!Options.Update)) + patchFrameInfoForObject( + LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext, + LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize()); + + // Clean-up before starting working on the next object. + endDebugObject(LinkContext); + } + + // Emit everything that's global. + if (!Options.NoOutput) { + Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion); + Streamer->emitStrings(OffsetsStringPool); + Streamer->emitAppleNames(AppleNames); + Streamer->emitAppleNamespaces(AppleNamespaces); + Streamer->emitAppleTypes(AppleTypes); + Streamer->emitAppleObjc(AppleObjc); + } + }); + + pool.wait(); return Options.NoOutput ? true : Streamer->finish(Map); }