diff --git a/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h b/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h --- a/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h +++ b/llvm/include/llvm/CodeGen/NonRelocatableStringpool.h @@ -55,6 +55,14 @@ /// strings which were added via internString only. std::vector getEntriesForEmission() const; + /// Clear contents. + void clear() { + Strings.clear(); + CurrentEndOffset = 0; + NumEntries = 0; + Translator = nullptr; + } + private: MapTy Strings; uint64_t CurrentEndOffset = 0; diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -91,7 +91,7 @@ /// Returns all valid functions address ranges(i.e., those ranges /// which points to sections with code). - virtual RangesTy &getValidAddressRanges() = 0; + virtual void getValidAddressRanges(RangesTy *&Ranges) = 0; /// Erases all data. virtual void clear() = 0; @@ -212,17 +212,23 @@ /// and it`s address map. class DWARFFile { public: - DWARFFile(StringRef Name, DWARFContext *Dwarf, AddressesMap *Addresses, - const std::vector &Warnings) - : FileName(Name), Dwarf(Dwarf), Addresses(Addresses), Warnings(Warnings) { - } + DWARFFile(StringRef Name, sys::TimePoint Timestamp, + std::unique_ptr Dwarf, + std::unique_ptr Addresses, + const std::vector &Warnings, + std::function Timestamp)>) + : FileName(Name), Timestamp(Timestamp), Dwarf(std::move(Dwarf)), + Addresses(std::move(Addresses)), Warnings(Warnings) {} /// object file name. StringRef FileName; + /// object file timestamp. + sys::TimePoint Timestamp; /// source DWARF information. - DWARFContext *Dwarf = nullptr; + std::unique_ptr Dwarf; /// helpful address information(list of valid address ranges, relocations). - AddressesMap *Addresses = nullptr; + std::unique_ptr Addresses; /// warnings for object file. const std::vector &Warnings; }; diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinkerNext/DWARFLinker.h copy from llvm/include/llvm/DWARFLinker/DWARFLinker.h copy to llvm/include/llvm/DWARFLinkerNext/DWARFLinker.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinkerNext/DWARFLinker.h @@ -6,27 +6,24 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_DWARFLINKER_DWARFLINKER_H -#define LLVM_DWARFLINKER_DWARFLINKER_H +#ifndef LLVM_DWARFLINKERNEXT_DWARFLINKER_H +#define LLVM_DWARFLINKERNEXT_DWARFLINKER_H #include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/NonRelocatableStringpool.h" -#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/MC/MCDwarf.h" +#include #include +#include namespace llvm { -enum class DwarfLinkerClient { Dsymutil, LLD, General }; +namespace dwarflinker { -/// The kind of accelerator tables we should emit. -enum class AccelTableKind { - Apple, ///< .apple_names, .apple_namespaces, .apple_types, .apple_objc. - Dwarf, ///< DWARF v5 .debug_names. - Default, ///< Dwarf for DWARF5 or later, Apple otherwise. -}; +enum class DwarfLinkerClient { Dsymutil, LLD, General }; /// Partial address range. Besides an offset, only the /// HighPC is stored. The structure is stored in a map where the LowPC is the @@ -51,6 +48,8 @@ /// by debug information. Valid addresses are those which points to /// live code sections. i.e. relocations for these addresses point /// into sections which would be/are placed into resulting binary. +/// +/// May be used asynchroniously for reading. class AddressesMap { public: virtual ~AddressesMap(); @@ -81,6 +80,8 @@ /// Apply the valid relocations to the buffer \p Data, taking into /// account that Data is at \p BaseOffset in the debug_info section. /// + /// This function must be called with monotonic \p BaseOffset values. + /// /// \returns true whether any reloc has been applied. virtual bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) = 0; @@ -91,17 +92,21 @@ /// Returns all valid functions address ranges(i.e., those ranges /// which points to sections with code). - virtual RangesTy &getValidAddressRanges() = 0; + virtual void getValidAddressRanges(RangesTy *&Ranges) = 0; /// Erases all data. virtual void clear() = 0; }; /// DwarfEmitter presents interface to generate all debug info tables. +/// +/// Can not be used asynchroniously. class DwarfEmitter { public: virtual ~DwarfEmitter(); + virtual const Triple &getTargetTriple() const = 0; + /// Emit DIE containing warnings. virtual void emitPaperTrailWarningsDie(DIE &Die) = 0; @@ -117,24 +122,20 @@ virtual void emitStrings(const NonRelocatableStringpool &Pool) = 0; /// Emit DWARF debug names. - virtual void - emitDebugNames(AccelTable &Table) = 0; + virtual void emitDebugNames(AccelTable &) {} /// Emit Apple namespaces accelerator table. virtual void - emitAppleNamespaces(AccelTable &Table) = 0; + emitAppleNamespaces(AccelTable &) {} /// Emit Apple names accelerator table. - virtual void - emitAppleNames(AccelTable &Table) = 0; + virtual void emitAppleNames(AccelTable &) {} /// Emit Apple Objective-C accelerator table. - virtual void - emitAppleObjc(AccelTable &Table) = 0; + virtual void emitAppleObjc(AccelTable &) {} /// Emit Apple type accelerator table. - virtual void - emitAppleTypes(AccelTable &Table) = 0; + virtual void emitAppleTypes(AccelTable &) {} /// Emit debug_ranges for \p FuncRange by translating the /// original \p Entries. @@ -178,7 +179,7 @@ /// \p Dwarf and offsetting them. Update the location attributes to point to /// the new entries. virtual void emitLocationsForUnit( - const CompileUnit &Unit, DWARFContext &Dwarf, + CompileUnit &Unit, DWARFContext &Dwarf, std::function &)> ProcessExpr) = 0; @@ -187,8 +188,7 @@ /// /// As a side effect, this also switches the current Dwarf version /// of the MC layer to the one of U.getOrigUnit(). - virtual void emitCompileUnitHeader(CompileUnit &Unit, - unsigned DwarfVersion) = 0; + virtual void emitCompileUnitHeader(CompileUnit &Unit) = 0; /// Recursively emit the DIE tree rooted at \p Die. virtual void emitDIE(DIE &Die) = 0; @@ -207,24 +207,51 @@ }; using UnitListTy = std::vector>; +using UnloadCallback = std::function Timestamp)>; /// this class represents DWARF information for source file /// and it`s address map. +/// +/// May be used asynchroniously for reading. class DWARFFile { public: - DWARFFile(StringRef Name, DWARFContext *Dwarf, AddressesMap *Addresses, - const std::vector &Warnings) - : FileName(Name), Dwarf(Dwarf), Addresses(Addresses), Warnings(Warnings) { + DWARFFile(StringRef Name, sys::TimePoint Timestamp, + std::unique_ptr Dwarf, + std::unique_ptr Addresses, + const std::vector &Warnings, UnloadCallback unloadFunc) + : FileName(Name), Timestamp(Timestamp), Dwarf(std::move(Dwarf)), + Addresses(std::move(Addresses)), Warnings(Warnings), + unloadFunc(unloadFunc) { + if (Dwarf) + Endianess = Dwarf->isLittleEndian() ? support::endianness::little + : support::endianness::big; } /// object file name. StringRef FileName; + /// object file timestamp. + sys::TimePoint Timestamp; /// source DWARF information. - DWARFContext *Dwarf = nullptr; + std::unique_ptr Dwarf; /// helpful address information(list of valid address ranges, relocations). - AddressesMap *Addresses = nullptr; + std::unique_ptr Addresses; /// warnings for object file. const std::vector &Warnings; + /// Endiannes of source DWARF information. + support::endianness Endianess = support::endianness::little; + + /// Callback to the module keeping object file to unload. + UnloadCallback unloadFunc; + + /// Unloads object file and corresponding AddressesMap and Dwarf Context. + void unload() { + Addresses.reset(); + Dwarf.reset(); + + if (unloadFunc) + unloadFunc(FileName, Timestamp); + } }; typedef std::function swiftInterfacesMap; typedef std::map objectPrefixMap; -/// The core of the Dwarf linking logic. -/// -/// The generation of the dwarf information from the object files will be -/// driven by the selection of 'root DIEs', which are DIEs that -/// describe variables or functions that resolves to the corresponding -/// code section(and thus have entries in the Addresses map). All the debug -/// information that will be generated(the DIEs, but also the line -/// tables, ranges, ...) is derived from that set of root DIEs. -/// -/// The root DIEs are identified because they contain relocations that -/// points to code section(the low_pc for a function, the location for -/// a variable). These relocations are called ValidRelocs in the -/// AddressesInfo and are gathered as a very first step when we start -/// processing a object file. -class DWARFLinker { -public: - DWARFLinker(DwarfEmitter *Emitter, - DwarfLinkerClient ClientID = DwarfLinkerClient::General) - : TheDwarfEmitter(Emitter), DwarfLinkerClientID(ClientID) {} +class DwarfStreamer; - /// Add object file to be linked. - void addObjectFile(DWARFFile &File); +/// Hold the input and output of the debug info size in bytes. +struct DebugInfoSize { + uint64_t Input; + uint64_t Output; +}; - /// Link debug info for added objFiles. Object - /// files are linked all together. - bool link(); +/// Keeps track of data associated with one object during linking. +struct LinkContext { + struct FDERecordPatch { + FDERecordPatch(uint64_t OffsetToFDERecord) + : OffsetToFDERecord(OffsetToFDERecord) {} + /// Offset to the FDE record which need to update reference to CIE record. + uint64_t OffsetToFDERecord; + }; + /// Object file descriptor. + DWARFFile &File; - /// A number of methods setting various linking options: + /// Set of Compilation Units(may be accessed asynchroniously for reading). + UnitListTy CompileUnits; - /// Allows to generate log of linking process to the standard output. - void setVerbosity(bool Verbose) { Options.Verbose = Verbose; } + /// Set of Compile Units for modules. May be updated asynchroniously. + UnitListTy ModulesCompileUnits; + std::mutex ModulesCompileUnitsMutex; - /// Print statistics to standard output. - void setStatistics(bool Statistics) { Options.Statistics = Statistics; } + /// Semaphore for Compile Units line tables. + std::mutex LineTablesMutex; - /// Do not emit linked dwarf info. - void setNoOutput(bool NoOut) { Options.NoOutput = NoOut; } + /// Size of Debug info before optimizing. + uint64_t OriginalDebugInfoSize = 0; - /// Do not unique types according to ODR. - void setNoODR(bool NoODR) { Options.NoODR = NoODR; } + /// Set of patches for FDE records(containing references to the CIE + /// records, which should be updated). After we glued resulting file + /// from the pieces corresponding to the source object files, we need + /// to update references to the CIE records with proper offsets. + std::vector FDERecords; - /// update existing DWARF info(for the linked binary). - void setUpdate(bool Update) { Options.Update = Update; } + /// Data, common for all compilation units. + SmallString<4096> OutDebugInfoBytes; - /// Use specified number of threads for parallel files linking. - void setNumThreads(unsigned NumThreads) { Options.Threads = NumThreads; } + support::endianness Endianess = support::endianness::little; - /// Set kind of accelerator tables to be generated. - void setAccelTableKind(AccelTableKind Kind) { - Options.TheAccelTableKind = Kind; - } + std::array TableOffsets = {0}; - /// Set prepend path for clang modules. - void setPrependPath(const std::string &Ppath) { Options.PrependPath = Ppath; } + LinkContext(DWARFFile &File) : File(File) { + if (File.Dwarf) { + if (!File.Dwarf->compile_units().empty()) + CompileUnits.reserve(File.Dwarf->getNumCompileUnits()); - /// Set translator which would be used for strings. - void - setStringsTranslator(std::function StringsTranslator) { - this->StringsTranslator = StringsTranslator; + Endianess = File.Dwarf->isLittleEndian() ? support::endianness::little + : support::endianness::big; + } } - /// Set estimated objects files amount, for preliminary data allocation. - void setEstimatedObjfilesAmount(unsigned ObjFilesNum) { - ObjectContexts.reserve(ObjFilesNum); - } + /// Add Compile Unit corresponding to the module. + void addModulesCompileUnit(std::unique_ptr Unit) { + std::unique_lock Guard(ModulesCompileUnitsMutex); - /// Set warning handler which would be used to report warnings. - void setWarningHandler(messageHandler Handler) { - Options.WarningHandler = Handler; + ModulesCompileUnits.insert(ModulesCompileUnits.begin(), std::move(Unit)); } - /// Set error handler which would be used to report errors. - void setErrorHandler(messageHandler Handler) { - Options.ErrorHandler = Handler; + /// Returns set of offsets to FDE records(referencing CIE records) + /// which should be updated after final offsets are become known. + std::vector getFDERecordsPatches() { + return std::move(FDERecords); } - /// Set object files loader which would be used to load - /// additional objects for splitted dwarf. - void setObjFileLoader(objFileLoader Loader) { - Options.ObjFileLoader = Loader; + /// Remember FDE record(we need to update CIE record offset in it). + /// It should be updated after final offsets are become known. + void noteFDERecordToPatch(FDERecordPatch Patch) { + FDERecords.emplace_back(Patch); } - /// Set map for Swift interfaces. - void setSwiftInterfacesMap(swiftInterfacesMap *Map) { - Options.ParseableSwiftInterfaces = Map; + /// Returns Endiannes of the source DWARF information. + support::endianness getEndianness() { return Endianess; } + + /// Link context might contain debug tables which are common for the whole + /// object files(invariant tables, debug_frame). This method returns start + /// offset inside final DWARF file for such tables. + uint64_t getStartOffset(CompileUnit::DebugTableKind TableKind) const { + return TableOffsets[static_cast::type>(TableKind)]; } - /// Set prefix map for objects. - void setObjectPrefixMap(objectPrefixMap *Map) { - Options.ObjectPrefixMap = Map; + /// Set start offset for debug tables common for the whole object file. + void setStartOffset(CompileUnit::DebugTableKind TableKind, uint64_t Offset) { + TableOffsets[static_cast::type>(TableKind)] = Offset; } + CompileUnit *getUnitForOffset(CompileUnit &CU, uint64_t Offset) const; +}; + +/// The core of the Dwarf linking logic. +/// +/// The generation of the dwarf information from the object files will be +/// driven by the selection of 'root DIEs', which are DIEs that +/// describe variables or functions that resolves to the corresponding +/// code section(and thus have entries in the Addresses map). All the debug +/// information that will be generated(the DIEs, but also the line +/// tables, ranges, ...) is derived from that set of root DIEs. +/// +/// The root DIEs are identified because they contain relocations that +/// points to code section(the low_pc for a function, the location for +/// a variable). These relocations are gathered as a very first step +/// when we start processing a object file by AddressesMap. +/// +/// Can not be used asynchroniously. +class DWARFLinker { private: /// Flags passed to DwarfLinker::lookForDIEsToKeep enum TraversalFlags { @@ -339,7 +384,7 @@ TF_SkipPC = 1 << 5, ///< Skip all location attributes. }; - /// The distinct types of work performed by the work loop. + /// The distinct types of work performed by the work loop. enum class WorklistItemType { /// Given a DIE, look for DIEs to be kept. LookForDIEsToKeep, @@ -383,54 +428,344 @@ Flags(Flags), AncestorIdx(AncestorIdx) {} }; + using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; + + /// Information gathered and exchanged between the various + /// clone*Attributes helpers about the attributes of a particular DIE. + /// + /// Can not be used asynchroniously. + struct AttributesInfo { + /// Names. + StringRef Name; + StringRef MangledName; + StringRef NameWithoutTemplate; + + /// Offsets in the string pool. + uint32_t NameOffset = 0; + uint32_t MangledNameOffset = 0; + + /// Value of AT_low_pc in the input DIE + uint64_t OrigLowPc = std::numeric_limits::max(); + + /// Value of AT_high_pc in the input DIE + uint64_t OrigHighPc = 0; + + /// Value of DW_AT_call_return_pc in the input DIE + uint64_t OrigCallReturnPc = 0; + + /// Value of DW_AT_call_pc in the input DIE + uint64_t OrigCallPc = 0; + + /// Offset to apply to PC addresses inside a function. + int64_t PCOffset = 0; + + /// Does the DIE have a low_pc attribute? + bool HasLowPc = false; + + /// Is this DIE only a declaration? + bool IsDeclaration = false; + + /// Attributes which must be updated with proper offsets. + /// When we glue final file from the pieces corresponding + /// to the compile units, we know final offsets. Some attributes + /// should be updated with these offsets. Following members + /// keeps information about such attributes. + std::vector RangeAttrReferences; + std::vector LocationAttrReferences; + std::vector AttrReferences; + + AttributesInfo() = default; + }; + + /// linking options + struct DWARFLinkerOptions { + /// Generate processing log to the standard output. + bool Verbose = false; + + /// Print statistics. + bool Statistics = false; + + /// Skip emitting output + bool NoOutput = false; + + /// Do not unique types according to ODR + bool NoODR = false; + + /// Update + bool RemoveObsoleteDebugInfo = true; + + /// Number of threads. + unsigned Threads = 1; + + /// Generate index tables. + bool GenerateAppleNamespaces = false; + bool GenerateAppleNames = false; + bool GenerateAppleTypes = false; + bool GenerateAppleObjC = false; + bool GeneratePubNames = false; + bool GeneratePubTypes = false; + bool GenerateDebugNames = false; + + /// Prepend path for the clang modules. + std::string PrependPath; + + /// warning handler(it might be called asynchronously). + messageHandler WarningHandler = nullptr; + + /// error handler(it might be called asynchronously). + messageHandler ErrorHandler = nullptr; + + /// Callback for load object file(it might be called asynchronously). + objFileLoader ObjFileLoader = nullptr; + + /// A list of all .swiftinterface files referenced by the debug + /// info, mapping Module name to path on disk. The entries need to + /// be uniqued and sorted and there are only few entries expected + /// per compile unit, which is why this is a std::map. + /// this is dsymutil specific fag. + /// + /// (it might be called asynchronously). + swiftInterfacesMap *ParseableSwiftInterfaces = nullptr; + + /// A list of remappings to apply to file paths. + /// + /// (it might be called asynchronously). + objectPrefixMap *ObjectPrefixMap = nullptr; + } Options; + +public: + DWARFLinker(DwarfEmitter *Emitter, + DwarfLinkerClient ClientID = DwarfLinkerClient::General, + std::function StringsTranslator = nullptr); + + /// Add object file \p File to be linked. + void addObjectFile(DWARFFile &File); + + /// Link debug info for added objFiles. Object + /// files are linked all together. + bool link(); + + /// \defgroup Methods setting various linking options: + /// + /// @{ + /// + + /// Allows to generate log of linking process to the standard output. + void setVerbosity(bool Verbose) { Options.Verbose = Verbose; } + + /// Print statistics to standard output. + void setStatistics(bool Statistics) { Options.Statistics = Statistics; } + + /// Do not emit linked dwarf info. + void setNoOutput(bool NoOut) { Options.NoOutput = NoOut; } + + /// Do not unique types according to ODR. + void setNoODR(bool NoODR) { Options.NoODR = NoODR; } + + /// update existing DWARF info(for the linked binary). + void setRemoveObsoleteDebugInfo(bool RemoveObsoleteDebugInfo) { + Options.RemoveObsoleteDebugInfo = RemoveObsoleteDebugInfo; + } + + /// Use specified number of threads for parallel files linking. + void setNumThreads(unsigned NumThreads) { Options.Threads = NumThreads; } + + /// Set generation Apple namespaces On/Off. + void setGenerateAppleNamespaces(bool generate) { + Options.GenerateAppleNamespaces = generate; + } + + /// Set generation Apple names On/Off. + void setGenerateAppleNames(bool generate) { + Options.GenerateAppleNames = generate; + } + + /// Set generation Apple types On/Off. + void setGenerateAppleTypes(bool generate) { + Options.GenerateAppleTypes = generate; + } + + /// Set generation Apple ObjC On/Off. + void setGenerateAppleObjC(bool generate) { + Options.GenerateAppleObjC = generate; + } + + /// Set generation Pubnames On/Off. + void setGeneratePubNames(bool generate) { + Options.GeneratePubNames = generate; + } + + /// Set generation Pubtypes On/Off. + void setGeneratePubTypes(bool generate) { + Options.GeneratePubTypes = generate; + } + + /// Set generation Debug names On/Off. + void setGenerateDebugNames(bool generate) { + Options.GenerateDebugNames = generate; + } + + /// Set prepend path for clang modules. + void setPrependPath(const std::string &Ppath) { Options.PrependPath = Ppath; } + + /// Set warning handler which would be used to report warnings. + void setWarningHandler(messageHandler Handler) { + Options.WarningHandler = Handler; + } + + /// Set error handler which would be used to report errors. + void setErrorHandler(messageHandler Handler) { + Options.ErrorHandler = Handler; + } + + /// Set object files loader which would be used to load + /// additional objects for splitted dwarf. + void setObjFileLoader(objFileLoader Loader) { + Options.ObjFileLoader = Loader; + } + + /// Set map for Swift interfaces. + void setSwiftInterfacesMap(swiftInterfacesMap *Map) { + Options.ParseableSwiftInterfaces = Map; + } + + /// Set prefix map for objects. + void setObjectPrefixMap(objectPrefixMap *Map) { + Options.ObjectPrefixMap = Map; + } + + /// @} + +protected: + /// \defgroup Methods used to link the debug information + /// + /// @{ + + /// Returns next available unique Compile Unit ID. + unsigned getNextUniqueUnitID() { return UniqueUnitID.fetch_add(1); } + + /// Returns true if DWARFLinker is in multy-thread mode. + bool IsMT() { return Options.Threads != 1; } + + /// Returns true if all compile units for specified \p Context were cloned. + /// If there are unprocessed inter-CU references after all compile units + /// are done then we need to do additional pass to handle these inter-CU + /// references. + bool IsClonningComplete(LinkContext &Context) const; + + /// Link single object file from specified \p Context. + void linkObjectFile(LinkContext &Context); + + /// Generate linked output for single Compile Unit \p CU from the specified \p + /// Context. + void processSingleCompileUnit(LinkContext &Context, CompileUnit &CU); + + /// Copy invariant debug sections from the specified \p Dwarf context into + /// the output \p Emitter. + void copyInvariantDebugSection(DWARFContext &Dwarf, DwarfEmitter &Emitter); + + /// Emit warnings as Dwarf compile units to leave a trail after linking. + void emitPaperTrailWarnings(CompileUnit &CU, DwarfEmitter &Emitter); + + /// Resolve the DIE attribute reference that has been extracted in \p + /// RefValue. The resulting DIE might be in another CompileUnit which is + /// stored into \p RefCU. + /// \returns null if resolving fails for any reason. + DWARFDie resolveDIEReference(const LinkContext &Context, CompileUnit &U, + const DWARFFormValue &RefValue, + const DWARFDie &DIE, CompileUnit *&RefCU); + /// returns true if we need to translate strings. - bool needToTranslateStrings() { return StringsTranslator != nullptr; } + bool needToTranslateStrings() { return NeedToTranslateStrings; } + /// Reports Warning. void reportWarning(const Twine &Warning, const DWARFFile &File, const DWARFDie *DIE = nullptr) const { if (Options.WarningHandler != nullptr) Options.WarningHandler(Warning, File.FileName, DIE); } + /// Reports Warning. + void reportWarning(const Twine &Warning, StringRef FileName, + const DWARFDie *DIE = nullptr) const { + if (Options.WarningHandler != nullptr) + Options.WarningHandler(Warning, FileName, DIE); + } + + /// Reports Error. void reportError(const Twine &Warning, const DWARFFile &File, const DWARFDie *DIE = nullptr) const { if (Options.ErrorHandler != nullptr) Options.ErrorHandler(Warning, File.FileName, DIE); } - /// Remembers the oldest and newest DWARF version we've seen in a unit. - void updateDwarfVersion(unsigned Version) { - MaxDwarfVersion = std::max(MaxDwarfVersion, Version); - MinDwarfVersion = std::min(MinDwarfVersion, Version); + /// Reports Error. + void reportError(const Twine &Warning, StringRef FileName, + const DWARFDie *DIE = nullptr) const { + if (Options.ErrorHandler != nullptr) + Options.ErrorHandler(Warning, FileName, DIE); } - /// Remembers the kinds of accelerator tables we've seen in a unit. - void updateAccelKind(DWARFContext &Dwarf); + /// @} - /// Emit warnings as Dwarf compile units to leave a trail after linking. - bool emitPaperTrailWarnings(const DWARFFile &File, - OffsetsStringPool &StringPool); + /// \defgroup Methods used to search for Clang modules. + /// + /// @{ + /// + /// If this compile unit is really a skeleton CU that points to a + /// clang module, register it in ClangModules and return true. + /// + /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name + /// pointing to the module, and a DW_AT_gnu_dwo_id with the module + /// hash. + bool registerModuleReference(LinkContext &Context, DWARFUnit &CU, + const DWARFFile &File, unsigned Indent = 0, + bool Quiet = false); + + /// Recursively add the debug info in this clang module .pcm + /// file (and all the modules imported by it in a bottom-up fashion) + /// to link context's modules compile units list. + Error loadClangModule(LinkContext &Context, DWARFUnit &CU, StringRef FilePath, + StringRef ModuleName, uint64_t DwoId, + const DWARFFile &File, unsigned Indent = 0, + bool Quiet = false); - void copyInvariantDebugSection(DWARFContext &Dwarf); + /// @} - /// Keeps track of data associated with one object during linking. - struct LinkContext { - DWARFFile &File; - UnitListTy CompileUnits; - bool Skip = false; + /// \defgroup Methods used to analyzing of type declaration contexts. + /// + /// @{ - LinkContext(DWARFFile &File) : File(File) {} + /// Non-recursive helper to build the global DeclContext information and + /// gather the child->parent relationships in the original compile unit. + /// + /// This function uses the same work list approach as lookForDIEsToKeep. + /// + /// \return true when this DIE and all of its children are only + /// forward declarations to types defined in external clang modules + /// (i.e., forward declarations that are children of a DW_TAG_module). + bool analyzeContextInfo(const LinkContext &Context, const DWARFDie &DIE, + unsigned ParentIdx, CompileUnit &CU, + swiftInterfacesMap *ParseableSwiftInterfaces, + bool &HasUnresolvedRefs); + void analyzeImportedModule(const DWARFDie &DIE, CompileUnit &CU, + swiftInterfacesMap *ParseableSwiftInterfaces); + + /// Create DeclarationContext specified by \p Key ans set it into \p Info. + void setDeclContext(const DeclContextKey *Key, CompileUnit::DIEInfo *Info); + + /// Set Declaration Contexts(accummulated through analyzing stage) from the \p + /// CU to the corresponding DIEInfo structures. + void assignDelayedDeclContexts(CompileUnit &CU); - /// Clear part of the context that's no longer needed when we're done with - /// the debug object. - void clear() { - CompileUnits.clear(); - File.Addresses->clear(); - } - }; + /// @} - /// Called before emitting object data - void cleanupAuxiliarryData(LinkContext &Context); + /// \defgroup Methods used for analyzing liveness information. + /// + /// Recursively walk the \p DIE tree and look for DIEs to + /// keep. Store that information in \p CU's DIEInfo. + /// + /// @{ /// Look at the parent of the given DIE and decide whether they should be /// kept. @@ -446,107 +781,49 @@ /// Look at DIEs referenced by the given DIE and decide whether they should be /// kept. All DIEs referenced though attributes should be kept. - void lookForRefDIEsToKeep(const DWARFDie &Die, CompileUnit &CU, - unsigned Flags, const UnitListTy &Units, - const DWARFFile &File, + bool lookForRefDIEsToKeep(const DWARFDie &Die, CompileUnit &CU, + unsigned Flags, const LinkContext &Context, SmallVectorImpl &Worklist); - /// \defgroup FindRootDIEs Find DIEs corresponding to Address map entries. - /// - /// @{ - /// Recursively walk the \p DIE tree and look for DIEs to - /// keep. Store that information in \p CU's DIEInfo. - /// - /// The return value indicates whether the DIE is incomplete. - void lookForDIEsToKeep(AddressesMap &RelocMgr, RangesTy &Ranges, - const UnitListTy &Units, const DWARFDie &DIE, - const DWARFFile &File, CompileUnit &CU, - unsigned Flags); - - /// If this compile unit is really a skeleton CU that points to a - /// clang module, register it in ClangModules and return true. - /// - /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name - /// pointing to the module, and a DW_AT_gnu_dwo_id with the module - /// hash. - bool registerModuleReference(DWARFDie CUDie, const DWARFUnit &Unit, - const DWARFFile &File, - OffsetsStringPool &OffsetsStringPool, - DeclContextTree &ODRContexts, - uint64_t ModulesEndOffset, unsigned &UnitID, - bool IsLittleEndian, unsigned Indent = 0, - bool Quiet = false); + /// Returns false if there were encountered unresolved Die references. + bool lookForDIEsToKeep(const LinkContext &Context, const DWARFDie &DIE, + CompileUnit &CU, unsigned Flags); - /// 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(DWARFDie CUDie, StringRef FilePath, - StringRef ModuleName, uint64_t DwoId, - const DWARFFile &File, - OffsetsStringPool &OffsetsStringPool, - DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, - unsigned &UnitID, bool IsLittleEndian, - unsigned Indent = 0, bool Quiet = false); - - /// Mark the passed DIE as well as all the ones it depends on as kept. - void keepDIEAndDependencies(AddressesMap &RelocMgr, RangesTy &Ranges, - const UnitListTy &Units, const DWARFDie &DIE, - CompileUnit::DIEInfo &MyInfo, - const DWARFFile &File, CompileUnit &CU, - bool UseODR); - - unsigned shouldKeepDIE(AddressesMap &RelocMgr, RangesTy &Ranges, - const DWARFDie &DIE, const DWARFFile &File, - CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, - unsigned Flags); + unsigned shouldKeepDIE(const DWARFDie &DIE, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, unsigned Flags); /// Check if a variable describing DIE should be kept. /// \returns updated TraversalFlags. - unsigned shouldKeepVariableDIE(AddressesMap &RelocMgr, const DWARFDie &DIE, + unsigned shouldKeepVariableDIE(const DWARFDie &DIE, CompileUnit &CU, CompileUnit::DIEInfo &MyInfo, unsigned Flags); - unsigned shouldKeepSubprogramDIE(AddressesMap &RelocMgr, RangesTy &Ranges, - const DWARFDie &DIE, const DWARFFile &File, - CompileUnit &Unit, + unsigned shouldKeepSubprogramDIE(const DWARFDie &DIE, CompileUnit &CU, CompileUnit::DIEInfo &MyInfo, unsigned Flags); - /// Resolve the DIE attribute reference that has been extracted in \p - /// RefValue. The resulting DIE might be in another CompileUnit which is - /// stored into \p ReferencedCU. \returns null if resolving fails for any - /// reason. - DWARFDie resolveDIEReference(const DWARFFile &File, const UnitListTy &Units, - const DWARFFormValue &RefValue, - const DWARFDie &DIE, CompileUnit *&RefCU); - /// @} - /// \defgroup Methods used to link the debug information + /// \defgroup Methods used to clone debug information. + /// + /// DIECloner is a helper to clone single CU. /// /// @{ - - struct DWARFLinkerOptions; - + /// class DIECloner { + const LinkContext &Context; DWARFLinker &Linker; - DwarfEmitter *Emitter; - DWARFFile &ObjFile; - - /// Allocator used for all the DIEValue objects. - BumpPtrAllocator &DIEAlloc; - - std::vector> &CompileUnits; + CompileUnit &CU; - bool Update; + const DWARFLinkerOptions &Options; public: - DIECloner(DWARFLinker &Linker, DwarfEmitter *Emitter, DWARFFile &ObjFile, - BumpPtrAllocator &DIEAlloc, - std::vector> &CompileUnits, - bool Update) - : Linker(Linker), Emitter(Emitter), ObjFile(ObjFile), - DIEAlloc(DIEAlloc), CompileUnits(CompileUnits), Update(Update) {} + DIECloner(CompileUnit &CU, const LinkContext &Context, DWARFLinker &Linker, + raw_pwrite_stream &Out, const DWARFLinkerOptions &Options); + ~DIECloner(); + void cloneCU(); + + private: /// Recursively clone \p InputDIE into an tree of DIE objects /// where useless (as decided by lookForDIEsToKeep()) bits have been /// stripped out and addresses have been rewritten according to the @@ -558,257 +835,203 @@ /// 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, const DWARFFile &File, - CompileUnit &U, OffsetsStringPool &StringPool, - int64_t PCOffset, uint32_t OutOffset, unsigned Flags, - bool IsLittleEndian, 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. - uint64_t cloneAllCompileUnits(DWARFContext &DwarfContext, - const DWARFFile &File, - OffsetsStringPool &StringPool, - bool IsLittleEndian); - - private: - using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; - - /// Information gathered and exchanged between the various - /// clone*Attributes helpers about the attributes of a particular DIE. - struct AttributesInfo { - /// Names. - DwarfStringPoolEntryRef Name, MangledName, NameWithoutTemplate; - - /// Offsets in the string pool. - uint32_t NameOffset = 0; - uint32_t MangledNameOffset = 0; - - /// Value of AT_low_pc in the input DIE - uint64_t OrigLowPc = std::numeric_limits::max(); - - /// Value of AT_high_pc in the input DIE - uint64_t OrigHighPc = 0; - - /// Value of DW_AT_call_return_pc in the input DIE - uint64_t OrigCallReturnPc = 0; - - /// Value of DW_AT_call_pc in the input DIE - uint64_t OrigCallPc = 0; - - /// Offset to apply to PC addresses inside a function. - int64_t PCOffset = 0; - - /// Does the DIE have a low_pc attribute? - bool HasLowPc = false; - - /// Does the DIE have a ranges attribute? - bool HasRanges = false; - - /// Is this DIE only a declaration? - bool IsDeclaration = false; - - AttributesInfo() = default; - }; + DIE *cloneDIE(const DWARFDie &InputDIE, int64_t PCOffset, + uint32_t OutOffset, unsigned Flags, DIE *Die = nullptr); /// Helper for cloneDIE. unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, - const DWARFFile &File, CompileUnit &U, - OffsetsStringPool &StringPool, const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &AttrInfo, bool IsLittleEndian); + AttributesInfo &AttrInfo, uint64_t AttrOutOffset); - /// Clone a string attribute described by \p AttrSpec and add - /// it to \p Die. + /// Clone a string attribute described by \p AttrSpec and add it to \p Die. /// \returns the size of the new attribute. - unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, const DWARFUnit &U, - OffsetsStringPool &StringPool, + unsigned cloneStringAttribute(DIE &Die, const DWARFDie &InputDIE, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, AttributesInfo &Info); - /// Clone an attribute referencing another DIE and add - /// it to \p Die. + /// Clone an attribute referencing another DIE and add it to \p Die. /// \returns the size of the new attribute. - unsigned cloneDieReferenceAttribute(DIE &Die, const DWARFDie &InputDIE, - AttributeSpec AttrSpec, + unsigned cloneDieReferenceAttribute(DIE &Die, AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val, - const DWARFFile &File, - CompileUnit &Unit); + AttributesInfo &Info, + uint64_t AttrOutOffset); /// Clone a DWARF expression that may be referencing another DIE. void cloneExpression(DataExtractor &Data, DWARFExpression Expression, - const DWARFFile &File, CompileUnit &Unit, SmallVectorImpl &OutputBuffer); - /// Clone an attribute referencing another DIE and add - /// it to \p Die. + /// Clone an attribute referencing another DIE and add it to \p Die. /// \returns the size of the new attribute. - unsigned cloneBlockAttribute(DIE &Die, const DWARFFile &File, - CompileUnit &Unit, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize, - bool IsLittleEndian); + unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize); - /// Clone an attribute referencing another DIE and add - /// it to \p Die. + /// Clone an attribute referencing another DIE and add it to \p Die. /// \returns the size of the new attribute. unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, - const CompileUnit &Unit, AttributesInfo &Info); /// Clone a scalar attribute and add it to \p Die. /// \returns the size of the new attribute. unsigned cloneScalarAttribute(DIE &Die, const DWARFDie &InputDIE, - const DWARFFile &File, CompileUnit &U, AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, - AttributesInfo &Info); + AttributesInfo &Info, uint64_t AttrOutOffset); /// Get the potential name and mangled name for the entity - /// described by \p Die and store them in \Info if they are not + /// described by \p Die and store them in \p Info if they are not /// already there. /// \returns is a name was found. bool getDIENames(const DWARFDie &Die, AttributesInfo &Info, - OffsetsStringPool &StringPool, bool StripTemplate = false); - - /// Create a copy of abbreviation Abbrev. - void copyAbbrev(const DWARFAbbreviationDeclaration &Abbrev, bool hasODR); + bool StripTemplate = false); uint32_t hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, const DWARFFile &File, int RecurseDepth = 0); /// Helper for cloneDIE. - void addObjCAccelerator(CompileUnit &Unit, const DIE *Die, - DwarfStringPoolEntryRef Name, - OffsetsStringPool &StringPool, bool SkipPubSection); - }; + void addObjCAccelerator(CompileUnit &Unit, const DIE *Die, StringRef Name, + bool SkipPubSection); + + /// Emits all debug info corresponding to the cloned compile unit. + std::unique_ptr Emitter; - /// Assign an abbreviation number to \p Abbrev - void assignAbbrev(DIEAbbrev &Abbrev); + /// DIELoc objects that need to be destructed (but not freed!). + std::vector DIELocs; + + /// DIEBlock objects that need to be destructed (but not freed!). + std::vector DIEBlocks; + + /// Allocator used for all the DIEValue objects. + BumpPtrAllocator DIEAlloc; + }; /// Compute and emit debug_ranges section for \p Unit, and /// patch the attributes referencing it. - void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf, - const DWARFFile &File) const; - - /// Generate and emit the DW_AT_ranges attribute for a compile_unit if it had - /// one. - void generateUnitRanges(CompileUnit &Unit) const; + void patchRangesForUnit(CompileUnit &Unit, DwarfEmitter &Emitter) const; /// 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, - const DWARFFile &File); - - /// Emit the accelerator entries for \p Unit. - void emitAcceleratorEntriesForUnit(CompileUnit &Unit); - void emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit); - void emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit); + void patchLineTableForUnit(CompileUnit &CU, DwarfEmitter &Emitter); /// Patch the frame info for an object file and emit it. - void patchFrameInfoForObject(const DWARFFile &, RangesTy &Ranges, - DWARFContext &, unsigned AddressSize); + void patchFrameInfoForObject(LinkContext &Context, RangesTy &Ranges, + unsigned AddressSize, DwarfEmitter &Emitter); - /// FoldingSet that uniques the abbreviations. - FoldingSet AbbreviationsSet; + /// Store specified string \p String into the global string + /// pool(TheOffsetsStringPool). Assign the string offset to the + /// specified \p It Die Value. + void setStringsOffset(DIE::value_iterator It, const char *String); - /// Storage for the unique Abbreviations. - /// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot be - /// changed to a vector of unique_ptrs. - std::vector> Abbreviations; + /// Store strings accumulated for \p CU at clonning stage into the global + /// string pool (TheOffsetsStringPool). Assign offsets to the + /// corresponding Die Values. + void assignDelayedOffsets(CompileUnit &CU); - /// DIELoc objects that need to be destructed (but not freed!). - std::vector DIELocs; - - /// DIEBlock objects that need to be destructed (but not freed!). - std::vector DIEBlocks; - - /// Allocator used for all the DIEValue objects. - BumpPtrAllocator DIEAlloc; /// @} - DwarfEmitter *TheDwarfEmitter; - std::vector ObjectContexts; + /// \defgroup Methods used to assemble final file from cloned + /// compile units. + /// + /// @{ - unsigned MaxDwarfVersion = 0; - unsigned MinDwarfVersion = std::numeric_limits::max(); + /// Assemble all CompileUnits into the single output DWARF File. + void glueCompileUnitsAndWriteToTheOutput(); - bool AtLeastOneAppleAccelTable = false; - bool AtLeastOneDwarfAccelTable = false; + /// Assign offsets for Compile Units sections. + void assignOffsets(StringMap &); + void + assignOffsets(LinkContext &Context, + std::array &TableOffsets); + void + assignOffsets(CompileUnit &CU, + std::array &TableOffsets); + + /// Patch Offsets and Size fields. + void patchOffsetsAndSizes(); + void patchOffsetsAndSizes(LinkContext &Context); + void patchOffsetsAndSizes(CompileUnit &CU); + void patchCompileUnit(CompileUnit &CU, StringRef Data); + void patchAranges(CompileUnit &CU, StringRef Data); + void patchFrames(LinkContext &Context, StringRef Data); + + /// Write data to the resulting output. + void writeDWARFToTheOutput(); + void writeDWARFToTheOutput(CompileUnit &CU, bool &HasAbbreviations); + void writeDWARFToTheOutput(LinkContext &CU); + + /// Print statistic for processed Debug Info. + void printStatistic(const StringMap &SizeByObject); + + /// Build Accelerator tables. + void buildAcceleratorTables(); + void buildAcceleratorTables(CompileUnit &CU); + + /// Increase Object File reference counter. Archive files might contain + /// several object files. Thus, we count number of object files inside + /// an archive, so that we could safely unload archive if all it`s object + /// files are already processed. + void addBinHolderNameRef(DWARFFile &File); + + /// Decrease Object file reference counter and unload Object File + /// if there are no more referencies. + void unloadObjectFile(DWARFFile &File); + /// @} - /// 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; + /// \defgroup Members accessed asinchroniously. + /// + /// @{ - /// Offset of the last CIE that has been emitted in the output - /// debug_frame section. - uint32_t LastCIEOffset = 0; - - /// Apple accelerator tables. - AccelTable DebugNames; - AccelTable AppleNames; - AccelTable AppleNamespaces; - AccelTable AppleObjc; - AccelTable AppleTypes; + /// Unique ID for compile unit. + std::atomic UniqueUnitID; /// Mapping the PCM filename to the DwoId. StringMap ClangModules; + std::mutex ClangModulesMutex; - DwarfLinkerClient DwarfLinkerClientID; - - std::function StringsTranslator = nullptr; - - /// linking options - struct DWARFLinkerOptions { - /// Generate processing log to the standard output. - bool Verbose = false; - - /// Print statistics. - bool Statistics = false; - - /// Skip emitting output - bool NoOutput = false; - - /// Do not unique types according to ODR - bool NoODR = false; + /// BinaryHolderRefCounter counts whether references to the binary + /// files exist. + DenseMap BinaryHolderRefCounter; + std::mutex BinaryHolderRefMutex; - /// Update - bool Update = false; + /// The Dwarf string pool is used for emission. + OffsetsStringPool TheOffsetsStringPool; + std::mutex StringPoolMutex; - /// Number of threads. - unsigned Threads = 1; + /// ODR Contexts for the data deduplication. + DeclContextTree ODRContexts; + std::mutex ContextsMutex; - /// The accelerator table kind - AccelTableKind TheAccelTableKind = AccelTableKind::Default; + // no need semaphore, since it is immutable: + std::vector> ObjectContexts; - /// Prepend path for the clang modules. - std::string PrependPath; + bool NeedToTranslateStrings = false; - // warning handler - messageHandler WarningHandler = nullptr; + DwarfLinkerClient DwarfLinkerClientID; - // error handler - messageHandler ErrorHandler = nullptr; + /// @} - objFileLoader ObjFileLoader = nullptr; + /// \defgroup Members accessed sequentially. + /// + /// @{ - /// A list of all .swiftinterface files referenced by the debug - /// info, mapping Module name to path on disk. The entries need to - /// be uniqued and sorted and there are only few entries expected - /// per compile unit, which is why this is a std::map. - /// this is dsymutil specific fag. - swiftInterfacesMap *ParseableSwiftInterfaces = nullptr; + /// Apple accelerator tables. + AccelTable DebugNames; + AccelTable AppleNames; + AccelTable AppleNamespaces; + AccelTable AppleObjc; + AccelTable AppleTypes; - /// A list of remappings to apply to file paths. - objectPrefixMap *ObjectPrefixMap = nullptr; - } Options; + DwarfEmitter *TheDwarfEmitter; + /// @} }; +} // end namespace dwarflinker + } // end namespace llvm -#endif // LLVM_DWARFLINKER_DWARFLINKER_H +#endif // LLVM_DWARFLINKERNEXT_DWARFLINKER_H diff --git a/llvm/include/llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h b/llvm/include/llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h @@ -0,0 +1,682 @@ +//===- DWARFLinkerCompileUnit.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DWARFLINKERNEXT_DWARFLINKERCOMPILEUNIT_H +#define LLVM_DWARFLINKERNEXT_DWARFLINKERCOMPILEUNIT_H + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include +#include + +namespace llvm { + +namespace dwarflinker { + +class DeclContext; +class DWARFFile; +class DeclContextTree; + +template +using HalfOpenIntervalMap = + IntervalMap::LeafSize, + IntervalMapHalfOpenInfo>; + +using FunctionIntervals = HalfOpenIntervalMap; + +/// DeclContextKey keeps all meaningful fields for DeclContext. +/// It is used to memorize context fields for types used in current compile +/// unit. +class DeclContextKey { +public: + DeclContextKey() {} + DeclContextKey(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, + StringRef Name, StringRef File, + unsigned ParentQualifiedNameHash, bool IsInvalid, + bool IsDefinedInClangModule) + : Hash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), Name(Name), + File(File), ParentQualifiedNameHash(ParentQualifiedNameHash), + IsInvalid(IsInvalid), IsDefinedInClangModule(IsDefinedInClangModule) {} + + unsigned Hash = 0; + uint32_t Line = 0; + uint32_t ByteSize = 0; + uint16_t Tag = 0; + StringRef Name; + StringRef File; + unsigned ParentQualifiedNameHash = 0; + unsigned IsInvalid : 1; + unsigned IsDefinedInClangModule : 1; +}; + +/// Info type for the DenseMap storing the DeclContextKey pointers. +struct DeclKeyMapInfo : private DenseMapInfo { + using DenseMapInfo::getEmptyKey; + using DenseMapInfo::getTombstoneKey; + + static unsigned getHashValue(const DeclContextKey *Ctxt) { + return Ctxt->Hash; + } + + static bool isEqual(const DeclContextKey *LHS, const DeclContextKey *RHS) { + if (RHS == getEmptyKey() || RHS == getTombstoneKey()) + return RHS == LHS; + return LHS->Hash == RHS->Hash && + LHS->ParentQualifiedNameHash == RHS->ParentQualifiedNameHash && + LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && + LHS->Name == RHS->Name && LHS->File == RHS->File; + } +}; + +/// Stores all information relating to a compile unit, be it in its original +/// instance in the object file to its brand new cloned and generated DIE tree. +class CompileUnit { +public: + /// The stages of new compile unit processing. + enum class Stages { + Created, + SrcDWARFLoaded, + ModulesLoaded, + TypesDeclarationsFound, + LivenessAnalysisDone, + Cloned, + }; + + /// The debug tables containing pieces or related + /// information for the current compilation unit. + enum class DebugTableKind : uint8_t { + DebugInfo = 0, + DebugLine = 1, + DebugFrame = 2, + DebugRange = 3, + DebugLoc = 4, + DebugARanges = 5, + DebugAbbrev = 6, + }; + const static size_t TableKindsNum = 7; + + /// Information gathered about a DIE in the object file. + struct DIEInfo { + DIEInfo() { + Keep = false; + InDebugMap = false; + Prune = false; + IncompleteDeclaration = false; + } + DIEInfo(const DIEInfo &Info) { + AddrAdjust = Info.AddrAdjust; + Ctxt.store(Info.Ctxt); + ClonedDieOffset = Info.ClonedDieOffset; + ParentIdx = Info.ParentIdx; + Keep = Info.Keep; + InDebugMap = Info.InDebugMap; + Prune = Info.Prune; + IncompleteDeclaration = Info.IncompleteDeclaration; + } + /// Address offset to apply to the described entity. + int64_t AddrAdjust = 0; + + /// ODR Declaration context. + std::atomic Ctxt = {nullptr}; + + /// Offset to cloned version of that DIE. + uint64_t ClonedDieOffset = 0; + + /// The index of this DIE's parent. + uint32_t ParentIdx = 0; + + /// Is the DIE part of the linked output? + bool Keep : 1; + + /// Was this DIE's entity found in the map? + bool InDebugMap : 1; + + /// Is this a pure forward declaration we can strip? + bool Prune : 1; + + /// Does DIE transitively refer an incomplete decl? + bool IncompleteDeclaration : 1; + }; + + /// This structure keeps offset to DW_AT_range attribute and + /// value which should be updated when final offsets would be known. + struct RangeAttributePatchInfo { + RangeAttributePatchInfo(uint64_t CULocalOffsetValue, uint64_t OffsetToAttr) + : CULocalOffsetValue(CULocalOffsetValue), OffsetToAttr(OffsetToAttr) {} + uint64_t CULocalOffsetValue; + uint64_t OffsetToAttr; + }; + + /// This structure keeps offset to DW_AT_location attribute and + /// value which should be updated when final offsets would be known. + struct LocationAttributePatchInfo { + LocationAttributePatchInfo(uint64_t OffsetToAttr, + uint64_t CULocalOffsetValue, int64_t PCValue) + : OffsetToAttr(OffsetToAttr), CULocalOffsetValue(CULocalOffsetValue), + PCValue(PCValue) {} + uint64_t OffsetToAttr; + uint64_t CULocalOffsetValue; + int64_t PCValue; + }; + + /// This structure keeps offset to Die reference attribute and + /// its target(CU/Die index) which should be updated when final offsets would + /// be known. + struct DieRefAttrPatchInfo { + DieRefAttrPatchInfo(uint64_t RefDieIdx, const CompileUnit *RefCU, + uint64_t OffsetToAttr, bool IsLocal) + : RefDieIdx(RefDieIdx), RefCU(RefCU), OffsetToAttr(OffsetToAttr), + IsCULocal(IsLocal) {} + uint64_t RefDieIdx = 0; + const CompileUnit *RefCU = nullptr; + uint64_t OffsetToAttr = 0; + bool IsCULocal = false; + }; + + /// This structure keeps pointer to string and pointer to die string + /// attribute. That string should be added to the global string pool later and + /// it`s offset should be assigned to the die string attribute. + struct OffsetsData { + OffsetsData(DIE::value_iterator AttrToPatch, const char *String) + : AttrToPatch(AttrToPatch), String(String) {} + DIE::value_iterator AttrToPatch; + const char *String; + }; + + /// This structure keeps pointer to type declaration context key + /// and pointer to DIE info. The type declaration context key would be used + /// later for adding type declaration into global DeclContextTree and created + /// context declaration would be assigned to the DIE info. + struct DeclContextKeyData { + DeclContextKeyData(const DeclContextKey *Key, CompileUnit::DIEInfo *Info) + : Key(Key), Info(Info) {} + const DeclContextKey *Key = nullptr; + CompileUnit::DIEInfo *Info = nullptr; + }; + + /// This structure keeps fields which would be used for creating accelerator + /// table. + struct AccelInfo { + /// Name of the entry. + DwarfStringPoolEntryRef Name; + + /// Tag of the DIE this entry describes. + dwarf::Tag Tag; + + /// Output offset of the DIE this entry describes. + uint64_t OutOffset; + + /// Hash of the fully qualified name. + uint32_t QualifiedNameHash; + + /// Emit this entry only in the apple_* sections. + bool SkipPubSection; + + /// Is this an ObjC class implementation? + bool ObjcClassImplementation; + + AccelInfo(DwarfStringPoolEntryRef Name, dwarf::Tag Tag, uint64_t OutOffset, + bool SkipPubSection = false) + : Name(Name), Tag(Tag), OutOffset(OutOffset), + SkipPubSection(SkipPubSection) {} + + AccelInfo(DwarfStringPoolEntryRef Name, dwarf::Tag Tag, uint64_t OutOffset, + uint32_t QualifiedNameHash, bool ObjCClassIsImplementation) + : Name(Name), Tag(Tag), OutOffset(OutOffset), + QualifiedNameHash(QualifiedNameHash), SkipPubSection(false), + ObjcClassImplementation(ObjCClassIsImplementation) {} + }; + + CompileUnit(unsigned ID, bool, StringRef ClangModuleName, DWARFFile &File); + + CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, + StringRef ClangModuleName, DWARFFile &File); + + /// Marks compile units which does not have a pair in the source debug info. + bool isArtificialCU() const { return OrigUnit == None; } + + /// Endiannes for the compile unit. + support::endianness getEndianness() const { return Endianess; } + + /// Returns DWARF version. + uint16_t getVersion() const { return Version; } + + /// Returns size of compile unit info. + uint16_t getHeaderSize() const { return Version >= 5 ? 12 : 11; } + + /// Returns stage of overall processing. + Stages getStage() const { return Stage; } + + /// Set stage of overall processing. + void setStage(Stages Stage) { this->Stage = Stage; } + + /// Returns true if there are references to this compile unit + /// from other comile units. + bool hasExternalReferences() const { + std::unique_lock Guard(ExternalReferencesMutex); + + return !ExternalReferences.empty(); + } + + /// Recognises the table name and match it with the DebugTableKind. + static Optional parseDebugTableName(StringRef Name); + + /// Returns paired compile unit from input DWARF. + DWARFUnit &getOrigUnit() const { return **OrigUnit; } + + /// Unique id. + unsigned getUniqueID() const { return ID; } + + /// Create a Die for this compile unit. + void createOutputDIE() { + NewUnit.emplace(getOrigUnit().getUnitDIE().getTag()); + } + + /// Returns Die for this compile unit. + DIE *getOutputUnitDIE() const { + if (NewUnit) + return &const_cast(*NewUnit).getUnitDie(); + return nullptr; + } + + /// Returns true id ODR deduplication should be done for this compile unit. + bool hasODR() const { return HasODR; } + + /// Returns true if this compile unit is from Clang module. + bool isClangModule() const { return !ClangModuleName.empty(); } + + uint16_t getLanguage(); + /// Return the DW_AT_LLVM_sysroot of the compile unit or an empty StringRef. + StringRef getSysRoot(); + + /// Returns Clang module name; + const std::string &getClangModuleName() const { return ClangModuleName; } + + /// Group of functions returning DIE info. + /// + /// \p Idx index of the DIE + /// \returns DIEInfo descriptor. + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } + + /// \p Idx index of the DIE + /// \returns DIEInfo descriptor. + const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } + + /// \p Die + /// \returns DIEInfo descriptor. + DIEInfo &getInfo(const DWARFDie &Die) { + unsigned Idx = getOrigUnit().getDIEIndex(Die); + return Info[Idx]; + } + + /// \p Die + /// \returns DIEInfo descriptor. + const DIEInfo &getInfo(const DWARFDie &Die) const { + unsigned Idx = getOrigUnit().getDIEIndex(Die); + return Info[Idx]; + } + + /// Put reference from other CU into the list of external references. + void addLiveReference(uint64_t OrigOffset) { + std::unique_lock Guard(ExternalReferencesMutex); + + ExternalReferences.insert(OrigOffset); + } + + /// Returns set of references to dies of this compile unit + /// from other compile units. + DenseSet getLiveReferences() { + std::unique_lock Guard(ExternalReferencesMutex); + + return std::move(ExternalReferences); + } + + /// Returns offset to the start of specified \p TableKind debug table, + /// corresponding to this compile unit. + uint64_t getStartOffset(DebugTableKind TableKind) const { + return TableOffsets[static_cast< + typename std::underlying_type::type>(TableKind)]; + } + + /// Set offset to the start of specified \p TableKind debug table, + /// corresponding to this compile unit. + void setStartOffset(DebugTableKind TableKind, uint64_t Offset) { + TableOffsets[static_cast< + typename std::underlying_type::type>(TableKind)] = + Offset; + } + + /// Set size of this(newly generated) compile unit. + void setUnitSize(uint64_t UnitSize) { this->UnitSize = UnitSize; } + + /// Returns size of this(newly generated) compile unit. + uint64_t getUnitSize() const { return UnitSize; } + + /// Returns offset to the DW_AT_stmt_list attribute. + Optional getOffsetToStmtList() const { return OffsetToStmtList; } + + /// Set offset to the DW_AT_stmt_list attribute. + void setOffsetToStmtList(uint64_t Offset) { OffsetToStmtList = Offset; } + + /// Returns value of DW_AT_low_pc attribute. + uint64_t getLowPc() const { return LowPc; } + + /// Returns value of DW_AT_high_pc attribute. + uint64_t getHighPc() const { return HighPc; } + + /// Returns true if there is a label corresponding to the specified \p Addr. + bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } + + /// Returns patch information for unit`s DW_AT_range attribute. + Optional getUnitRangesAttribute() const { + return UnitRangeAttribute; + } + + /// Returns function ranges of this compile unit. + const FunctionIntervals &getFunctionRanges() const { return Ranges; } + + /// Returns list of patches for DW_AT_range attributes of this + /// compile units dies. + std::vector &getRangesAttributes() { + return RangeAttributes; + } + + /// Returns list of patches for DW_AT_location attributes of this + /// compile units dies. + std::vector &getLocationAttributes() { + return LocationAttributes; + } + + /// Mark every DIE in this unit as kept. This function also + /// marks variables as InDebugMap so that they appear in the + /// reconstructed accelerator tables. + void markEverythingAsKept(); + + /// Keep track of a reference to DIE info \p RefDieIdx in \p RefUnit by \p + /// Attr. The attribute should be fixed up later to point to the absolute + /// offset of \p Die in the debug_info section or to the canonical offset of + /// \p Ctxt if it is non-null. + void noteDieReference(CompileUnit::DieRefAttrPatchInfo Data); + + /// Returns list of patches for reference attributes of dies of this compile + /// unit. + std::vector getDiesReferences(); + + /// Do not clear Input dies after the compile unit is cloned. + void setKeepInputDies() { KeepInputDies = true; } + + /// Returns true if Input dies should not be cleared after the compile unit is + /// cloned. + bool needToKeepInputDies() { return KeepInputDies; } + + /// Add the low_pc of a label that is relocated by applying + /// offset \p PCOffset. + void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset); + + /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying + /// offset \p PCOffset. + void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + + /// Keep track of a DW_AT_range attribute that we will need to patch up later. + void noteRangeAttribute(const DIE &Die, RangeAttributePatchInfo AttrOffsets); + + /// Keep track of a location attribute pointing to a location list in the + /// debug_loc section. + void noteLocationAttribute(uint64_t AttrOffset, uint64_t Offset, + int64_t PcOffset); + + /// Add a name accelerator entry for \a Die with \a Name. + void addNamespaceAccelerator(const DIE *Die, StringRef Name); + + /// Add a name accelerator entry for \a Die with \a Name. + void addNameAccelerator(const DIE *Die, StringRef Name, + bool SkipPubnamesSection = false); + + /// Add various accelerator entries for \p Die with \p Name which is stored + /// in the string table at \p Offset. \p Name must be an Objective-C + /// selector. + void addObjCAccelerator(const DIE *Die, StringRef Name, + bool SkipPubnamesSection = false); + + /// Add a type accelerator entry for \p Die with \p Name which is stored in + /// the string table at \p Offset. + void addTypeAccelerator(const DIE *Die, StringRef Name, + bool ObjcClassImplementation, + uint32_t QualifiedNameHash); + + /// Returns stored info for creating apple_names/debug_pubnames accelerator + /// table. + const std::vector &getPubnames() const { return Pubnames; } + + /// Returns stored info for creating apple_types/debug_pubtypes accelerator + /// table. + const std::vector &getPubtypes() const { return Pubtypes; } + + /// Returns stored info for creating apple_namespaces accelerator table. + const std::vector &getNamespaces() const { return Namespaces; } + + /// Returns stored info for creating apple_objc accelerator table. + const std::vector &getObjC() const { return ObjC; } + + /// Initialize state of compile unit in the start of overall unit processing. + void initIteration(); + + /// Cleanup unneeded resources after compile unit is cloned. + void cleanupDataAfterClonning(); + + /// Cleanup unneeded resources after accelerator tables are built. + void cleanupAcceleratorTables(); + + MCSymbol *getLabelBegin() { return LabelBegin; } + void setLabelBegin(MCSymbol *S) { LabelBegin = S; } + + /// Remember type declaration fields, to create declaration context later. + void noteContextData(const DeclContextKey *Key, CompileUnit::DIEInfo *Info) { + DeclContextData.emplace_back(Key, Info); + } + + /// Get stored declaration contexts fields. + std::vector getDeclContextData() { + return std::move(DeclContextData); + } + + /// Remember pointer to string and die string attribute. Specified + /// \p String would added to the global string pool later and + /// \p AttrToPatch would be updated accordingly. + void noteOffsetData(DIE::value_iterator AttrToPatch, const char *String) { + StringOffsetsData.emplace_back(AttrToPatch, String); + } + + /// Get stored strings and attributes to update. + std::vector getStringsOffsetData() { + return std::move(StringOffsetsData); + } + + /// Adds \p Abbrev into unit`s abbreviation table. + void assignAbbrev(DIEAbbrev &Abbrev); + + /// Returns name of the file for the \p FileIdx + /// from the unit`s line table. + Optional getFileName(unsigned FileIdx); + + /// Store DeclContextKey body into the Keys container. + DeclContextKey *insertKey(DeclContextKey &Key) { + DenseSet::iterator It = Keys.find(&Key); + if (It == Keys.end()) { + DeclContextKey *NewKey = new (Allocator) + DeclContextKey(Key.Hash, Key.Line, Key.ByteSize, Key.Tag, Key.Name, + Key.File, Key.ParentQualifiedNameHash, Key.IsInvalid, + Key.IsDefinedInClangModule); + + It = Keys.insert(NewKey).first; + } + + return *It; + }; + + /// Returns DWARFFile containing this compile unit. + const DWARFFile &getContaingFile() const { return ContaingFile; } + + StringRef internString(StringRef S) { return Strings.internString(S); } + + /// Retuirns abbreviations for this compile unit. + const std::vector> &getAbbreviations() { + return Abbreviations; + } + + /// Returns generated elf file keeping debug tables for this compile unit. + SmallString<0> &getDwarfBits() { return OutDebugInfoBytes; } + + /// Erases generated elf file keeping debug tables for this compile unit. + void eraseDwarfBits() { OutDebugInfoBytes = SmallString<0>(); } + +private: + /// \defgroup Members accessed sequentially. + /// + /// @{ + + /// Pointer to the paired compile unit from the input DWARF. + Optional OrigUnit; + + /// Unique ID for the compile unit. + unsigned ID = 0; + + /// DWARF version. + uint16_t Version = 0; + + /// DIE info indexed by DIE index. + std::vector Info; + + /// DIE for newly generated compile unit. + Optional NewUnit; + MCSymbol *LabelBegin = nullptr; + + uint64_t LowPc = std::numeric_limits::max(); + uint64_t HighPc = 0; + + FunctionIntervals::Allocator RangeAlloc; + + /// The ranges in that interval map are the PC ranges for + /// functions in this unit, associated with the PC offset to apply + /// to the addresses to get the linked address. + FunctionIntervals Ranges; + + /// The DW_AT_low_pc of each DW_TAG_label. + SmallDenseMap Labels; + + /// DW_AT_ranges attributes to patch after we have gathered + /// all the unit's function addresses. + /// @{ + std::vector RangeAttributes; + Optional UnitRangeAttribute; + /// @} + + /// Location attributes that need to be transferred from the + /// original debug_loc section to the linked one. They are stored + /// along with the PC offset that is to be applied to their + /// function's address. + std::vector LocationAttributes; + + /// Accelerator entries for the unit, both for the pub* + /// sections and the apple* ones. + /// @{ + std::vector Pubnames; + std::vector Pubtypes; + std::vector Namespaces; + std::vector ObjC; + /// @} + + /// Is this unit subject to the ODR rule? + bool HasODR = false; + + /// The DW_AT_language of this unit. + uint16_t Language = 0; + + /// The DW_AT_LLVM_sysroot of this unit. + std::string SysRoot; + + /// If this is a Clang module, this holds the module's name. + std::string ClangModuleName; + + /// FoldingSet that uniques the abbreviations. + FoldingSet AbbreviationsSet; + + /// Storage for the unique Abbreviations. + /// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot be + /// changed to a vector of unique_ptrs. + std::vector> Abbreviations; + + /// String pool. + NonRelocatableStringpool Strings; + + /// Cached resolved paths from the line table. + /// The key is . + using ResolvedPathsMap = DenseMap; + ResolvedPathsMap ResolvedFullPaths; + StringMap ResolvedParentPaths; + + /// Data for delayed creation of Declaration contexts. + std::vector DeclContextData; + + /// Data for delayed creation of String pool entries. + std::vector StringOffsetsData; + + /// Bodies for DeclContextKeys. + DenseSet Keys; + BumpPtrAllocator Allocator; + + /// Offset to DW_AT_stmt_list. + Optional OffsetToStmtList; + + /// DWARFFile containing this compile unit. + DWARFFile &ContaingFile; + + /// Offsets to the debug tables corresponding to this compile unit. + std::array TableOffsets = {0}; + uint64_t UnitSize = 0; + + /// Elf file containg generated debug tables for this compile unit. + SmallString<0> OutDebugInfoBytes; + + /// Endiannes for this compile unit. + support::endianness Endianess = support::endianness::little; + + /// A list of attributes to fixup with the absolute offset of + /// a DIE in the debug_info section. + /// + /// The offsets for the attributes in this array couldn't be set while + /// cloning because for references the target DIE's offset + /// isn't known when you emit the reference attribute. + std::vector DIEReferences; + /// @} + + /// \defgroup Members accessed asinchroniously. + /// + /// @{ + /// This flag tells whether input dies could be cleared after + /// compile unit is fully processed. + std::atomic KeepInputDies = {false}; + + /// Offsets to the Dies referenced from the other compile units. + DenseSet ExternalReferences; + mutable std::mutex ExternalReferencesMutex; + + /// This field keeps current stage of overall compile unit processing. + std::atomic Stage; + /// @} +}; + +} // end of namespace dwarflinker + +} // end namespace llvm + +#endif // LLVM_DWARFLINKERNEXT_DWARFLINKERCOMPILEUNIT_H diff --git a/llvm/include/llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h b/llvm/include/llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h @@ -0,0 +1,186 @@ +//===- DWARFLinkerDeclContext.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DWARFLINKERNEXT_DWARFLINKERDECLCONTEXT_H +#define LLVM_DWARFLINKERNEXT_DWARFLINKERDECLCONTEXT_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +namespace llvm { + +namespace dwarflinker { + +struct DeclMapInfo; +struct LinkContext; + +/// A DeclContext is a named program scope that is used for ODR uniquing of +/// types. +/// +/// The set of DeclContext for the ODR-subject parts of a Dwarf link is +/// expanded (and uniqued) with each new object file processed. We need to +/// determine the context of each DIE in an linked object file to see if the +/// corresponding type has already been emitted. +/// +/// The contexts are conceptually organized as a tree (eg. a function scope is +/// contained in a namespace scope that contains other scopes), but +/// storing/accessing them in an actual tree is too inefficient: we need to be +/// able to very quickly query a context for a given child context by name. +/// Storing a StringMap in each DeclContext would be too space inefficient. +/// +/// The solution here is to give each DeclContext a link to its parent (this +/// allows to walk up the tree), but to query the existence of a specific +/// DeclContext using a separate DenseMap keyed on the hash of the fully +/// qualified name of the context. +class DeclContext { +public: + using Map = DenseSet; + + DeclContext() : DefinedInClangModule(0) {} + + DeclContext(DeclContextKey Key) + : QualifiedNameHash(Key.Hash), + ParentQualifiedNameHash(Key.ParentQualifiedNameHash), Line(Key.Line), + ByteSize(Key.ByteSize), Tag(Key.Tag), Name(Key.Name), File(Key.File), + DefinedInClangModule(0) {} + + DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, + StringRef Name, StringRef File, unsigned ParentQualifiedNameHash) + : QualifiedNameHash(Hash), + ParentQualifiedNameHash(ParentQualifiedNameHash), Line(Line), + ByteSize(ByteSize), Tag(Tag), Name(Name), File(File), + DefinedInClangModule(0) {} + + DeclContextKey getKey() { + return DeclContextKey(QualifiedNameHash, Line, ByteSize, Tag, Name, File, + ParentQualifiedNameHash, true, DefinedInClangModule); + } + + uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } + + bool setCanonicalInfo(uint64_t DieIndex, CompileUnit *CU) { + if (!HasCanonicalDie) { + std::unique_lock Guard(CanonicalInfoMutex); + + if (!HasCanonicalDie) { + assert(CU); + CanonicalDIEIndex = DieIndex; + CanonicalCU = CU; + HasCanonicalDie = true; + return true; + } + } + + return false; + } + + bool hasCanonicalDie() const { return HasCanonicalDie; } + uint32_t getCanonicalDIEIdx() const { return CanonicalDIEIndex; } + CompileUnit *getCanonicalCU() const { return CanonicalCU; } + + bool isDefinedInClangModule() const { return DefinedInClangModule; } + void setDefinedInClangModule(bool Val) { DefinedInClangModule = Val; } + + uint16_t getTag() const { return Tag; } + +private: + friend DeclMapInfo; + + /// \defgroup Members accessed sequentially. + /// + /// @{ + unsigned QualifiedNameHash = 0; + unsigned ParentQualifiedNameHash = 0; + uint32_t Line = 0; + uint32_t ByteSize = 0; + uint16_t Tag = dwarf::DW_TAG_compile_unit; + StringRef Name; + StringRef File; + /// @} + + /// \defgroup Members accessed asinchroniously. + /// + /// @{ + std::atomic DefinedInClangModule = {false}; + std::atomic HasCanonicalDie = {false}; + uint32_t CanonicalDIEIndex = 0; + CompileUnit *CanonicalCU = nullptr; + std::mutex CanonicalInfoMutex; + /// @} +}; + +/// This class gives a tree-like API to the DenseMap that stores the +/// DeclContext objects. It holds the BumpPtrAllocator where these objects will +/// be allocated. +class DeclContextTree { +public: + DeclContextKey *getChildDeclContextKey(const LinkContext &Context, + DeclContextKey *ContextKey, + const DWARFDie &DIE, CompileUnit &Unit, + bool InClangModule, + bool &IsUnresolved); + + /// Get the child of \a Context described by \a DIE in \a Unit. The + /// required strings will be interned in \a StringPool. + /// \returns The child DeclContext along with one bit that is set if + /// this context is invalid. + /// + /// An invalid context means it shouldn't be considered for uniquing, but its + /// not returning null, because some children of that context might be + /// uniquing candidates. + DeclContext *getChildDeclContext(const DeclContextKey &ContextKey); + + DeclContextKey &getRootKey() { return RootKey; } + + void clear() { + Contexts.clear(); + Allocator.Reset(); + StringPool.clear(); + } + +private: + BumpPtrAllocator Allocator; + DeclContextKey RootKey; + DeclContext::Map Contexts; + + /// String pool keeping type name and file name bodies. + NonRelocatableStringpool StringPool; +}; + +/// Info type for the DenseMap storing the DeclContext pointers. +struct DeclMapInfo : private DenseMapInfo { + using DenseMapInfo::getEmptyKey; + using DenseMapInfo::getTombstoneKey; + + static unsigned getHashValue(const DeclContext *Ctxt) { + return Ctxt->QualifiedNameHash; + } + + static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) { + if (RHS == getEmptyKey() || RHS == getTombstoneKey()) + return RHS == LHS; + return LHS->QualifiedNameHash == RHS->QualifiedNameHash && + LHS->ParentQualifiedNameHash == RHS->ParentQualifiedNameHash && + LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && + LHS->Name == RHS->Name && LHS->File == RHS->File; + } +}; + +} // end of namespace dwarflinker + +} // end namespace llvm + +#endif // LLVM_DWARFLINKERNEXT_DWARFLINKERDECLCONTEXT_H diff --git a/llvm/include/llvm/DWARFLinkerNext/DWARFStreamer.h b/llvm/include/llvm/DWARFLinkerNext/DWARFStreamer.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DWARFLinkerNext/DWARFStreamer.h @@ -0,0 +1,225 @@ +//===- DwarfStreamer.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DWARFLINKERNEXT_DWARFSTREAMER_H +#define LLVM_DWARFLINKERNEXT_DWARFSTREAMER_H + +#include "llvm/CodeGen/AccelTable.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/DWARFLinkerNext/DWARFLinker.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +/// User of DwarfStreamer should call initialization code +/// for AsmPrinter: +/// +/// InitializeAllTargetInfos(); +/// InitializeAllTargetMCs(); +/// InitializeAllTargets(); +/// InitializeAllAsmPrinters(); + +class MCCodeEmitter; + +namespace dwarflinker { + +enum class OutputFileType { + Object, + Assembly, +}; + +/// The Dwarf streaming logic. +/// +/// All interactions with the MC layer that is used to build the debug +/// information binary representation are handled in this class. +class DwarfStreamer : public DwarfEmitter { +public: + DwarfStreamer(OutputFileType OutFileType, raw_pwrite_stream &OutFile, + std::function Translator, + messageHandler Error, messageHandler Warning) + : OutFile(OutFile), OutFileType(OutFileType), Translator(Translator), + ErrorHandler(Error), WarningHandler(Warning) {} + + bool init(Triple TheTriple); + const Triple &getTargetTriple() const override { + return MOFI->getTargetTriple(); + } + + /// Dump the file to the disk. + void finish(); + + AsmPrinter &getAsmPrinter() const { return *Asm; } + + /// Set the current output section to debug_info and change + /// the MC Dwarf version to \p DwarfVersion. + void switchToDebugInfoSection(unsigned DwarfVersion); + + /// Emit the compilation unit header for \p Unit in the + /// debug_info section. + /// + /// As a side effect, this also switches the current Dwarf version + /// of the MC layer to the one of U.getOrigUnit(). + void emitCompileUnitHeader(CompileUnit &Unit) override; + + /// Recursively emit the DIE tree rooted at \p Die. + void emitDIE(DIE &Die) override; + + /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. + void emitAbbrevs(const std::vector> &Abbrevs, + unsigned DwarfVersion) override; + + /// Emit DIE containing warnings. + void emitPaperTrailWarningsDie(DIE &Die) override; + + /// Emit contents of section SecName From Obj. + void emitSectionContents(StringRef SecData, StringRef SecName) override; + + /// Emit the string table described by \p Pool. + void emitStrings(const NonRelocatableStringpool &Pool) override; + + /// Emit the swift_ast section stored in \p Buffer. + void emitSwiftAST(StringRef Buffer); + + /// Emit debug_ranges for \p FuncRange by translating the + /// original \p Entries. + void emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector &Entries, + unsigned AddressSize) override; + + /// Emit debug_aranges entries for \p Unit and if \p DoRangesSection is true, + /// also emit the debug_ranges entries for the DW_TAG_compile_unit's + /// DW_AT_ranges attribute. + void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection) override; + + uint64_t getRangesSectionSize() const override { return RangesSectionSize; } + + /// Emit the debug_loc contribution for \p Unit by copying the entries from + /// \p Dwarf and offsetting them. Update the location attributes to point to + /// the new entries. + void emitLocationsForUnit( + CompileUnit &Unit, DWARFContext &Dwarf, + std::function &)> ProcessExpr) + override; + + /// Emit the line table described in \p Rows into the debug_line section. + void emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, unsigned MinInstLength, + std::vector &Rows, + unsigned AdddressSize) override; + + /// Copy the debug_line over to the updated binary while unobfuscating the + /// file names and directories. + void translateLineTable(DataExtractor LineData, uint64_t Offset) override; + + uint64_t getLineSectionSize() const override { return LineSectionSize; } + + /// Emit the .debug_pubnames contribution for \p Unit. + void emitPubNamesForUnit(const CompileUnit &Unit) override; + + /// Emit the .debug_pubtypes contribution for \p Unit. + void emitPubTypesForUnit(const CompileUnit &Unit) override; + + /// Emit a CIE. + void emitCIE(StringRef CIEBytes) override; + + /// Emit an FDE with data \p Bytes. + void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes) override; + + /// Emit DWARF debug names. + void emitDebugNames(AccelTable &Table) override; + + /// Emit Apple namespaces accelerator table. + void emitAppleNamespaces( + AccelTable &Table) override; + + /// Emit Apple names accelerator table. + void + emitAppleNames(AccelTable &Table) override; + + /// Emit Apple Objective-C accelerator table. + void + emitAppleObjc(AccelTable &Table) override; + + /// Emit Apple type accelerator table. + void + emitAppleTypes(AccelTable &Table) override; + + uint64_t getFrameSectionSize() const override { return FrameSectionSize; } + + uint64_t getDebugInfoSectionSize() const override { + return DebugInfoSectionSize; + } + +private: + inline void error(const Twine &Error, StringRef Context = "") { + if (ErrorHandler) + ErrorHandler(Error, Context, nullptr); + } + + inline void warn(const Twine &Warning, StringRef Context = "") { + if (WarningHandler) + WarningHandler(Warning, Context, nullptr); + } + + /// \defgroup MCObjects MC layer objects constructed by the streamer + /// @{ + std::unique_ptr MRI; + std::unique_ptr MAI; + std::unique_ptr MOFI; + std::unique_ptr MC; + MCAsmBackend *MAB; // Owned by MCStreamer + std::unique_ptr MII; + std::unique_ptr MSTI; + MCInstPrinter *MIP; // Owned by AsmPrinter + MCCodeEmitter *MCE; // Owned by MCStreamer + MCStreamer *MS; // Owned by AsmPrinter + std::unique_ptr TM; + std::unique_ptr Asm; + /// @} + + /// The output file we stream the linked Dwarf to. + raw_pwrite_stream &OutFile; + OutputFileType OutFileType = OutputFileType::Object; + std::function Translator; + + uint64_t RangesSectionSize = 0; + uint64_t LocSectionSize = 0; + uint64_t LineSectionSize = 0; + uint64_t FrameSectionSize = 0; + uint64_t DebugInfoSectionSize = 0; + + /// Keep track of emitted CUs and their Unique ID. + struct EmittedUnit { + unsigned ID; + MCSymbol *LabelBegin; + }; + std::vector EmittedUnits; + + /// Emit the pubnames or pubtypes section contribution for \p + /// Unit into \p Sec. The data is provided in \p Names. + void emitPubSectionForUnit(MCSection *Sec, StringRef Name, + const CompileUnit &Unit, + const std::vector &Names); + + messageHandler ErrorHandler = nullptr; + messageHandler WarningHandler = nullptr; +}; + +} // end of namespace dwarflinker + +} // end namespace llvm + +#endif // LLVM_DWARFLINKERNEXT_DWARFSTREAMER_H diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt --- a/llvm/lib/CMakeLists.txt +++ b/llvm/lib/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(Bitcode) add_subdirectory(Bitstream) add_subdirectory(DWARFLinker) +add_subdirectory(DWARFLinkerNext) add_subdirectory(Extensions) add_subdirectory(Frontend) add_subdirectory(Transforms) diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp --- a/llvm/lib/DWARFLinker/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -69,13 +69,14 @@ CompileUnit *&RefCU) { assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); uint64_t RefOffset = *RefValue.getAsReference(); - if ((RefCU = getUnitForOffset(Units, RefOffset))) + if ((RefCU = getUnitForOffset(Units, RefOffset))) { if (const auto RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) { // In a file with broken references, an attribute might point to a NULL // DIE. if (!RefDie.isNULL()) return RefDie; } + } reportWarning("could not find referenced DIE", File, &DIE); return DWARFDie(); @@ -1655,7 +1656,8 @@ patchStmtList(*OutputDIE, DIEInteger(TheDwarfEmitter->getLineSectionSize())); - RangesTy &Ranges = File.Addresses->getValidAddressRanges(); + RangesTy *Ranges; + File.Addresses->getValidAddressRanges(Ranges); // Parse the original line info for the unit. DWARFDebugLine::LineTable LineTable; @@ -1718,11 +1720,11 @@ // for now do as dsymutil. // FIXME: Understand exactly what cases this addresses and // potentially remove it along with the Ranges map. - auto Range = Ranges.lower_bound(Row.Address.Address); - if (Range != Ranges.begin() && Range != Ranges.end()) + auto Range = Ranges->lower_bound(Row.Address.Address); + if (Range != Ranges->begin() && Range != Ranges->end()) --Range; - if (Range != Ranges.end() && Range->first <= Row.Address.Address && + if (Range != Ranges->end() && Range->first <= Row.Address.Address && Range->second.HighPC >= Row.Address.Address) { StopAddress = Row.Address.Address + Range->second.Offset; } @@ -2495,9 +2497,10 @@ CurrentUnit->markEverythingAsKept(); copyInvariantDebugSection(*OptContext.File.Dwarf); } else { + RangesTy *Ranges; + OptContext.File.Addresses->getValidAddressRanges(Ranges); for (auto &CurrentUnit : OptContext.CompileUnits) - lookForDIEsToKeep(*OptContext.File.Addresses, - OptContext.File.Addresses->getValidAddressRanges(), + lookForDIEsToKeep(*OptContext.File.Addresses, *Ranges, OptContext.CompileUnits, CurrentUnit->getOrigUnit().getUnitDIE(), OptContext.File, *CurrentUnit, 0); @@ -2518,11 +2521,14 @@ OptContext.File.Dwarf->isLittleEndian()); } if (!Options.NoOutput && !OptContext.CompileUnits.empty() && - LLVM_LIKELY(!Options.Update)) + LLVM_LIKELY(!Options.Update)) { + RangesTy *Ranges; + OptContext.File.Addresses->getValidAddressRanges(Ranges); + patchFrameInfoForObject( - OptContext.File, OptContext.File.Addresses->getValidAddressRanges(), - *OptContext.File.Dwarf, + OptContext.File, *Ranges, *OptContext.File.Dwarf, OptContext.CompileUnits[0]->getOrigUnit().getAddressByteSize()); + } // Clean-up before starting working on the next object. cleanupAuxiliarryData(OptContext); diff --git a/llvm/lib/DWARFLinkerNext/CMakeLists.txt b/llvm/lib/DWARFLinkerNext/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerNext/CMakeLists.txt @@ -0,0 +1,21 @@ +add_llvm_component_library(LLVMDWARFLinkerNext + DWARFLinkerCompileUnit.cpp + DWARFLinkerDeclContext.cpp + DWARFLinker.cpp + DWARFStreamer.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/DWARFLinkerNext + + DEPENDS + intrinsics_gen + + LINK_COMPONENTS + BinaryFormat + DebugInfoDWARF + AsmPrinter + CodeGen + MC + Object + Support + ) diff --git a/llvm/lib/DWARFLinkerNext/DWARFLinker.cpp b/llvm/lib/DWARFLinkerNext/DWARFLinker.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerNext/DWARFLinker.cpp @@ -0,0 +1,3230 @@ +//=== DWARFLinker.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DWARFLinkerNext/DWARFLinker.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h" +#include "llvm/DWARFLinkerNext/DWARFStreamer.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ThreadPool.h" +#include + +namespace llvm { + +namespace dwarflinker { + +/// Compute the total size of the debug info. +static uint64_t getDebugInfoSize(DWARFContext &Dwarf) { + uint64_t Size = 0; + for (auto &Unit : Dwarf.compile_units()) { + Size += Unit->getLength(); + } + return Size; +} + +/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our +/// CompileUnit object instead. +CompileUnit *LinkContext::getUnitForOffset(CompileUnit &CurrentCU, + uint64_t Offset) const { + if (CurrentCU.isClangModule()) + return &CurrentCU; + + auto CU = llvm::upper_bound( + CompileUnits, Offset, + [](uint64_t LHS, const std::unique_ptr &RHS) { + return LHS < RHS->getOrigUnit().getNextUnitOffset(); + }); + + return CU != CompileUnits.end() ? CU->get() : nullptr; +} + +DWARFLinker::DWARFLinker(DwarfEmitter *Emitter, DwarfLinkerClient ClientID, + std::function StringsTranslator) + : UniqueUnitID(0), TheOffsetsStringPool(StringsTranslator, true), + NeedToTranslateStrings(StringsTranslator != nullptr), + DwarfLinkerClientID(ClientID), TheDwarfEmitter(Emitter) {} + +DWARFDie DWARFLinker::resolveDIEReference(const LinkContext &Context, + CompileUnit &CU, + const DWARFFormValue &RefValue, + const DWARFDie &DIE, + CompileUnit *&RefCU) { + assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); + uint64_t RefOffset = *RefValue.getAsReference(); + + if ((RefCU = Context.getUnitForOffset(CU, RefOffset))) + if (const auto RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) { + // In a file with broken references, an attribute might point to a NULL + // DIE. + if (!RefDie.isNULL()) + return RefDie; + } + + reportWarning("could not find referenced DIE", CU.getContaingFile(), &DIE); + return DWARFDie(); +} + +/// \returns whether the passed \a Attr type might contain a DIE reference +/// suitable for ODR uniquing. +static bool isODRAttribute(uint16_t Attr) { + switch (Attr) { + default: + return false; + case dwarf::DW_AT_type: + case dwarf::DW_AT_containing_type: + case dwarf::DW_AT_specification: + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_import: + return true; + } + llvm_unreachable("Improper attribute."); +} + +static bool isTypeTag(uint16_t Tag) { + switch (Tag) { + case dwarf::DW_TAG_array_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_pointer_type: + case dwarf::DW_TAG_reference_type: + case dwarf::DW_TAG_string_type: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_ptr_to_member_type: + case dwarf::DW_TAG_set_type: + case dwarf::DW_TAG_subrange_type: + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_file_type: + case dwarf::DW_TAG_namelist: + case dwarf::DW_TAG_packed_type: + case dwarf::DW_TAG_volatile_type: + case dwarf::DW_TAG_restrict_type: + case dwarf::DW_TAG_atomic_type: + case dwarf::DW_TAG_interface_type: + case dwarf::DW_TAG_unspecified_type: + case dwarf::DW_TAG_shared_type: + return true; + default: + break; + } + return false; +} + +AddressesMap::~AddressesMap() {} + +DwarfEmitter::~DwarfEmitter() {} + +static Optional StripTemplateParameters(StringRef Name) { + // We are looking for template parameters to strip from Name. e.g. + // + // operator< + // + // We look for > at the end but if it does not contain any < then we + // have something like operator>>. We check for the operator<=> case. + if (!Name.endswith(">") || Name.count("<") == 0 || Name.endswith("<=>")) + return {}; + + // How many < until we have the start of the template parameters. + size_t NumLeftAnglesToSkip = 1; + + // If we have operator<=> then we need to skip its < as well. + NumLeftAnglesToSkip += Name.count("<=>"); + + size_t RightAngleCount = Name.count('>'); + size_t LeftAngleCount = Name.count('<'); + + // If we have more < than > we have operator< or operator<< + // we to account for their < as well. + if (LeftAngleCount > RightAngleCount) + NumLeftAnglesToSkip += LeftAngleCount - RightAngleCount; + + size_t StartOfTemplate = 0; + while (NumLeftAnglesToSkip--) + StartOfTemplate = Name.find('<', StartOfTemplate) + 1; + + return Name.substr(0, StartOfTemplate - 1); +} + +bool DWARFLinker::DIECloner::getDIENames(const DWARFDie &Die, + AttributesInfo &Info, + bool StripTemplate) { + // This function will be called on DIEs having low_pcs and + // ranges. As getting the name might be more expansive, filter out + // blocks directly. + if (Die.getTag() == dwarf::DW_TAG_lexical_block) + return false; + + if (Info.MangledName.empty()) + Info.MangledName = Die.getLinkageName(); + + if (Info.Name.empty()) + Info.Name = Die.getShortName(); + + if (Info.MangledName.empty()) + Info.MangledName = Info.Name; + + if (StripTemplate && !Info.Name.empty() && Info.MangledName != Info.Name) { + // StringRef Name = Info.Name.getString(); + if (Optional StrippedName = StripTemplateParameters(Info.Name)) + Info.NameWithoutTemplate = *StrippedName; + } + + return !Info.Name.empty() || !Info.MangledName.empty(); +} + +/// Resolve the relative path to a build artifact referenced by DWARF by +/// applying DW_AT_comp_dir. +static void resolveRelativeObjectPath(SmallVectorImpl &Buf, DWARFDie CU) { + sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), "")); +} + +/// Collect references to parseable Swift interfaces in imported +/// DW_TAG_module blocks. +void DWARFLinker::analyzeImportedModule( + const DWARFDie &DIE, CompileUnit &CU, + swiftInterfacesMap *ParseableSwiftInterfaces) { + if (CU.getLanguage() != dwarf::DW_LANG_Swift) + return; + + if (!ParseableSwiftInterfaces) + return; + + StringRef Path = dwarf::toStringRef(DIE.find(dwarf::DW_AT_LLVM_include_path)); + if (!Path.endswith(".swiftinterface")) + return; + // Don't track interfaces that are part of the SDK. + StringRef SysRoot = dwarf::toStringRef(DIE.find(dwarf::DW_AT_LLVM_sysroot)); + if (SysRoot.empty()) + SysRoot = CU.getSysRoot(); + if (!SysRoot.empty() && Path.startswith(SysRoot)) + return; + if (Optional Val = DIE.find(dwarf::DW_AT_name)) + if (Optional Name = Val->getAsCString()) { + auto &Entry = (*ParseableSwiftInterfaces)[*Name]; + // The prepend path is applied later when copying. + DWARFDie CUDie = CU.getOrigUnit().getUnitDIE(); + SmallString<128> ResolvedPath; + if (sys::path::is_relative(Path)) + resolveRelativeObjectPath(ResolvedPath, CUDie); + sys::path::append(ResolvedPath, Path); + if (!Entry.empty() && Entry != ResolvedPath && Options.WarningHandler) + Options.WarningHandler( + Twine("Conflicting parseable interfaces for Swift Module ") + + *Name + ": " + Entry + " and " + Path, + CU.getContaingFile().FileName, &DIE); + Entry = std::string(ResolvedPath.str()); + } +} + +/// The distinct types of work performed by the work loop in +/// analyzeContextInfo. +enum class ContextWorklistItemType : uint8_t { + AnalyzeContextInfo, + UpdateChildPruning, + UpdatePruning, +}; + +/// This class represents an item in the work list. The type defines what kind +/// of work needs to be performed when processing the current item. Everything +/// but the Type and Die fields are optional based on the type. +struct ContextWorklistItem { + DWARFDie Die; + unsigned ParentIdx; + union { + CompileUnit::DIEInfo *OtherInfo; + DeclContextKey *ContextKey; + }; + ContextWorklistItemType Type; + bool InImportedModule; + + ContextWorklistItem(DWARFDie Die, ContextWorklistItemType T, + CompileUnit::DIEInfo *OtherInfo = nullptr) + : Die(Die), ParentIdx(0), OtherInfo(OtherInfo), Type(T), + InImportedModule(false) {} + + ContextWorklistItem(DWARFDie Die, DeclContextKey *ContextKeyPtr, + unsigned ParentIdx, bool InImportedModule) + : Die(Die), ParentIdx(ParentIdx), ContextKey(ContextKeyPtr), + Type(ContextWorklistItemType::AnalyzeContextInfo), + InImportedModule(InImportedModule) {} +}; + +static bool updatePruning(const DWARFDie &Die, CompileUnit &CU) { + CompileUnit::DIEInfo &Info = CU.getInfo(Die); + + // Prune this DIE if it is either a forward declaration inside a + // DW_TAG_module or a DW_TAG_module that contains nothing but + // forward declarations. + Info.Prune &= (Die.getTag() == dwarf::DW_TAG_module) || + (isTypeTag(Die.getTag()) && + dwarf::toUnsigned(Die.find(dwarf::DW_AT_declaration), 0)); + + // Only prune forward declarations inside a DW_TAG_module for which a + // definition exists elsewhere. + Info.Prune &= + Info.Ctxt && Info.Ctxt.load()->hasCanonicalDie() && + Info.Ctxt.load()->getCanonicalCU()->getUniqueID() != CU.getUniqueID(); + + return Info.Prune; +} + +static void updateChildPruning(const DWARFDie &Die, CompileUnit &CU, + CompileUnit::DIEInfo &ChildInfo) { + CompileUnit::DIEInfo &Info = CU.getInfo(Die); + Info.Prune &= ChildInfo.Prune; +} + +/// Recursive helper to build the global DeclContext information and +/// gather the child->parent relationships in the original compile unit. +/// +/// This function uses the same work list approach as lookForDIEsToKeep. +/// +/// \return true when this DIE and all of its children are only +/// forward declarations to types defined in external clang modules +/// (i.e., forward declarations that are children of a DW_TAG_module). +bool DWARFLinker::analyzeContextInfo( + const LinkContext &Context, const DWARFDie &DIE, unsigned ParentIdx, + CompileUnit &CU, swiftInterfacesMap *ParseableSwiftInterfaces, + bool &HasUnresolvedRefs) { + // LIFO work list. + std::vector Worklist; + DeclContextKey *RootKey = CU.insertKey(ODRContexts.getRootKey()); + Worklist.emplace_back(DIE, RootKey, ParentIdx, CU.isClangModule()); + + while (!Worklist.empty()) { + ContextWorklistItem Current = Worklist.back(); + Worklist.pop_back(); + + switch (Current.Type) { + case ContextWorklistItemType::UpdatePruning: + updatePruning(Current.Die, CU); + continue; + case ContextWorklistItemType::UpdateChildPruning: + updateChildPruning(Current.Die, CU, *Current.OtherInfo); + continue; + case ContextWorklistItemType::AnalyzeContextInfo: + break; + } + + unsigned Idx = CU.getOrigUnit().getDIEIndex(Current.Die); + CompileUnit::DIEInfo &Info = CU.getInfo(Idx); + + // Clang imposes an ODR on modules(!) regardless of the language: + // "The module-id should consist of only a single identifier, + // which provides the name of the module being defined. Each + // module shall have a single definition." + // + // This does not extend to the types inside the modules: + // "[I]n C, this implies that if two structs are defined in + // different submodules with the same name, those two types are + // distinct types (but may be compatible types if their + // definitions match)." + // + // We treat non-C++ modules like namespaces for this reason. + if (Current.Die.getTag() == dwarf::DW_TAG_module && + Current.ParentIdx == 0 && + dwarf::toString(Current.Die.find(dwarf::DW_AT_name), "") != + CU.getClangModuleName()) { + Current.InImportedModule = true; + analyzeImportedModule(Current.Die, CU, ParseableSwiftInterfaces); + } + + Info.ParentIdx = Current.ParentIdx; + bool InClangModule = CU.isClangModule() || Current.InImportedModule; + Info.Ctxt = nullptr; + if (CU.hasODR() || InClangModule) { + if (Current.ContextKey) { + + bool IsUnresolved = false; + DeclContextKey *NewContextKey = ODRContexts.getChildDeclContextKey( + Context, Current.ContextKey, Current.Die, CU, InClangModule, + IsUnresolved); + + if (IsUnresolved) { + // Current.Die references other compile unit, which is not loaded yet. + // Stop type declaration analyzing for now. + HasUnresolvedRefs = true; + return false; + } + + if (NewContextKey) { + Current.ContextKey = NewContextKey; + + if (IsMT()) { + // In multy-threaded case it is in-effective to lock&use + // common resource ODRContexts.getChildDeclContext() for + // every type declaration context. Instead, we remember + // DeclarationContextKey and create DeclarationContext and + // update Info.Ctxt field later. That way we lock + // ODRContexts.getChildDeclContext() only once for each compile + // unit. + + CU.noteContextData(Current.ContextKey, &Info); + } else { + setDeclContext(Current.ContextKey, &Info); + } + } + } + } + Info.Prune = Current.InImportedModule; + // Add children in reverse order to the worklist to effectively process + // them in order. + Worklist.emplace_back(Current.Die, ContextWorklistItemType::UpdatePruning); + for (DWARFDie Child = Current.Die.getLastChild(); Child.isValid(); + Child = Child.getPreviousSibling()) { + CompileUnit::DIEInfo &ChildInfo = CU.getInfo(Child); + Worklist.emplace_back( + Current.Die, ContextWorklistItemType::UpdateChildPruning, &ChildInfo); + Worklist.emplace_back(Child, Current.ContextKey, Idx, + Current.InImportedModule); + } + } + + return CU.getInfo(DIE).Prune; +} + +static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { + switch (Tag) { + default: + return false; + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_common_block: + case dwarf::DW_TAG_lexical_block: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_union_type: + return true; + } + llvm_unreachable("Invalid Tag"); +} + +/// Check if a variable describing DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DWARFLinker::shouldKeepVariableDIE(const DWARFDie &DIE, + CompileUnit &CU, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + // Global variables with constant value can always be kept. + if (!(Flags & TF_InFunctionScope) && + Abbrev->findAttributeIndex(dwarf::DW_AT_const_value)) { + MyInfo.InDebugMap = true; + return Flags | TF_Keep; + } + + // See if there is a relocation to a valid debug map entry inside + // this variable's location. The order is important here. We want to + // always check if the variable has a valid relocation, so that the + // DIEInfo is filled. However, we don't want a static variable in a + // function to force us to keep the enclosing function. + if (!CU.getContaingFile().Addresses->hasLiveMemoryLocation(DIE, MyInfo) || + (Flags & TF_InFunctionScope)) + return Flags; + + if (Options.Verbose) { + outs() << "Keeping variable DIE:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + DIE.dump(outs(), 8 /* Indent */, DumpOpts); + } + + return Flags | TF_Keep; +} + +/// Check if a function describing DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DWARFLinker::shouldKeepSubprogramDIE(const DWARFDie &DIE, + CompileUnit &CU, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { + Flags |= TF_InFunctionScope; + + auto LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); + if (!LowPc) + return Flags; + + assert(LowPc.hasValue() && "low_pc attribute is not an address."); + if (!CU.getContaingFile().Addresses->hasLiveAddressRange(DIE, MyInfo)) + return Flags; + + if (Options.Verbose) { + outs() << "Keeping subprogram DIE:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + DIE.dump(outs(), 8 /* Indent */, DumpOpts); + } + + if (DIE.getTag() == dwarf::DW_TAG_label) { + if (CU.hasLabelAt(*LowPc)) + return Flags; + + DWARFUnit &OrigUnit = CU.getOrigUnit(); + // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider labels + // that don't fall into the CU's aranges. This is wrong IMO. Debug info + // generation bugs aside, this is really wrong in the case of labels, where + // a label marking the end of a function will have a PC == CU's high_pc. + if (dwarf::toAddress(OrigUnit.getUnitDIE().find(dwarf::DW_AT_high_pc)) + .getValueOr(UINT64_MAX) <= LowPc) + return Flags; + CU.addLabelLowPc(*LowPc, MyInfo.AddrAdjust); + return Flags | TF_Keep; + } + + Flags |= TF_Keep; + + Optional HighPc = DIE.getHighPC(*LowPc); + if (!HighPc) { + reportWarning("Function without high_pc. Range will be discarded.\n", + CU.getContaingFile(), &DIE); + return Flags; + } + + // Replace the debug map range with a more accurate one. + dwarflinker::RangesTy *Ranges; + CU.getContaingFile().Addresses->getValidAddressRanges(Ranges); + (*Ranges)[*LowPc] = ObjFileAddressRange(*HighPc, MyInfo.AddrAdjust); + CU.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); + return Flags; +} + +/// Check if a DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DWARFLinker::shouldKeepDIE(const DWARFDie &DIE, CompileUnit &CU, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { + switch (DIE.getTag()) { + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_variable: + return shouldKeepVariableDIE(DIE, CU, MyInfo, Flags); + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_label: + return shouldKeepSubprogramDIE(DIE, CU, MyInfo, Flags); + case dwarf::DW_TAG_base_type: + // DWARF Expressions may reference basic types, but scanning them + // is expensive. Basic types are tiny, so just keep all of them. + case dwarf::DW_TAG_imported_module: + case dwarf::DW_TAG_imported_declaration: + case dwarf::DW_TAG_imported_unit: + // We always want to keep these. + return Flags | TF_Keep; + default: + break; + } + + return Flags; +} + +/// Helper that updates the completeness of the current DIE based on the +/// completeness of one of its children. It depends on the incompleteness of +/// the children already being computed. +static void updateChildIncompleteness(const DWARFDie &Die, CompileUnit &CU, + CompileUnit::DIEInfo &ChildInfo) { + switch (Die.getTag()) { + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + break; + default: + return; + } + + CompileUnit::DIEInfo &MyInfo = CU.getInfo(Die); + + if (ChildInfo.IncompleteDeclaration || ChildInfo.Prune) + MyInfo.IncompleteDeclaration = true; +} + +/// Helper that updates the completeness of the current DIE based on the +/// completeness of the DIEs it references. It depends on the incompleteness of +/// the referenced DIE already being computed. +static void updateRefIncompleteness(const DWARFDie &Die, CompileUnit &CU, + const CompileUnit::DIEInfo &RefInfo) { + switch (Die.getTag()) { + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_reference_type: + case dwarf::DW_TAG_ptr_to_member_type: + case dwarf::DW_TAG_pointer_type: + break; + default: + return; + } + + CompileUnit::DIEInfo &MyInfo = CU.getInfo(Die); + + if (MyInfo.IncompleteDeclaration) + return; + + if (RefInfo.IncompleteDeclaration) + MyInfo.IncompleteDeclaration = true; +} + +/// Look at the children of the given DIE and decide whether they should be +/// kept. +void DWARFLinker::lookForChildDIEsToKeep( + const DWARFDie &Die, CompileUnit &CU, unsigned Flags, + SmallVectorImpl &Worklist) { + // 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 the children + // of the parents as kept (consider for example a DW_TAG_namespace node in + // the parent chain). There are however a set of DIE types for which we want + // to ignore that directive and still walk their children. + if (dieNeedsChildrenToBeMeaningful(Die.getTag())) + Flags &= ~DWARFLinker::TF_ParentWalk; + + // We're finished if this DIE has no children or we're walking the parent + // chain. + if (!Die.hasChildren() || (Flags & DWARFLinker::TF_ParentWalk)) + return; + + // Add children in reverse order to the worklist to effectively process them + // in order. + for (auto Child : reverse(Die.children())) { + // Add a worklist item before every child to calculate incompleteness right + // after the current child is processed. + CompileUnit::DIEInfo &ChildInfo = CU.getInfo(Child); + Worklist.emplace_back(Die, CU, WorklistItemType::UpdateChildIncompleteness, + &ChildInfo); + Worklist.emplace_back(Child, CU, Flags); + } +} + +/// Look at DIEs referenced by the given DIE and decide whether they should be +/// kept. All DIEs referenced though attributes should be kept. +bool DWARFLinker::lookForRefDIEsToKeep( + const DWARFDie &Die, CompileUnit &CU, unsigned Flags, + const LinkContext &Context, SmallVectorImpl &Worklist) { + bool Res = true; + bool UseOdr = (Flags & DWARFLinker::TF_DependencyWalk) + ? (Flags & DWARFLinker::TF_ODR) + : CU.hasODR(); + DWARFUnit &Unit = CU.getOrigUnit(); + DWARFDataExtractor Data = Unit.getDebugInfoExtractor(); + const auto *Abbrev = Die.getAbbreviationDeclarationPtr(); + uint64_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode()); + + SmallVector, 4> ReferencedDIEs; + for (const auto &AttrSpec : Abbrev->attributes()) { + DWARFFormValue Val(AttrSpec.Form); + if (!Val.isFormClass(DWARFFormValue::FC_Reference) || + AttrSpec.Attr == dwarf::DW_AT_sibling) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, + Unit.getFormParams()); + continue; + } + + Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit); + assert(Val.isFormClass(DWARFFormValue::FC_Reference)); + uint64_t RefOffset = *Val.getAsReference(); + + if (CompileUnit *ReferencedCU = Context.getUnitForOffset(CU, RefOffset)) { + // Check whether referenced CU is equal to the current CU, or whether + // liveness analysis is done for referenced CU(i.e. we have correct + // IncompleteDeclaration field value). + if (ReferencedCU->getUniqueID() == CU.getUniqueID() || + ReferencedCU->getStage() >= + CompileUnit::Stages::LivenessAnalysisDone) { + if (const DWARFDie RefDie = + ReferencedCU->getOrigUnit().getDIEForOffset(RefOffset)) { + // In a file with broken references, an attribute might point to a + // NULL DIE. + if (RefDie.isNULL()) + reportWarning("could not find referenced DIE", CU.getContaingFile(), + &Die); + else { + CompileUnit::DIEInfo &RefInfo = ReferencedCU->getInfo(RefDie); + if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && RefInfo.Ctxt && + RefInfo.Ctxt.load()->hasCanonicalDie()) + continue; + + // Keep a module forward declaration if there is no definition. + if (!(isODRAttribute(AttrSpec.Attr) && RefInfo.Ctxt && + RefInfo.Ctxt.load()->hasCanonicalDie())) + RefInfo.Prune = false; + + if (ReferencedCU->getUniqueID() == CU.getUniqueID()) + ReferencedDIEs.emplace_back(RefDie, *ReferencedCU); + else { + // update IncompleteDeclaration field based on + // information from the RefInfo. + updateRefIncompleteness(Die, CU, RefInfo); + } + } + } else { + reportWarning("could not find referenced DIE", CU.getContaingFile(), + &Die); + } + } else { + // if referenced CU is not analyzed yet, set flag + // keeping input Dies(so that we do not reload them during next pass) + // and add referenced offset as live external reference to ReferencedCU. + Res = false; + ReferencedCU->addLiveReference(RefOffset); + ReferencedCU->setKeepInputDies(); + CU.setKeepInputDies(); + } + } else { + reportWarning("could not find referenced Compile Unit", + CU.getContaingFile(), &Die); + } + } + + unsigned ODRFlag = UseOdr ? DWARFLinker::TF_ODR : 0; + + // Add referenced DIEs in reverse order to the worklist to effectively + // process them in order. + for (auto &P : reverse(ReferencedDIEs)) { + // Add a worklist item before every child to calculate incompleteness right + // after the current child is processed. + CompileUnit::DIEInfo &Info = P.second.getInfo(P.first); + Worklist.emplace_back(Die, CU, WorklistItemType::UpdateRefIncompleteness, + &Info); + Worklist.emplace_back(P.first, P.second, + DWARFLinker::TF_Keep | + DWARFLinker::TF_DependencyWalk | ODRFlag); + } + + return Res; +} + +/// Look at the parent of the given DIE and decide whether they should be kept. +void DWARFLinker::lookForParentDIEsToKeep( + unsigned AncestorIdx, CompileUnit &CU, unsigned Flags, + SmallVectorImpl &Worklist) { + // Stop if we encounter an ancestor that's already marked as kept. + if (CU.getInfo(AncestorIdx).Keep) + return; + + DWARFUnit &Unit = CU.getOrigUnit(); + DWARFDie ParentDIE = Unit.getDIEAtIndex(AncestorIdx); + Worklist.emplace_back(CU.getInfo(AncestorIdx).ParentIdx, CU, Flags); + Worklist.emplace_back(ParentDIE, CU, Flags); +} + +/// Recursively walk the \p DIE tree and look for DIEs to keep. Store that +/// information in \p CU's DIEInfo. +/// +/// This function is the entry point of the DIE selection algorithm. It is +/// expected to walk the DIE tree in file order and (though the mediation of +/// its helper) call hasValidRelocation() on each DIE that might be a 'root +/// DIE' (See DwarfLinker class comment). +/// +/// While walking the dependencies of root DIEs, this function is also called, +/// but during these dependency walks the file order is not respected. The +/// TF_DependencyWalk flag tells us which kind of traversal we are currently +/// doing. +/// +/// The recursive algorithm is implemented iteratively as a work list because +/// very deep recursion could exhaust the stack for large projects. The work +/// list acts as a scheduler for different types of work that need to be +/// performed. +/// +/// The recursive nature of the algorithm is simulated by running the "main" +/// algorithm (LookForDIEsToKeep) followed by either looking at more DIEs +/// (LookForChildDIEsToKeep, LookForRefDIEsToKeep, LookForParentDIEsToKeep) or +/// fixing up a computed property (UpdateChildIncompleteness, +/// UpdateRefIncompleteness). +/// +/// Returns false if DIE has inter-CU references and referenced CU is not +/// analyzed yet. +bool DWARFLinker::lookForDIEsToKeep(const LinkContext &Context, + const DWARFDie &Die, CompileUnit &CU, + unsigned Flags) { + // LIFO work list. + SmallVector Worklist; + Worklist.emplace_back(Die, CU, Flags); + + bool Res = true; + + for (uint64_t DieOffset : CU.getLiveReferences()) { + if (const auto RefDie = CU.getOrigUnit().getDIEForOffset(DieOffset)) { + // In a file with broken references, an attribute might point to a NULL + // DIE. + if (!RefDie.isNULL()) { + Worklist.emplace_back(RefDie, CU, + TF_Keep | TF_DependencyWalk | + (CU.hasODR() ? TF_ODR : 0)); + } else { + reportWarning("DIE contains broken reference attribute", + CU.getContaingFile(), &Die); + } + } else { + reportWarning("DIE contains broken reference attribute", + CU.getContaingFile(), &Die); + } + } + + while (!Worklist.empty()) { + WorklistItem Current = Worklist.pop_back_val(); + + // Look at the worklist type to decide what kind of work to perform. + switch (Current.Type) { + case WorklistItemType::UpdateChildIncompleteness: + updateChildIncompleteness(Current.Die, Current.CU, *Current.OtherInfo); + continue; + case WorklistItemType::UpdateRefIncompleteness: + updateRefIncompleteness(Current.Die, Current.CU, *Current.OtherInfo); + continue; + case WorklistItemType::LookForChildDIEsToKeep: + lookForChildDIEsToKeep(Current.Die, Current.CU, Current.Flags, Worklist); + continue; + case WorklistItemType::LookForRefDIEsToKeep: + if (!lookForRefDIEsToKeep(Current.Die, Current.CU, Current.Flags, Context, + Worklist)) + Res = false; + continue; + case WorklistItemType::LookForParentDIEsToKeep: + lookForParentDIEsToKeep(Current.AncestorIdx, Current.CU, Current.Flags, + Worklist); + continue; + case WorklistItemType::LookForDIEsToKeep: + break; + } + + unsigned Idx = Current.CU.getOrigUnit().getDIEIndex(Current.Die); + CompileUnit::DIEInfo &MyInfo = Current.CU.getInfo(Idx); + + if (MyInfo.Prune) + continue; + + // If the Keep flag is set, we are marking a required DIE's dependencies. + // If our target is already marked as kept, we're all set. + bool AlreadyKept = MyInfo.Keep; + if ((Current.Flags & TF_DependencyWalk) && AlreadyKept) + continue; + + // We must not call shouldKeepDIE while called from keepDIEAndDependencies, + // because it would screw up the relocation finding logic. + if (!(Current.Flags & TF_DependencyWalk)) + Current.Flags = + shouldKeepDIE(Current.Die, Current.CU, MyInfo, Current.Flags); + + // Finish by looking for child DIEs. Because of the LIFO worklist we need + // to schedule that work before any subsequent items are added to the + // worklist. + Worklist.emplace_back(Current.Die, Current.CU, Current.Flags, + WorklistItemType::LookForChildDIEsToKeep); + + if (AlreadyKept || !(Current.Flags & TF_Keep)) + continue; + + // If it is a newly kept DIE mark it as well as all its dependencies as + // kept. + MyInfo.Keep = true; + + // We're looking for incomplete types. + MyInfo.IncompleteDeclaration = + Current.Die.getTag() != dwarf::DW_TAG_subprogram && + Current.Die.getTag() != dwarf::DW_TAG_member && + dwarf::toUnsigned(Current.Die.find(dwarf::DW_AT_declaration), 0); + + // After looking at the parent chain, look for referenced DIEs. Because of + // the LIFO worklist we need to schedule that work before any subsequent + // items are added to the worklist. + Worklist.emplace_back(Current.Die, Current.CU, Current.Flags, + WorklistItemType::LookForRefDIEsToKeep); + + bool UseOdr = (Current.Flags & TF_DependencyWalk) ? (Current.Flags & TF_ODR) + : Current.CU.hasODR(); + unsigned ODRFlag = UseOdr ? TF_ODR : 0; + unsigned ParFlags = TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag; + + // Now schedule the parent walk. + Worklist.emplace_back(MyInfo.ParentIdx, Current.CU, ParFlags); + } + + return Res; +} + +unsigned DWARFLinker::DIECloner::cloneStringAttribute(DIE &Die, + const DWARFDie &InputDIE, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, + AttributesInfo &Info) { + Optional String; + if (AttrSpec.Form == dwarf::DW_FORM_string) { + // In this case Val points to inplace copy of the string(inside DIECopy) + // done by cloneDIE. We are going to store pointer to the string, + // so take a string value from the InputDie. + Optional StrVal = InputDIE.find(AttrSpec.Attr); + if (StrVal) + String = StrVal->getAsCString(); + } else + String = Val.getAsCString(); + + if (!String) + return 0; + + // Update attributes info. + if (AttrSpec.Attr == dwarf::DW_AT_name) + Info.Name = *String; + else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || + AttrSpec.Attr == dwarf::DW_AT_linkage_name) + Info.MangledName = *String; + + if (Linker.IsMT()) { + // In multy-threaded case it is in-effective to lock&use + // common resource TheOffsetsStringPool for every string + // attribute. Instead, we remember string attribute and + // put it into the string pool and update later. That way + // we lock TheOffsetsStringPool only once for each compile + // unit. + + // create string attribute with dummy string offset + DIE::value_iterator It = + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_strp, DIEInteger(0xbaddef)); + + // create delayed task for setting proper offset value for the string + // attribute. + CU.noteOffsetData(It, *String); + } else { + // add string to the pool and set proper offset. + uint64_t Offset = Linker.TheOffsetsStringPool.getEntry(*String).getOffset(); + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, + DIEInteger(Offset)); + } + + return 4; +} + +unsigned DWARFLinker::DIECloner::cloneDieReferenceAttribute( + DIE &Die, AttributeSpec AttrSpec, unsigned AttrSize, + const DWARFFormValue &Val, AttributesInfo &Info, uint64_t AttrOutOffset) { + const DWARFUnit &U = CU.getOrigUnit(); + uint64_t RefOffset = *Val.getAsReference(); + + // If the referenced DIE is not found, drop the attribute. + if (AttrSpec.Attr == dwarf::DW_AT_sibling) + return 0; + + const CompileUnit *RefCU = Context.getUnitForOffset(CU, RefOffset); + if (!RefCU) + return 0; + + DWARFDie RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset); + if (!RefDie) + return 0; + + const CompileUnit::DIEInfo &RefInfo = RefCU->getInfo(RefDie); + + bool IsModuleRef = RefInfo.Ctxt && RefInfo.Ctxt.load()->hasCanonicalDie() && + RefInfo.Ctxt.load()->isDefinedInClangModule(); + + // Check for canonical Die for ODR attribute. + if (isODRAttribute(AttrSpec.Attr) && (CU.hasODR() || IsModuleRef)) { + // We cannot currently rely on a DIEEntry to emit ref_addr + // references, because the implementation calls back to DwarfDebug + // to find the unit offset. (We don't have a DwarfDebug) + // FIXME: we should be able to design DIEEntry reliance on + // DwarfDebug away. + uint64_t Attr; + Attr = 0xBADDEF; + + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, DIEInteger(Attr)); + + // We remember reference attribute to update it later when + // final offsets are known. + if (RefInfo.Ctxt && RefInfo.Ctxt.load()->hasCanonicalDie()) { + // Remember offset to the canonical Die. + Info.AttrReferences.emplace_back( + RefInfo.Ctxt.load()->getCanonicalDIEIdx(), + RefInfo.Ctxt.load()->getCanonicalCU(), AttrOutOffset, false); + } else { + // Remember offset to the referenced Die. + Info.AttrReferences.emplace_back(RefCU->getOrigUnit().getDIEIndex(RefDie), + RefCU, AttrOutOffset, false); + } + + return U.getRefAddrByteSize(); + } + + // If referenced Die is already cloned and this is local reference then + // we might use already known ClonedDieOffset. + if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && RefInfo.ClonedDieOffset) { + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), + DIEInteger(RefInfo.ClonedDieOffset)); + return AttrSize; + } + + // For other cases we need to remember reference attribute to update it later + // when final offsets are known. + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(RefInfo.ClonedDieOffset)); + + Info.AttrReferences.emplace_back(RefCU->getOrigUnit().getDIEIndex(RefDie), + RefCU, AttrOutOffset, + AttrSpec.Form != dwarf::DW_FORM_ref_addr); + return AttrSize; +} + +void DWARFLinker::DIECloner::cloneExpression( + DataExtractor &Data, DWARFExpression Expression, + SmallVectorImpl &OutputBuffer) { + using Encoding = DWARFExpression::Operation::Encoding; + + uint64_t OpOffset = 0; + for (auto &Op : Expression) { + auto Description = Op.getDescription(); + // DW_OP_const_type is variable-length and has 3 + // operands. DWARFExpression thus far only supports 2. + auto Op0 = Description.Op[0]; + auto Op1 = Description.Op[1]; + if ((Op0 == Encoding::BaseTypeRef && Op1 != Encoding::SizeNA) || + (Op1 == Encoding::BaseTypeRef && Op0 != Encoding::Size1)) + Linker.reportWarning("Unsupported DW_OP encoding.", CU.getContaingFile()); + + if ((Op0 == Encoding::BaseTypeRef && Op1 == Encoding::SizeNA) || + (Op1 == Encoding::BaseTypeRef && Op0 == Encoding::Size1)) { + // This code assumes that the other non-typeref operand fits into 1 byte. + assert(OpOffset < Op.getEndOffset()); + uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1; + assert(ULEBsize <= 16); + + // Copy over the operation. + OutputBuffer.push_back(Op.getCode()); + uint64_t RefOffset; + if (Op1 == Encoding::SizeNA) { + RefOffset = Op.getRawOperand(0); + } else { + OutputBuffer.push_back(Op.getRawOperand(0)); + RefOffset = Op.getRawOperand(1); + } + uint32_t Offset = 0; + // Look up the base type. For DW_OP_convert, the operand may be 0 to + // instead indicate the generic type. The same holds for + // DW_OP_reinterpret, which is currently not supported. + if (RefOffset > 0 || Op.getCode() != dwarf::DW_OP_convert) { + auto RefDie = CU.getOrigUnit().getDIEForOffset(RefOffset); + CompileUnit::DIEInfo &Info = CU.getInfo(RefDie); + uint64_t ClonedDieOffset = Info.ClonedDieOffset; + if (ClonedDieOffset != 0) + Offset = ClonedDieOffset; + else + Linker.reportWarning( + "base type ref doesn't point to DW_TAG_base_type.", + CU.getContaingFile()); + } + uint8_t ULEB[16]; + unsigned RealSize = encodeULEB128(Offset, ULEB, ULEBsize); + if (RealSize > ULEBsize) { + // Emit the generic type as a fallback. + RealSize = encodeULEB128(0, ULEB, ULEBsize); + Linker.reportWarning("base type ref doesn't fit.", + CU.getContaingFile()); + } + assert(RealSize == ULEBsize && "padding failed"); + ArrayRef ULEBbytes(ULEB, ULEBsize); + OutputBuffer.append(ULEBbytes.begin(), ULEBbytes.end()); + } else { + // Copy over everything else unmodified. + StringRef Bytes = Data.getData().slice(OpOffset, Op.getEndOffset()); + OutputBuffer.append(Bytes.begin(), Bytes.end()); + } + OpOffset = Op.getEndOffset(); + } +} + +unsigned DWARFLinker::DIECloner::cloneBlockAttribute(DIE &Die, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, + unsigned AttrSize) { + DIEValueList *Attr; + DIEValue Value; + DIELoc *Loc = nullptr; + DIEBlock *Block = nullptr; + if (AttrSpec.Form == dwarf::DW_FORM_exprloc) { + Loc = new (DIEAlloc) DIELoc; + DIELocs.push_back(Loc); + } else { + Block = new (DIEAlloc) DIEBlock; + DIEBlocks.push_back(Block); + } + Attr = Loc ? static_cast(Loc) + : static_cast(Block); + + if (Loc) + Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), Loc); + else + Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), Block); + + // If the block is a DWARF Expression, clone it into the temporary + // buffer using cloneExpression(), otherwise copy the data directly. + SmallVector Buffer; + ArrayRef Bytes = *Val.getAsBlock(); + if (DWARFAttribute::mayHaveLocationDescription(AttrSpec.Attr) && + (Val.isFormClass(DWARFFormValue::FC_Block) || + Val.isFormClass(DWARFFormValue::FC_Exprloc))) { + DWARFUnit &OrigUnit = CU.getOrigUnit(); + DataExtractor Data(StringRef((const char *)Bytes.data(), Bytes.size()), + CU.getEndianness() == support::endianness::little, + OrigUnit.getAddressByteSize()); + DWARFExpression Expr(Data, OrigUnit.getAddressByteSize(), + OrigUnit.getFormParams().Format); + cloneExpression(Data, Expr, Buffer); + Bytes = Buffer; + } + for (auto Byte : Bytes) + Attr->addValue(DIEAlloc, static_cast(0), + dwarf::DW_FORM_data1, DIEInteger(Byte)); + + // FIXME: If DIEBlock and DIELoc just reuses the Size field of + // the DIE class, this "if" could be replaced by + // Attr->setSize(Bytes.size()). + if (Loc) + Loc->setSize(Bytes.size()); + else + Block->setSize(Bytes.size()); + + Die.addValue(DIEAlloc, Value); + return AttrSize; +} + +unsigned +DWARFLinker::DIECloner::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, + AttributesInfo &Info) { + if (!Options.RemoveObsoleteDebugInfo) { + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) + Info.HasLowPc = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Val.getRawUValue())); + return CU.getOrigUnit().getAddressByteSize(); + } + + dwarf::Form Form = AttrSpec.Form; + uint64_t Addr = 0; + if (Form == dwarf::DW_FORM_addrx) { + if (Optional AddrOffsetSectionBase = + CU.getOrigUnit().getAddrOffsetSectionBase()) { + uint64_t StartOffset = *AddrOffsetSectionBase + Val.getRawUValue(); + uint64_t EndOffset = StartOffset + CU.getOrigUnit().getAddressByteSize(); + if (llvm::Expected RelocAddr = + CU.getContaingFile().Addresses->relocateIndexedAddr(StartOffset, + EndOffset)) + Addr = *RelocAddr; + else + Linker.reportWarning(toString(RelocAddr.takeError()), + CU.getContaingFile()); + } else + Linker.reportWarning("no base offset for address table", + CU.getContaingFile()); + + // If this is an indexed address emit the debug_info address. + Form = dwarf::DW_FORM_addr; + } else + Addr = *Val.getAsAddress(); + + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) { + if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine || + Die.getTag() == dwarf::DW_TAG_lexical_block) + // The low_pc of a block or inline subroutine might get + // relocated because it happens to match the low_pc of the + // enclosing subprogram. To prevent issues with that, always use + // the low_pc from the input DIE if relocations have been applied. + Addr = (Info.OrigLowPc != std::numeric_limits::max() + ? Info.OrigLowPc + : Addr) + + Info.PCOffset; + else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { + Addr = CU.getLowPc(); + if (Addr == std::numeric_limits::max()) + return 0; + } + Info.HasLowPc = true; + } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc) { + if (Die.getTag() == dwarf::DW_TAG_compile_unit) { + if (uint64_t HighPc = CU.getHighPc()) + Addr = HighPc; + else + return 0; + } else + // If we have a high_pc recorded for the input DIE, use + // it. Otherwise (when no relocations where applied) just use the + // one we just decoded. + Addr = (Info.OrigHighPc ? Info.OrigHighPc : Addr) + Info.PCOffset; + } else if (AttrSpec.Attr == dwarf::DW_AT_call_return_pc) { + // Relocate a return PC address within a call site entry. + if (Die.getTag() == dwarf::DW_TAG_call_site) + Addr = (Info.OrigCallReturnPc ? Info.OrigCallReturnPc : Addr) + + Info.PCOffset; + } else if (AttrSpec.Attr == dwarf::DW_AT_call_pc) { + // Relocate the address of a branch instruction within a call site entry. + if (Die.getTag() == dwarf::DW_TAG_call_site) + Addr = (Info.OrigCallPc ? Info.OrigCallPc : Addr) + Info.PCOffset; + } + + Die.addValue(DIEAlloc, static_cast(AttrSpec.Attr), + static_cast(Form), DIEInteger(Addr)); + return CU.getOrigUnit().getAddressByteSize(); +} + +unsigned DWARFLinker::DIECloner::cloneScalarAttribute( + DIE &Die, const DWARFDie &InputDIE, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize, AttributesInfo &Info, + uint64_t AttrOutOffset) { + uint64_t Value; + + if (!Options.RemoveObsoleteDebugInfo) { + 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.", + CU.getContaingFile(), &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 (CU.getLowPc() == -1ULL) + return 0; + // Dwarf >= 4 high_pc is an size, not an address. + Value = CU.getHighPc() - CU.getLowPc(); + } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) + Value = *Val.getAsSectionOffset(); + else if (AttrSpec.Form == dwarf::DW_FORM_sdata) + Value = *Val.getAsSignedConstant(); + else if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else { + Linker.reportWarning( + "Unsupported scalar attribute form. Dropping attribute.", + CU.getContaingFile(), &InputDIE); + return 0; + } + + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Value)); + + if (AttrSpec.Attr == dwarf::DW_AT_ranges) + Info.RangeAttrReferences.emplace_back(Value, AttrOutOffset); + + // A more generic way to check for location attributes would be + // nice, but it's very unlikely that any other attribute needs a + // location list. + // FIXME: use DWARFAttribute::mayHaveLocationDescription(). + else if (AttrSpec.Attr == dwarf::DW_AT_location || + AttrSpec.Attr == dwarf::DW_AT_frame_base) { + Info.LocationAttrReferences.emplace_back(AttrOutOffset, Value, + Info.PCOffset); + } else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + Info.IsDeclaration = true; + + return AttrSize; +} + +/// Clone \p InputDIE's attribute described by \p AttrSpec with +/// 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, const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info, + uint64_t AttrOutOffset) { + switch (AttrSpec.Form) { + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_string: + case dwarf::DW_FORM_strx: + case dwarf::DW_FORM_strx1: + case dwarf::DW_FORM_strx2: + case dwarf::DW_FORM_strx3: + case dwarf::DW_FORM_strx4: + return cloneStringAttribute(Die, InputDIE, AttrSpec, Val, 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, AttrSpec, AttrSize, Val, Info, + AttrOutOffset); + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + case dwarf::DW_FORM_exprloc: + return cloneBlockAttribute(Die, AttrSpec, Val, AttrSize); + case dwarf::DW_FORM_addr: + case dwarf::DW_FORM_addrx: + return cloneAddressAttribute(Die, AttrSpec, Val, Info); + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_sdata: + case dwarf::DW_FORM_sec_offset: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_flag_present: + return cloneScalarAttribute(Die, InputDIE, AttrSpec, Val, AttrSize, Info, + AttrOutOffset); + default: + Linker.reportWarning("Unsupported attribute form " + + dwarf::FormEncodingString(AttrSpec.Form) + + " in cloneAttribute. Dropping.", + CU.getContaingFile(), &InputDIE); + } + + return 0; +} + +static bool isObjCSelector(StringRef Name) { + return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') && + (Name[1] == '['); +} + +void DWARFLinker::DIECloner::addObjCAccelerator(CompileUnit &CU, const DIE *Die, + StringRef Name, + bool SkipPubSection) { + assert(isObjCSelector(Name) && "not an objc selector"); + // Objective C method or class function. + // "- [Class(Category) selector :withArg ...]" + StringRef ClassNameStart(StringRef(Name).drop_front(2)); + size_t FirstSpace = ClassNameStart.find(' '); + if (FirstSpace == StringRef::npos) + return; + + StringRef SelectorStart(ClassNameStart.data() + FirstSpace + 1); + if (!SelectorStart.size()) + return; + + StringRef Selector(SelectorStart.data(), SelectorStart.size() - 1); + CU.addNameAccelerator(Die, Selector, SkipPubSection); + + // Add an entry for the class name that points to this + // method/class function. + StringRef ClassName(ClassNameStart.data(), FirstSpace); + CU.addObjCAccelerator(Die, ClassName, SkipPubSection); + + if (ClassName[ClassName.size() - 1] == ')') { + size_t OpenParens = ClassName.find('('); + if (OpenParens != StringRef::npos) { + StringRef ClassNameNoCategory(ClassName.data(), OpenParens); + CU.addObjCAccelerator(Die, ClassNameNoCategory, SkipPubSection); + + std::string MethodNameNoCategory(Name.data(), OpenParens + 2); + // FIXME: The missing space here may be a bug, but + // dsymutil-classic also does it this way. + MethodNameNoCategory.append(std::string(SelectorStart)); + + CU.addNameAccelerator(Die, CU.internString(MethodNameNoCategory), + SkipPubSection); + } + } +} + +static bool +shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, + uint16_t Tag, bool InDebugMap, bool SkipPC, + bool InFunctionScope) { + switch (AttrSpec.Attr) { + default: + return false; + case dwarf::DW_AT_low_pc: + case dwarf::DW_AT_high_pc: + case dwarf::DW_AT_ranges: + return SkipPC; + case dwarf::DW_AT_str_offsets_base: + // FIXME: Use the string offset table with Dwarf 5. + return true; + case dwarf::DW_AT_location: + case dwarf::DW_AT_frame_base: + // FIXME: for some reason dsymutil-classic keeps the location attributes + // when they are of block type (i.e. not location lists). This is totally + // wrong for globals where we will keep a wrong address. It is mostly + // harmless for locals, but there is no point in keeping these anyway when + // the function wasn't linked. + return (SkipPC || (!InFunctionScope && Tag == dwarf::DW_TAG_variable && + !InDebugMap)) && + !DWARFFormValue(AttrSpec.Form).isFormClass(DWARFFormValue::FC_Block); + } +} + +DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE, + int64_t PCOffset, uint32_t OutOffset, + unsigned Flags, DIE *Die) { + DWARFUnit &U = CU.getOrigUnit(); + unsigned Idx = U.getDIEIndex(InputDIE); + CompileUnit::DIEInfo &Info = CU.getInfo(Idx); + + // Should the DIE appear in the output? + if (!Info.Keep) + return nullptr; + + uint64_t Offset = InputDIE.getOffset(); + assert((!Die || Idx == 0) && "DIE is already cloned."); + + // The DIE might have been already created by a forward reference + // (see cloneDieReferenceAttribute()). + if (!Die) + Die = DIE::get(DIEAlloc, dwarf::Tag(InputDIE.getTag())); + + assert(Die->getTag() == InputDIE.getTag()); + Die->setOffset(OutOffset); + CU.getInfo(Idx).ClonedDieOffset = OutOffset; + if ((CU.hasODR() || CU.isClangModule()) && !Info.IncompleteDeclaration && + Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt && + Info.Ctxt != CU.getInfo(Info.ParentIdx).Ctxt && + !Info.Ctxt.load()->hasCanonicalDie()) { + // We are about to emit a DIE that is the root of its own valid + // DeclContext tree. Make the current offset the canonical offset + // for this context. + const_cast(Info.Ctxt.load())->setCanonicalInfo(Idx, &CU); + } + + // Extract and clone every attribute. + DWARFDataExtractor Data = U.getDebugInfoExtractor(); + // Point to the next DIE (generally there is always at least a NULL + // entry after the current one). If this is a lone + // DW_TAG_compile_unit without any children, point to the next unit. + uint64_t NextOffset = (Idx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(Idx + 1).getOffset() + : U.getNextUnitOffset(); + AttributesInfo AttrInfo; + + // We could copy the data only if we need to apply a relocation to it. After + // testing, it seems there is no performance downside to doing the copy + // unconditionally, and it makes the code simpler. + SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset)); + Data = + DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); + + // Modify the copy with relocated addresses. + if (CU.getContaingFile().Addresses->areRelocationsResolved() && + CU.getContaingFile().Addresses->applyValidRelocs(DIECopy, Offset, + Data.isLittleEndian())) { + // If we applied relocations, we store the value of high_pc that was + // potentially stored in the input DIE. If high_pc is an address + // (Dwarf version == 2), then it might have been relocated to a + // totally unrelated value (because the end address in the object + // file might be start address of another function which got moved + // independently by the linker). The computation of the actual + // high_pc value is done in cloneAddressAttribute(). + AttrInfo.OrigHighPc = + dwarf::toAddress(InputDIE.find(dwarf::DW_AT_high_pc), 0); + // Also store the low_pc. It might get relocated in an + // inline_subprogram that happens at the beginning of its + // inlining function. + AttrInfo.OrigLowPc = dwarf::toAddress(InputDIE.find(dwarf::DW_AT_low_pc), + std::numeric_limits::max()); + AttrInfo.OrigCallReturnPc = + dwarf::toAddress(InputDIE.find(dwarf::DW_AT_call_return_pc), 0); + AttrInfo.OrigCallPc = + dwarf::toAddress(InputDIE.find(dwarf::DW_AT_call_pc), 0); + } + + // Reset the Offset to 0 as we will be working on the local copy of + // the data. + Offset = 0; + + const auto *Abbrev = InputDIE.getAbbreviationDeclarationPtr(); + Offset += getULEB128Size(Abbrev->getCode()); + + // We are entering a subprogram. Get and propagate the PCOffset. + if (Die->getTag() == dwarf::DW_TAG_subprogram) + PCOffset = Info.AddrAdjust; + AttrInfo.PCOffset = PCOffset; + + if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) { + Flags |= TF_InFunctionScope; + if (!Info.InDebugMap && Options.RemoveObsoleteDebugInfo) + Flags |= TF_SkipPC; + } + + for (const auto &AttrSpec : Abbrev->attributes()) { + if (Options.RemoveObsoleteDebugInfo && + shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, + Flags & TF_SkipPC, Flags & TF_InFunctionScope)) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, + U.getFormParams()); + continue; + } + + if (Die->getTag() == dwarf::DW_TAG_compile_unit && + AttrSpec.Attr == dwarf::DW_AT_stmt_list) { + CU.setOffsetToStmtList(OutOffset); + } + + DWARFFormValue Val(AttrSpec.Form); + uint64_t AttrSize = Offset; + Val.extractValue(Data, &Offset, U.getFormParams(), &U); + AttrSize = Offset - AttrSize; + + OutOffset += cloneAttribute(*Die, InputDIE, Val, AttrSpec, AttrSize, + AttrInfo, OutOffset); + } + + // Look for accelerator entries. + uint16_t Tag = InputDIE.getTag(); + // FIXME: This is slightly wrong. An inline_subroutine without a + // low_pc, but with AT_ranges might be interesting to get into the + // accelerator tables too. For now stick with dsymutil's behavior. + if ((Info.InDebugMap || AttrInfo.HasLowPc || + AttrInfo.RangeAttrReferences.size() > 0) && + Tag != dwarf::DW_TAG_compile_unit && + getDIENames(InputDIE, AttrInfo, + Tag != dwarf::DW_TAG_inlined_subroutine)) { + if (Options.GeneratePubNames || Options.GenerateAppleNames || + Options.GenerateDebugNames) { + if (!AttrInfo.MangledName.empty() && + AttrInfo.MangledName != AttrInfo.Name) + CU.addNameAccelerator(Die, AttrInfo.MangledName, + Tag == dwarf::DW_TAG_inlined_subroutine); + if (!AttrInfo.Name.empty()) { + if (!AttrInfo.NameWithoutTemplate.empty()) + CU.addNameAccelerator(Die, AttrInfo.NameWithoutTemplate, + /* SkipPubSection */ true); + CU.addNameAccelerator(Die, AttrInfo.Name, + Tag == dwarf::DW_TAG_inlined_subroutine); + } + } + if (Options.GenerateAppleObjC) + if (!AttrInfo.Name.empty() && isObjCSelector(AttrInfo.Name)) + addObjCAccelerator(CU, Die, AttrInfo.Name, /* SkipPubSection =*/true); + } else if (Tag == dwarf::DW_TAG_namespace && + (Options.GenerateAppleNamespaces || Options.GenerateDebugNames)) { + if (AttrInfo.Name.empty()) + AttrInfo.Name = "(anonymous namespace)"; + CU.addNamespaceAccelerator(Die, AttrInfo.Name); + } else if (Options.GeneratePubTypes || Options.GenerateAppleTypes) { + if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && + getDIENames(InputDIE, AttrInfo) && !AttrInfo.Name.empty() && + AttrInfo.Name[0]) { + uint32_t Hash = + hashFullyQualifiedName(InputDIE, CU, CU.getContaingFile()); + uint64_t RuntimeLang = + dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class)) + .getValueOr(0); + bool ObjCClassIsImplementation = + (RuntimeLang == dwarf::DW_LANG_ObjC || + RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) && + dwarf::toUnsigned( + InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type)) + .getValueOr(0); + CU.addTypeAccelerator(Die, AttrInfo.Name, ObjCClassIsImplementation, + Hash); + } + } + + // Determine whether there are any children that we want to keep. + bool HasChildren = false; + for (auto Child : InputDIE.children()) { + unsigned Idx = U.getDIEIndex(Child); + if (CU.getInfo(Idx).Keep) { + HasChildren = true; + break; + } + } + + DIEAbbrev NewAbbrev = Die->generateAbbrev(); + if (HasChildren) + NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); + // Assign a permanent abbrev number + CU.assignAbbrev(NewAbbrev); + Die->setAbbrevNumber(NewAbbrev.getNumber()); + + uint64_t SizeOfAbbreviationNumer = getULEB128Size(Die->getAbbrevNumber()); + + // We need to update offsets to remembered attributes after we know + // the size of the abbreviation number. + for (CompileUnit::DieRefAttrPatchInfo AttrRef : AttrInfo.AttrReferences) { + AttrRef.OffsetToAttr += SizeOfAbbreviationNumer; + + CU.noteDieReference(AttrRef); + } + + for (CompileUnit::RangeAttributePatchInfo AttrOffsets : + AttrInfo.RangeAttrReferences) { + AttrOffsets.OffsetToAttr += SizeOfAbbreviationNumer; + CU.noteRangeAttribute(*Die, AttrOffsets); + } + + for (CompileUnit::LocationAttributePatchInfo LocAttr : + AttrInfo.LocationAttrReferences) { + LocAttr.OffsetToAttr += SizeOfAbbreviationNumer; + CU.noteLocationAttribute(LocAttr.OffsetToAttr, LocAttr.CULocalOffsetValue, + LocAttr.PCValue); + } + + if (Die->getTag() == dwarf::DW_TAG_compile_unit) { + if (Optional Offset = CU.getOffsetToStmtList()) + CU.setOffsetToStmtList(*Offset + SizeOfAbbreviationNumer); + } + + // Add the size of the abbreviation number to the output offset. + OutOffset += SizeOfAbbreviationNumer; + + if (!HasChildren) { + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; + } + + // Recursively clone children. + for (auto Child : InputDIE.children()) { + if (DIE *Clone = cloneDIE(Child, PCOffset, OutOffset, Flags)) { + Die->addChild(Clone); + OutOffset = Clone->getOffset() + Clone->getSize(); + } + } + + // Account for the end of children marker. + OutOffset += sizeof(int8_t); + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; +} + +/// Patch the input object file relevant debug_ranges entries +/// and emit them in the output file. Update the relevant attributes +/// to point at the new entries. +void DWARFLinker::patchRangesForUnit(CompileUnit &CU, + DwarfEmitter &Emitter) const { + DWARFDebugRangeList RangeList; + const auto &FunctionRanges = CU.getFunctionRanges(); + unsigned AddressSize = CU.getOrigUnit().getAddressByteSize(); + DWARFDataExtractor RangeExtractor( + CU.getContaingFile().Dwarf->getDWARFObj(), + CU.getContaingFile().Dwarf->getDWARFObj().getRangesSection(), + CU.getContaingFile().Dwarf->isLittleEndian(), AddressSize); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + DWARFUnit &OrigUnit = CU.getOrigUnit(); + auto OrigUnitDie = OrigUnit.getUnitDIE(false); + uint64_t OrigLowPc = + dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), -1ULL); + // Ranges addresses are based on the unit's low_pc. Compute the + // offset we need to apply to adapt to the new unit's low_pc. + int64_t UnitPcOffset = 0; + if (OrigLowPc != -1ULL) + UnitPcOffset = int64_t(OrigLowPc) - CU.getLowPc(); + + for (auto &RangeAttributeOffsets : CU.getRangesAttributes()) { + uint64_t Offset = RangeAttributeOffsets.CULocalOffsetValue; + RangeAttributeOffsets.CULocalOffsetValue = Emitter.getRangesSectionSize(); + if (Error E = RangeList.extract(RangeExtractor, &Offset)) { + llvm::consumeError(std::move(E)); + reportWarning("invalid range list ignored.", CU.getContaingFile()); + RangeList.clear(); + } + const auto &Entries = RangeList.getEntries(); + if (!Entries.empty()) { + const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); + + if (CurrRange == InvalidRange || + First.StartAddress + OrigLowPc < CurrRange.start() || + First.StartAddress + OrigLowPc >= CurrRange.stop()) { + CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); + if (CurrRange == InvalidRange || + CurrRange.start() > First.StartAddress + OrigLowPc) { + reportWarning("no mapping for range.", CU.getContaingFile()); + continue; + } + } + } + + Emitter.emitRangesEntries(UnitPcOffset, OrigLowPc, CurrRange, Entries, + AddressSize); + } +} + +/// Insert the new line info sequence \p Seq into the current +/// set of already linked line info \p Rows. +static void insertLineSequence(std::vector &Seq, + std::vector &Rows) { + if (Seq.empty()) + return; + + if (!Rows.empty() && Rows.back().Address < Seq.front().Address) { + llvm::append_range(Rows, Seq); + Seq.clear(); + return; + } + + object::SectionedAddress Front = Seq.front().Address; + auto InsertPoint = partition_point( + Rows, [=](const DWARFDebugLine::Row &O) { return O.Address < Front; }); + + // FIXME: this only removes the unneeded end_sequence if the + // sequences have been inserted in order. Using a global sort like + // described in patchLineTableForUnit() and delaying the end_sequene + // elimination to emitLineTableForUnit() we can get rid of all of them. + if (InsertPoint != Rows.end() && InsertPoint->Address == Front && + InsertPoint->EndSequence) { + *InsertPoint = Seq.front(); + Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end()); + } else { + Rows.insert(InsertPoint, Seq.begin(), Seq.end()); + } + + Seq.clear(); +} + +/// Extract the line table for \p CU, and +/// recreate a relocated version of these for the address ranges that +/// are present in the binary. +void DWARFLinker::patchLineTableForUnit(CompileUnit &CU, + DwarfEmitter &Emitter) { + DWARFDie CUDie = CU.getOrigUnit().getUnitDIE(); + auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); + if (!StmtList) + return; + + RangesTy *Ranges; + CU.getContaingFile().Addresses->getValidAddressRanges(Ranges); + + // Parse the original line info for the unit. + DWARFDebugLine::LineTable LineTable; + uint64_t StmtOffset = *StmtList; + DWARFDataExtractor LineExtractor( + CU.getContaingFile().Dwarf->getDWARFObj(), + CU.getContaingFile().Dwarf->getDWARFObj().getLineSection(), + CU.getContaingFile().Dwarf->isLittleEndian(), + CU.getOrigUnit().getAddressByteSize()); + if (needToTranslateStrings()) + return Emitter.translateLineTable(LineExtractor, StmtOffset); + + if (Error Err = LineTable.parse( + LineExtractor, &StmtOffset, *CU.getContaingFile().Dwarf, + &CU.getOrigUnit(), CU.getContaingFile().Dwarf->getWarningHandler())) + CU.getContaingFile().Dwarf->getWarningHandler()(std::move(Err)); + + // This vector is the output line table. + std::vector NewRows; + NewRows.reserve(LineTable.Rows.size()); + + // Current sequence of rows being extracted, before being inserted + // in NewRows. + std::vector Seq; + const auto &FunctionRanges = CU.getFunctionRanges(); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + + // FIXME: This logic is meant to generate exactly the same output as + // Darwin's classic dsymutil. There is a nicer way to implement this + // by simply putting all the relocated line info in NewRows and simply + // sorting NewRows before passing it to emitLineTableForUnit. This + // should be correct as sequences for a function should stay + // together in the sorted output. There are a few corner cases that + // look suspicious though, and that required to implement the logic + // this way. Revisit that once initial validation is finished. + + // Iterate over the object file line info and extract the sequences + // that correspond to linked functions. + for (auto &Row : LineTable.Rows) { + // Check whether we stepped out of the range. The range is + // half-open, but consider accept the end address of the range if + // it is marked as end_sequence in the input (because in that + // case, the relocation offset is accurate and that entry won't + // serve as the start of another function). + if (CurrRange == InvalidRange || Row.Address.Address < CurrRange.start() || + Row.Address.Address > CurrRange.stop() || + (Row.Address.Address == CurrRange.stop() && !Row.EndSequence)) { + // We just stepped out of a known range. Insert a end_sequence + // corresponding to the end of the range. + uint64_t StopAddress = CurrRange != InvalidRange + ? CurrRange.stop() + CurrRange.value() + : -1ULL; + CurrRange = FunctionRanges.find(Row.Address.Address); + bool CurrRangeValid = + CurrRange != InvalidRange && CurrRange.start() <= Row.Address.Address; + if (!CurrRangeValid) { + CurrRange = InvalidRange; + if (StopAddress != -1ULL) { + // Try harder by looking in the Address ranges map. + // There are corner cases where this finds a + // valid entry. It's unclear if this is right or wrong, but + // for now do as dsymutil. + // FIXME: Understand exactly what cases this addresses and + // potentially remove it along with the Ranges map. + auto Range = Ranges->lower_bound(Row.Address.Address); + if (Range != Ranges->begin() && Range != Ranges->end()) + --Range; + + if (Range != Ranges->end() && Range->first <= Row.Address.Address && + Range->second.HighPC >= Row.Address.Address) { + StopAddress = Row.Address.Address + Range->second.Offset; + } + } + } + if (StopAddress != -1ULL && !Seq.empty()) { + // Insert end sequence row with the computed end address, but + // the same line as the previous one. + auto NextLine = Seq.back(); + NextLine.Address.Address = StopAddress; + NextLine.EndSequence = 1; + NextLine.PrologueEnd = 0; + NextLine.BasicBlock = 0; + NextLine.EpilogueBegin = 0; + Seq.push_back(NextLine); + insertLineSequence(Seq, NewRows); + } + + if (!CurrRangeValid) + continue; + } + + // Ignore empty sequences. + if (Row.EndSequence && Seq.empty()) + continue; + + // Relocate row address and add it to the current sequence. + Row.Address.Address += CurrRange.value(); + Seq.emplace_back(Row); + + if (Row.EndSequence) + insertLineSequence(Seq, NewRows); + } + + // Finished extracting, now emit the line tables. + // FIXME: LLVM hard-codes its prologue values. We just copy the + // prologue over and that works because we act as both producer and + // consumer. It would be nicer to have a real configurable line + // table emitter. + if (LineTable.Prologue.getVersion() < 2 || + LineTable.Prologue.getVersion() > 5 || + LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || + LineTable.Prologue.OpcodeBase > 13) + reportWarning("line table parameters mismatch. Cannot emit.", + CU.getContaingFile()); + else { + uint32_t PrologueEnd = *StmtList + 10 + LineTable.Prologue.PrologueLength; + // DWARF v5 has an extra 2 bytes of information before the header_length + // field. + if (LineTable.Prologue.getVersion() == 5) + PrologueEnd += 2; + StringRef LineData = + CU.getContaingFile().Dwarf->getDWARFObj().getLineSection().Data; + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; + Params.DWARF2LineBase = LineTable.Prologue.LineBase; + Params.DWARF2LineRange = LineTable.Prologue.LineRange; + Emitter.emitLineTableForUnit(Params, + LineData.slice(*StmtList + 4, PrologueEnd), + LineTable.Prologue.MinInstLength, NewRows, + CU.getOrigUnit().getAddressByteSize()); + } +} + +/// Read the frame info stored in the object, and emit the +/// patched frame descriptions for the resulting file. +/// +/// This is actually pretty easy as the data of the CIEs and FDEs can +/// 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(LinkContext &Context, + RangesTy &Ranges, unsigned AddrSize, + DwarfEmitter &Emitter) { + StringRef FrameData = + Context.File.Dwarf->getDWARFObj().getFrameSection().Data; + if (FrameData.empty()) + return; + + DataExtractor Data(FrameData, Context.File.Dwarf->isLittleEndian(), 0); + uint64_t InputOffset = 0; + + // Store the data of the CIEs defined in this object, keyed by their + // offsets. + DenseMap LocalCIES; + + /// The CIEs that have been emitted in the output section. The actual CIE + /// data serves a the key to this StringMap. + StringMap EmittedCIEs; + + while (Data.isValidOffset(InputOffset)) { + uint64_t EntryOffset = InputOffset; + uint32_t InitialLength = Data.getU32(&InputOffset); + if (InitialLength == 0xFFFFFFFF) + return reportWarning("Dwarf64 bits no supported", Context.File); + + uint32_t CIEId = Data.getU32(&InputOffset); + if (CIEId == 0xFFFFFFFF) { + // This is a CIE, store it. + StringRef CIEData = FrameData.substr(EntryOffset, InitialLength + 4); + LocalCIES[EntryOffset] = CIEData; + // The -4 is to account for the CIEId we just read. + InputOffset += InitialLength - 4; + continue; + } + + uint32_t Loc = Data.getUnsigned(&InputOffset, AddrSize); + + // Some compilers seem to emit frame info that doesn't start at + // the function entry point, thus we can't just lookup the address + // in the debug map. Use the AddressInfo's range map to see if the FDE + // describes something that we can relocate. + auto Range = Ranges.upper_bound(Loc); + if (Range != Ranges.begin()) + --Range; + if (Range == Ranges.end() || Range->first > Loc || + Range->second.HighPC <= Loc) { + // The +4 is to account for the size of the InitialLength field itself. + InputOffset = EntryOffset + InitialLength + 4; + continue; + } + + // This is an FDE, and we have a mapping. + // Have we already emitted a corresponding CIE? + StringRef CIEData = LocalCIES[CIEId]; + if (CIEData.empty()) + return reportWarning("Inconsistent debug_frame content. Dropping.", + Context.File); + + uint64_t OffsetToCIERecord = Emitter.getFrameSectionSize(); + + // Look if we already emitted a CIE that corresponds to the + // referenced one (the CIE data is the key of that lookup). + auto IteratorInserted = + EmittedCIEs.insert(std::make_pair(CIEData, OffsetToCIERecord)); + OffsetToCIERecord = IteratorInserted.first->getValue(); + + // If there is no CIE yet for this ID, emit it. + if (IteratorInserted.second) + Emitter.emitCIE(CIEData); + + uint64_t OffsetToFDERecord = Emitter.getFrameSectionSize(); + // Remember offset to the FDE record, so that we might update + // field referencing CIE record, when final offsets are known. + Context.noteFDERecordToPatch( + LinkContext::FDERecordPatch(OffsetToFDERecord)); + + // Emit the FDE with updated address and CIE pointer. + // (4 + AddrSize) is the size of the CIEId + initial_location + // fields that will get reconstructed by emitFDE(). + unsigned FDERemainingBytes = InitialLength - (4 + AddrSize); + Emitter.emitFDE(OffsetToCIERecord, AddrSize, Loc + Range->second.Offset, + FrameData.substr(InputOffset, FDERemainingBytes)); + InputOffset += FDERemainingBytes; + } +} + +uint32_t DWARFLinker::DIECloner::hashFullyQualifiedName(DWARFDie DIE, + CompileUnit &U, + const DWARFFile &File, + int ChildRecurseDepth) { + const char *Name = nullptr; + DWARFUnit *OrigUnit = &U.getOrigUnit(); + CompileUnit *CU = &U; + Optional Ref; + + while (1) { + if (const char *CurrentName = DIE.getName(DINameKind::ShortName)) + Name = CurrentName; + + if (!(Ref = DIE.find(dwarf::DW_AT_specification)) && + !(Ref = DIE.find(dwarf::DW_AT_abstract_origin))) + break; + + if (!Ref->isFormClass(DWARFFormValue::FC_Reference)) + break; + + CompileUnit *RefCU; + if (auto RefDIE = + Linker.resolveDIEReference(Context, U, *Ref, DIE, RefCU)) { + CU = RefCU; + OrigUnit = &RefCU->getOrigUnit(); + DIE = RefDIE; + } + } + + unsigned Idx = OrigUnit->getDIEIndex(DIE); + if (!Name && DIE.getTag() == dwarf::DW_TAG_namespace) + Name = "(anonymous namespace)"; + + if (CU->getInfo(Idx).ParentIdx == 0 || + // FIXME: dsymutil-classic compatibility. Ignore modules. + CU->getOrigUnit().getDIEAtIndex(CU->getInfo(Idx).ParentIdx).getTag() == + dwarf::DW_TAG_module) + return djbHash(Name ? Name : "", djbHash(ChildRecurseDepth ? "" : "::")); + + DWARFDie Die = OrigUnit->getDIEAtIndex(CU->getInfo(Idx).ParentIdx); + return djbHash( + (Name ? Name : ""), + djbHash((Name ? "::" : ""), + hashFullyQualifiedName(Die, *CU, File, ++ChildRecurseDepth))); +} + +static uint64_t getDwoId(const DWARFDie &CUDie) { + if (Optional DwoId = dwarf::toUnsigned( + CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id}))) + return *DwoId; + return 0; +} + +static std::string remapPath(StringRef Path, + const objectPrefixMap &ObjectPrefixMap) { + if (ObjectPrefixMap.empty()) + return Path.str(); + + SmallString<256> p = Path; + for (const auto &Entry : ObjectPrefixMap) + if (llvm::sys::path::replace_path_prefix(p, Entry.first, Entry.second)) + break; + return p.str().str(); +} + +bool DWARFLinker::registerModuleReference(LinkContext &Context, DWARFUnit &CU, + const DWARFFile &File, + unsigned Indent, bool Quiet) { + DWARFDie CUDie = CU.getUnitDIE(); + + std::string PCMfile = dwarf::toString( + CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); + if (PCMfile.empty()) + return false; + if (Options.ObjectPrefixMap) + PCMfile = remapPath(PCMfile, *Options.ObjectPrefixMap); + + // Clang module DWARF skeleton CUs abuse this for the path to the module. + uint64_t DwoId = getDwoId(CUDie); + + std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); + if (Name.empty()) { + if (!Quiet) + reportWarning("Anonymous module skeleton CU for " + PCMfile, File); + return true; + } + + if (!Quiet && Options.Verbose) { + outs().indent(Indent); + outs() << "Found clang module reference " << PCMfile; + } + + { + std::unique_lock Guard(ClangModulesMutex); + + auto Cached = ClangModules.find(PCMfile); + if (Cached != ClangModules.end()) { + // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is + // fixed in clang, only warn about DWO_id mismatches in verbose mode. + // ASTFileSignatures will change randomly when a module is rebuilt. + if (!Quiet && Options.Verbose && (Cached->second != DwoId)) + reportWarning( + Twine("hash mismatch: this object file was built against a " + "different version of the module ") + + PCMfile, + File); + if (!Quiet && Options.Verbose) + outs() << " [cached].\n"; + return true; + } + if (!Quiet && Options.Verbose) + outs() << " ...\n"; + + // 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(Context, CU, PCMfile, Name, DwoId, File, + Indent + 2, Quiet)) { + consumeError(std::move(E)); + return false; + } + return true; +} + +Error DWARFLinker::loadClangModule(LinkContext &Context, DWARFUnit &CU, + StringRef Filename, StringRef ModuleName, + uint64_t DwoId, const DWARFFile &File, + unsigned Indent, bool Quiet) { + /// Using a SmallString<0> because loadClangModule() is recursive. + SmallString<0> Path(Options.PrependPath); + if (sys::path::is_relative(Filename)) + resolveRelativeObjectPath(Path, CU.getUnitDIE()); + sys::path::append(Path, Filename); + // Don't use the cached binary holder because we have no thread-safety + // guarantee and the lifetime is limited. + + if (Options.ObjFileLoader == nullptr) + return Error::success(); + + auto ErrOrObj = Options.ObjFileLoader(File.FileName, Path); + if (!ErrOrObj) + return Error::success(); + + std::unique_ptr Unit; + for (const auto &ChildCU : ErrOrObj->Dwarf->compile_units()) { + // Recursively get all modules imported by this one. + auto CUDie = ChildCU->getUnitDIE(false); + if (!CUDie) + continue; + if (!registerModuleReference(Context, *ChildCU, File, Indent, Quiet)) { + if (Unit) { + std::string Err = + (Filename + + ": Clang modules are expected to have exactly 1 compile unit.\n") + .str(); + reportError(Err, File); + return make_error(Err, inconvertibleErrorCode()); + } + // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is + // fixed in clang, only warn about DWO_id mismatches in verbose mode. + // ASTFileSignatures will change randomly when a module is rebuilt. + uint64_t PCMDwoId = getDwoId(CUDie); + if (PCMDwoId != DwoId) { + if (!Quiet && Options.Verbose) + reportWarning( + Twine("hash mismatch: this object file was built against a " + "different version of the module ") + + Filename, + File); + std::unique_lock Guard(ClangModulesMutex); + + // Update the cache entry with the DwoId of the module loaded from disk. + ClangModules[Filename] = PCMDwoId; + } + + if (!ChildCU->getUnitDIE().hasChildren()) + return Error::success(); + + // Add compile unit corresponding to this module. + Unit = + std::make_unique(*ChildCU, getNextUniqueUnitID(), + !Options.NoODR, ModuleName, *ErrOrObj); + Unit->setStage(CompileUnit::Stages::ModulesLoaded); + + Context.addModulesCompileUnit(std::move(Unit)); + } + } + + return Error::success(); +} + +void DWARFLinker::emitPaperTrailWarnings(CompileUnit &CU, + DwarfEmitter &Emitter) { + BumpPtrAllocator DIEAlloc; + std::vector DIEBlocks; + + CU.getInfo(0).Keep = true; + DIE *CUDie = DIE::get(DIEAlloc, dwarf::DW_TAG_compile_unit); + CUDie->setOffset(CU.getHeaderSize()); + StringRef Producer; + StringRef WarningHeader; + + std::unique_lock Guard(StringPoolMutex); + + switch (DwarfLinkerClientID) { + case DwarfLinkerClient::Dsymutil: + Producer = TheOffsetsStringPool.internString("dsymutil"); + WarningHeader = "dsymutil_warning"; + break; + + default: + Producer = TheOffsetsStringPool.internString("dwarfopt"); + WarningHeader = "dwarfopt_warning"; + break; + } + + StringRef FileName = + TheOffsetsStringPool.internString(CU.getContaingFile().FileName); + CUDie->addValue( + DIEAlloc, dwarf::DW_AT_producer, dwarf::DW_FORM_strp, + DIEInteger(TheOffsetsStringPool.getEntry(Producer).getOffset())); + DIEBlock *String = new (DIEAlloc) DIEBlock(); + DIEBlocks.push_back(String); + for (auto &C : FileName) + String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, + DIEInteger(C)); + String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, + DIEInteger(0)); + + CUDie->addValue(DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_string, String); + for (const auto &Warning : CU.getContaingFile().Warnings) { + DIE &ConstDie = CUDie->addChild(DIE::get(DIEAlloc, dwarf::DW_TAG_constant)); + ConstDie.addValue( + DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, + DIEInteger(TheOffsetsStringPool.getEntry(WarningHeader).getOffset())); + ConstDie.addValue(DIEAlloc, dwarf::DW_AT_artificial, dwarf::DW_FORM_flag, + DIEInteger(1)); + ConstDie.addValue( + DIEAlloc, dwarf::DW_AT_const_value, dwarf::DW_FORM_strp, + DIEInteger(TheOffsetsStringPool.getEntry(Warning).getOffset())); + } + unsigned Size = 4 /* FORM_strp */ + FileName.size() + 1 + + CU.getContaingFile().Warnings.size() * (4 + 1 + 4) + + 1 /* End of children */; + DIEAbbrev Abbrev = CUDie->generateAbbrev(); + CU.assignAbbrev(Abbrev); + CUDie->setAbbrevNumber(Abbrev.getNumber()); + Size += getULEB128Size(Abbrev.getNumber()); + // Abbreviation ordering needed for classic compatibility. + for (auto &Child : CUDie->children()) { + Abbrev = Child.generateAbbrev(); + CU.assignAbbrev(Abbrev); + Child.setAbbrevNumber(Abbrev.getNumber()); + Size += getULEB128Size(Abbrev.getNumber()); + } + CUDie->setSize(Size); + Emitter.emitPaperTrailWarningsDie(*CUDie); + + uint64_t UnitSize = 0; + UnitSize += CU.getHeaderSize(); + UnitSize += CUDie->getSize(); + CU.setUnitSize(UnitSize); + + Emitter.emitAbbrevs(CU.getAbbreviations(), CU.getVersion()); + + for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) + (*I)->~DIEBlock(); +} + +DWARFLinker::DIECloner::DIECloner(CompileUnit &CU, const LinkContext &Context, + DWARFLinker &Linker, raw_pwrite_stream &Out, + const DWARFLinkerOptions &Options) + : Context(Context), Linker(Linker), CU(CU), Options(Options) { + auto ReportWarn = [&](const Twine &message, StringRef, const DWARFDie *) { + Linker.reportWarning(message, CU.getContaingFile()); + }; + + if (!Linker.TheDwarfEmitter) + return; + + Emitter = std::make_unique( + OutputFileType::Object, Out, [](StringRef input) { return input; }, + ReportWarn, ReportWarn); + + if (!Emitter->init(Linker.TheDwarfEmitter->getTargetTriple())) + return; +} + +DWARFLinker::DIECloner::~DIECloner() { + // release all memory used for cloning compile unit. + for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) + (*I)->~DIEBlock(); + for (auto I = DIELocs.begin(), E = DIELocs.end(); I != E; ++I) + (*I)->~DIELoc(); + + DIEBlocks.clear(); + DIELocs.clear(); + DIEAlloc.Reset(); + + if (Emitter) + Emitter->finish(); +} + +void DWARFLinker::DIECloner::cloneCU() { + auto InputDIE = CU.getOrigUnit().getUnitDIE(); + if (!InputDIE) + return; + + if (CU.getInfo(0).Keep) { + // Clone the InputDIE into your Unit DIE in our compile unit since it + // already has a DIE inside of it. + CU.createOutputDIE(); + cloneDIE(InputDIE, 0 /* PC offset */, + CU.getHeaderSize() /* Unit Header size */, 0, + CU.getOutputUnitDIE()); + + Linker.assignDelayedOffsets(CU); + } + + if (Options.NoOutput || !CU.getInfo(0).Keep) + return; + + assert(Emitter); + + if (Options.RemoveObsoleteDebugInfo || Linker.needToTranslateStrings()) + Linker.patchLineTableForUnit(CU, *Emitter); + + if (Options.RemoveObsoleteDebugInfo) { + // Emit all the compile unit's debug information. + Emitter->emitUnitRangesEntries( + CU, static_cast(CU.getUnitRangesAttribute())); + + Linker.patchRangesForUnit(CU, *Emitter); + auto ProcessExpr = [&](StringRef Bytes, SmallVectorImpl &Buffer) { + DWARFUnit &OrigUnit = CU.getOrigUnit(); + + DataExtractor Data(Bytes, + CU.getEndianness() == support::endianness::little, + OrigUnit.getAddressByteSize()); + cloneExpression(Data, + DWARFExpression(Data, OrigUnit.getAddressByteSize(), + OrigUnit.getFormParams().Format), + Buffer); + }; + Emitter->emitLocationsForUnit(CU, *CU.getContaingFile().Dwarf, ProcessExpr); + } + + Emitter->emitAbbrevs(CU.getAbbreviations(), CU.getVersion()); + + assert(Emitter->getDebugInfoSectionSize() == + CU.getStartOffset(CompileUnit::DebugTableKind::DebugInfo)); + + Emitter->emitCompileUnitHeader(CU); + Emitter->emitDIE(*CU.getOutputUnitDIE()); + +#ifndef NDEBUG + uint64_t UnitSize = 0; + if (DIE *OutUnitDie = CU.getOutputUnitDIE()) { + UnitSize += CU.getHeaderSize() /* Header size */; + UnitSize += OutUnitDie->getSize(); + } + assert(Emitter->getDebugInfoSectionSize() == UnitSize); +#endif + + CU.setUnitSize(Emitter->getDebugInfoSectionSize()); +} + +void DWARFLinker::copyInvariantDebugSection(DWARFContext &Dwarf, + DwarfEmitter &Emitter) { + if (!needToTranslateStrings()) + Emitter.emitSectionContents(Dwarf.getDWARFObj().getLineSection().Data, + "debug_line"); + Emitter.emitSectionContents(Dwarf.getDWARFObj().getLocSection().Data, + "debug_loc"); + Emitter.emitSectionContents(Dwarf.getDWARFObj().getRangesSection().Data, + "debug_ranges"); + Emitter.emitSectionContents(Dwarf.getDWARFObj().getFrameSection().Data, + "debug_frame"); + Emitter.emitSectionContents(Dwarf.getDWARFObj().getArangesSection(), + "debug_aranges"); +} + +void DWARFLinker::addObjectFile(DWARFFile &File) { + ObjectContexts.emplace_back(std::make_unique(File)); +} + +// ------------------------------------------------------------------ +// +// The overall linking process looks like this: +// +// parrallel_for_each(ObjectFile) { +// for_each (Compile Unit) { +// 1. load Clang modules. +// } +// +// parrallel_for_each(Compile Unit) { +// 1. load input DWARF for Compile Unit. +// 2. report warnings for Clang modules. +// 3. search for type declarations. +// 4. analyze live DIEs. +// 5. clone DIEs(Generate output DIEs and resulting DWARF tables). +// The result is in OutDebugInfoBytes, which is ELF file containg +// DWARF tables corresponding for current compile unit. +// 6. cleanup Input and Output DIEs. +// } +// +// deallocate loaded Object file. +// } +// +// for_each (ObjectFile) { +// for_each (Compile Unit) { +// 1. set offsets to Compile Units DWARF tables. +// 2. patch size/offsets fields. +// 3. generate index tables. +// 4. move DWARF tables of compile units into the resulting file. +// } +// } +// +// i.e. every compile unit is processed separately, visited only once +// (except case inter-CU references exist), and used data are freed +// after the compile unit is processed. The resulting file is glued +// from generated debug tables, corresponding to separate compile units. +// +// Handling inter-CU references: inter-CU references are hard to process +// using only one pass. f.e. if CU1 references CU100 and CU100 references +// CU1, we could not finish handling of CU1 until we finished CU100. +// Thus we either need to load all CUs into the memory, either load CUs +// several times. So we discover dependencies during the first pass(so that +// depending compile units stay loaded into the memory). And then, during the +// second pass it handles inter-connected compile units. + +bool DWARFLinker::link() { + assert(Options.NoOutput || TheDwarfEmitter); + + // reset compile unit unique ID counter. + UniqueUnitID = 0; + + if (Options.Verbose && Options.Threads != 1) { + Options.Threads = 1; + reportWarning( + "set number of threads to 1 to make --verbose to work properly", ""); + } + + for (std::unique_ptr &Context : ObjectContexts) { + if (Options.Verbose) { + if (DwarfLinkerClientID == DwarfLinkerClient::Dsymutil) + outs() << "DEBUG MAP OBJECT: " << Context->File.FileName << "\n"; + else + outs() << "OBJECT FILE: " << Context->File.FileName << "\n"; + } + + if (Context->File.Dwarf) { + for (const std::unique_ptr &OrigCU : + Context->File.Dwarf->compile_units()) { + if (Options.Verbose) { + outs() << "Input compilation unit:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + OrigCU->getUnitDIE().dump(outs(), 0, DumpOpts); + } + } + } + + // count references to source files. + addBinHolderNameRef(Context->File); + } + + parallel::strategy = hardware_concurrency(Options.Threads); + + if (Options.Threads == 1) { + for (std::unique_ptr &Context : ObjectContexts) + linkObjectFile(*Context); + } else { + ThreadPool Pool(parallel::strategy); + for (std::unique_ptr &Context : ObjectContexts) { + Pool.async([&]() { linkObjectFile(*Context); }); + } + Pool.wait(); + } + + ODRContexts.clear(); + + // Assemble final file after debug info for each compile unit is generated. + glueCompileUnitsAndWriteToTheOutput(); + + return true; +} + +bool DWARFLinker::IsClonningComplete(LinkContext &Context) const { + for (const auto &CU : Context.CompileUnits) { + if (CU->getStage() == CompileUnit::Stages::Cloned) { + if (CU->hasExternalReferences()) + return false; + } else + return false; + } + + return true; +} + +void DWARFLinker::setDeclContext(const DeclContextKey *Key, + CompileUnit::DIEInfo *Info) { + if (!Key->IsInvalid) { + // Create type declaration context corresponding to the Key + // and set it to Info->Ctxt. + Info->Ctxt = ODRContexts.getChildDeclContext(*Key); + const_cast(Info->Ctxt.load()) + ->setDefinedInClangModule(Key->IsDefinedInClangModule); + assert(Info->Ctxt); + } +} + +void DWARFLinker::assignDelayedDeclContexts(CompileUnit &CU) { + std::unique_lock Guard(ContextsMutex); + + for (const CompileUnit::DeclContextKeyData &Data : CU.getDeclContextData()) + setDeclContext(Data.Key, Data.Info); +} + +void DWARFLinker::setStringsOffset(DIE::value_iterator AttrToPatch, + const char *String) { + const auto &Old = *AttrToPatch; + assert(Old.getType() == DIEValue::isInteger); + + // Put specified String into the global string pool and set it`s + // offset for the specified Attribute. + uint64_t Offset = TheOffsetsStringPool.getEntry(String).getOffset(); + *AttrToPatch = + DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(Offset)); +} + +void DWARFLinker::assignDelayedOffsets(CompileUnit &CU) { + std::unique_lock Guard(StringPoolMutex); + + for (const CompileUnit::OffsetsData &Data : CU.getStringsOffsetData()) + setStringsOffset(Data.AttrToPatch, Data.String); +} + +void DWARFLinker::linkObjectFile(LinkContext &Context) { + if (Context.File.Warnings.empty()) { + if (!Context.File.Dwarf) + return; + + // Look for modules and load them. + for (const std::unique_ptr &OrigCU : + Context.File.Dwarf->compile_units()) { + auto CUDie = OrigCU->getUnitDIE(); + + if (CUDie && Options.RemoveObsoleteDebugInfo) + registerModuleReference(Context, *OrigCU, Context.File); + } + + // Process modules compile units in parallel. + parallelForEach(Context.ModulesCompileUnits, + [&](std::unique_ptr &CU) { + processSingleCompileUnit(Context, *CU); + }); + + // Check for live relocations. If there is no any live relocation then we + // can skip entire object file. + if (Options.RemoveObsoleteDebugInfo && + !Context.File.Addresses->hasValidRelocs()) { + if (Options.Verbose) + outs() << "No valid relocations found. Skipping.\n"; + return; + } + + Context.OriginalDebugInfoSize = getDebugInfoSize(*Context.File.Dwarf); + + // Create CompileUnit structures to keep information about source + // DWARFUnit`s + for (const auto &OrigCU : Context.File.Dwarf->compile_units()) { + Context.CompileUnits.emplace_back(std::make_unique( + *OrigCU, getNextUniqueUnitID(), + !Options.NoODR && Options.RemoveObsoleteDebugInfo, "", Context.File)); + Context.CompileUnits.back()->getOrigUnit().getUnitDIE(); + }; + + // Process compile units in parallel. We might do additional passes, + // if there are unprocessed inter-CU references. + while (!IsClonningComplete(Context)) { + parallelForEach(Context.CompileUnits, + [&](std::unique_ptr &CU) { + processSingleCompileUnit(Context, *CU); + }); + } + } + + if (!Options.NoOutput) { + assert(TheDwarfEmitter); + auto ReportWarn = [&](const Twine &message, StringRef, const DWARFDie *) { + reportWarning(message, Context.File); + }; + + if (!Context.File.Warnings.empty()) { + // Write generated debug info(for warnings) into the DWARF file of + // corresponding artificial compile unit. + Context.CompileUnits.push_back(std::make_unique( + getNextUniqueUnitID(), + !Options.NoODR && Options.RemoveObsoleteDebugInfo, "", Context.File)); + + raw_svector_ostream stream(Context.CompileUnits.back()->getDwarfBits()); + + DwarfStreamer Emitter( + OutputFileType::Object, stream, [](StringRef input) { return input; }, + ReportWarn, ReportWarn); + + if (!Emitter.init(TheDwarfEmitter->getTargetTriple())) + return; + + emitPaperTrailWarnings(*Context.CompileUnits.back(), Emitter); + + Emitter.finish(); + } else if (!Options.RemoveObsoleteDebugInfo) { + // Copy invariant debug tables into the DWARF file of + // link context. + raw_svector_ostream stream(Context.OutDebugInfoBytes); + + DwarfStreamer Emitter( + OutputFileType::Object, stream, [](StringRef input) { return input; }, + ReportWarn, ReportWarn); + + if (!Emitter.init(TheDwarfEmitter->getTargetTriple())) + return; + + copyInvariantDebugSection(*Context.File.Dwarf, Emitter); + + Emitter.finish(); + } else if (!Context.CompileUnits.empty()) { + // Generate debug info for .debug_frame and write it into the DWARF + // file of link context. + raw_svector_ostream stream(Context.OutDebugInfoBytes); + + DwarfStreamer Emitter( + OutputFileType::Object, stream, [](StringRef input) { return input; }, + ReportWarn, ReportWarn); + + if (!Emitter.init(TheDwarfEmitter->getTargetTriple())) + return; + RangesTy *Ranges; + + Context.File.Addresses->getValidAddressRanges(Ranges); + + patchFrameInfoForObject( + Context, *Ranges, + Context.CompileUnits[0]->getOrigUnit().getAddressByteSize(), Emitter); + + Emitter.finish(); + } + } + + // we could not simply unload object file here. + // Object file might be a member of an archive. + // Unloading object file in such case would also + // unload containing archive. Thus we count references + // to the archive and unload it when there are no + // more unprocessed archive members. + unloadObjectFile(Context.File); +} + +static bool isArchive(StringRef Name) { return Name.endswith(")"); } + +static StringRef getBinaryHolderName(StringRef Name) { + if (isArchive(Name)) + return Name.substr(0, Name.rfind('(')); + else + return Name; +} + +void DWARFLinker::addBinHolderNameRef(DWARFFile &File) { + BinaryHolderRefCounter[getBinaryHolderName(File.FileName)]++; +} + +void DWARFLinker::unloadObjectFile(DWARFFile &File) { + std::unique_lock Guard(BinaryHolderRefMutex); + + assert(BinaryHolderRefCounter[getBinaryHolderName(File.FileName)] != 0); + + BinaryHolderRefCounter[getBinaryHolderName(File.FileName)]--; + + if (BinaryHolderRefCounter[getBinaryHolderName(File.FileName)] == 0) + File.unload(); +} + +void DWARFLinker::processSingleCompileUnit(LinkContext &Context, + CompileUnit &CU) { + CU.initIteration(); + + // load input Dies for CU. + if (CU.getStage() != CompileUnit::Stages::Cloned) { + CU.getOrigUnit().getUnitDIE(false); + + // pre-load compile units line table. + if (CU.getContaingFile().Dwarf) { + std::unique_lock Guard(Context.LineTablesMutex); + CU.getContaingFile().Dwarf->getLineTableForUnit(&CU.getOrigUnit()); + } + } + + while (CU.getStage() != CompileUnit::Stages::Cloned) { + switch (CU.getStage()) { + case CompileUnit::Stages::Created: { + DWARFDie CUDie = CU.getOrigUnit().getUnitDIE(); + if (!CUDie) + CU.setStage(CompileUnit::Stages::LivenessAnalysisDone); + else + CU.setStage(CompileUnit::Stages::SrcDWARFLoaded); + } break; + + case CompileUnit::Stages::SrcDWARFLoaded: { + if (Options.RemoveObsoleteDebugInfo) { + // The registerModuleReference() condition effectively skips + // over fully resolved skeleton units. This second pass of + // registerModuleReferences doesn't do any new work, but it + // will collect top-level errors, which are suppressed. Module + // warnings were already displayed in the first iteration. + if (registerModuleReference(Context, CU.getOrigUnit(), + CU.getContaingFile(), 0, true)) { + CU.setStage(CompileUnit::Stages::Cloned); + return; + } + CU.setStage(CompileUnit::Stages::ModulesLoaded); + } else + CU.setStage(CompileUnit::Stages::TypesDeclarationsFound); + } break; + + case CompileUnit::Stages::ModulesLoaded: { + bool HasUnresolvedRefs = false; + analyzeContextInfo(Context, CU.getOrigUnit().getUnitDIE(), 0, CU, + Options.ParseableSwiftInterfaces, HasUnresolvedRefs); + if (HasUnresolvedRefs) { + // if analyzeContextInfo set true for HasUnresolvedRefs then this + // compile unit has inter-CU reference to the compile unit which is not + // analyzed yet. Stop processing of current unit until referenced + // unit would be analyzed. + + return; + } + assignDelayedDeclContexts(CU); + CU.setStage(CompileUnit::Stages::TypesDeclarationsFound); + } break; + + case CompileUnit::Stages::TypesDeclarationsFound: { + // Then mark all the DIEs that need to be present in the generated 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 (!Options.RemoveObsoleteDebugInfo || CU.isClangModule()) { + CU.markEverythingAsKept(); + } else { + RangesTy *Ranges; + CU.getContaingFile().Addresses->getValidAddressRanges(Ranges); + if (!lookForDIEsToKeep(Context, CU.getOrigUnit().getUnitDIE(false), CU, + 0)) { + // if lookForDIEsToKeep returned false then this compile unit + // has inter-CU reference to the compile unit which is not + // analyzed yet. Stop processing of current unit until referenced + // unit would be analyzed. + + return; + } + } + + CU.setStage(CompileUnit::Stages::LivenessAnalysisDone); + } break; + + case CompileUnit::Stages::LivenessAnalysisDone: { + if (CU.isClangModule() || !Options.RemoveObsoleteDebugInfo || + CU.getContaingFile().Addresses->hasValidRelocs()) { + raw_svector_ostream stream(CU.getDwarfBits()); + + DIECloner(CU, Context, *this, stream, Options).cloneCU(); + } + + CU.setStage(CompileUnit::Stages::Cloned); + } break; + + case CompileUnit::Stages::Cloned: + assert(false); + } + } + + CU.cleanupDataAfterClonning(); +} + +void DWARFLinker::patchCompileUnit(CompileUnit &CU, StringRef Data) { + assert(Data.size() >= sizeof(int32_t)); + + // Update the compile unit size. + { + char *PatchLocation = const_cast(Data.data()); + uint64_t PatchValue = CU.getUnitSize() - 4; + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + } + + // Update offset into the abbreviation table. + { + char *PatchLocation = + const_cast(Data.data() + (CU.getVersion() >= 5 ? 8 : 6)); + uint64_t PatchValue = + CU.getStartOffset(CompileUnit::DebugTableKind::DebugAbbrev); + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + } + + // Update Dies references. + for (const CompileUnit::DieRefAttrPatchInfo &Ref : CU.getDiesReferences()) { + if (!Ref.RefCU) + continue; + + const CompileUnit::DIEInfo &RefDieInfo = Ref.RefCU->getInfo(Ref.RefDieIdx); + + uint64_t Offset = RefDieInfo.ClonedDieOffset; + if (!Ref.IsCULocal) + Offset += + Ref.RefCU->getStartOffset(CompileUnit::DebugTableKind::DebugInfo); + + char *PatchLocation = const_cast(Data.data() + Ref.OffsetToAttr); + uint64_t PatchValue = Offset; + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + }; + + if (CU.isArtificialCU() || !Options.RemoveObsoleteDebugInfo) + return; + + // Update the cloned DW_AT_stmt_list with the correct debug_line offset. + if (Optional Offset = CU.getOffsetToStmtList()) { + assert(Data.size() >= (*Offset + sizeof(int32_t))); + char *PatchLocation = const_cast(Data.data() + *Offset); + uint64_t PatchValue = + CU.getStartOffset(CompileUnit::DebugTableKind::DebugLine); + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + } + + // Update compile unit DW_AT_range attribute. + Optional Offsets = + CU.getUnitRangesAttribute(); + if (Offsets) { + assert(Data.size() >= (Offsets->OffsetToAttr + sizeof(int32_t))); + char *PatchLocation = + const_cast(Data.data() + Offsets->OffsetToAttr); + uint64_t PatchValue = + CU.getStartOffset(CompileUnit::DebugTableKind::DebugRange); + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + } + + // Update Dies DW_AT_range attributes. + for (const auto &RangeOffset : CU.getRangesAttributes()) { + char *PatchLocation = + const_cast(Data.data() + RangeOffset.OffsetToAttr); + uint64_t PatchValue = + RangeOffset.CULocalOffsetValue + + CU.getStartOffset(CompileUnit::DebugTableKind::DebugRange); + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + } + + // Update Dies DW_AT_location attributes. + for (const CompileUnit::LocationAttributePatchInfo &LocPatch : + CU.getLocationAttributes()) { + char *PatchLocation = + const_cast(Data.data() + LocPatch.OffsetToAttr); + uint64_t PatchValue = + LocPatch.CULocalOffsetValue + + CU.getStartOffset(CompileUnit::DebugTableKind::DebugLoc); + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); + } +} + +void DWARFLinker::patchAranges(CompileUnit &CU, StringRef Data) { + if (CU.isArtificialCU()) + return; + + const int OffsetToCompileUnitField = sizeof(uint32_t) + sizeof(uint16_t); + assert(Data.size() >= (OffsetToCompileUnitField + sizeof(int32_t))); + + char *PatchLocation = + const_cast(Data.data() + OffsetToCompileUnitField); + uint64_t PatchValue = + CU.getStartOffset(CompileUnit::DebugTableKind::DebugInfo); + support::endian::write32(PatchLocation, PatchValue, CU.getEndianness()); +} + +void DWARFLinker::patchFrames(LinkContext &Context, StringRef Data) { + for (LinkContext::FDERecordPatch Patch : Context.getFDERecordsPatches()) { + // 4 is the offset from the start of FDE record to the field referencing CIE + // record. + uint64_t OffsetToTheFieldReferencingCIE = Patch.OffsetToFDERecord + 4; + char *PatchLocation = + const_cast(Data.data() + OffsetToTheFieldReferencingCIE); + + uint64_t CieOffsetValue = + support::endian::read32(PatchLocation, Context.getEndianness()); + + uint64_t PatchValue = + CieOffsetValue + + Context.getStartOffset(CompileUnit::DebugTableKind::DebugFrame); + support::endian::write32(PatchLocation, PatchValue, + Context.getEndianness()); + } +} + +/// Enumerates all debug_info sections from \p Obj and calls +/// \p Handler for each of them. +void enumerateDebugSections( + object::ObjectFile &Obj, + std::function Handler) { + for (const object::SectionRef &Sect : Obj.sections()) { + Expected SectNameOrErr = Sect.getName(); + if (!SectNameOrErr) + consumeError(SectNameOrErr.takeError()); + + if (!Sect.isDebugSection(*SectNameOrErr)) + continue; + + Expected Data = Sect.getContents(); + if (!Data) + consumeError(SectNameOrErr.takeError()); + StringRef Name = + SectNameOrErr->substr(SectNameOrErr->find_first_not_of("._z")); + Handler(Name, *Data); + } +} + +void DWARFLinker::glueCompileUnitsAndWriteToTheOutput() { + if (Options.NoOutput) + return; + + // For each object file map how many bytes were emitted. + StringMap SizeByObject; + + // Go through all object files, all compile units and assign + // offsets to them. + assignOffsets(SizeByObject); + + // Patch size/offsets fields according to the assigned offsets. + patchOffsetsAndSizes(); + + // Build accelerator tables. + buildAcceleratorTables(); + + // Write debug tables from all object files/compile units into the + // resulting file. + writeDWARFToTheOutput(); + + if (Options.Statistics) + printStatistic(SizeByObject); +} + +void DWARFLinker::printStatistic(const StringMap &SizeByObject) { + // Create a vector sorted in descending order by output size. + std::vector> Sorted; + for (auto &E : SizeByObject) + Sorted.emplace_back(E.first(), E.second); + llvm::sort(Sorted, [](auto &LHS, auto &RHS) { + return LHS.second.Output > RHS.second.Output; + }); + + auto ComputePercentange = [](int64_t Input, int64_t Output) -> float { + const float Difference = Output - Input; + const float Sum = Input + Output; + if (Sum == 0) + return 0; + return (Difference / (Sum / 2)); + }; + + int64_t InputTotal = 0; + int64_t OutputTotal = 0; + const char *FormatStr = "{0,-45} {1,10}b {2,10}b {3,8:P}\n"; + + // Print header. + outs() << ".debug_info section size (in bytes)\n"; + outs() << "----------------------------------------------------------------" + "---------------\n"; + outs() << "Filename Object " + " dSYM Change\n"; + outs() << "----------------------------------------------------------------" + "---------------\n"; + + // Print body. + for (auto &E : Sorted) { + InputTotal += E.second.Input; + OutputTotal += E.second.Output; + llvm::outs() << formatv( + FormatStr, sys::path::filename(E.first).take_back(45), E.second.Input, + E.second.Output, ComputePercentange(E.second.Input, E.second.Output)); + } + // Print total and footer. + outs() << "----------------------------------------------------------------" + "---------------\n"; + llvm::outs() << formatv(FormatStr, "Total", InputTotal, OutputTotal, + ComputePercentange(InputTotal, OutputTotal)); + outs() << "----------------------------------------------------------------" + "---------------\n\n"; +} + +void DWARFLinker::assignOffsets(StringMap &SizeByObject) { + std::array TableOffsets = {0}; + uint64_t DebugInfoSizeInTheStartOfContext = 0; + + for (const std::unique_ptr &Context : ObjectContexts) { + DebugInfoSizeInTheStartOfContext = TableOffsets[static_cast< + typename std::underlying_type::type>( + CompileUnit::DebugTableKind::DebugInfo)]; + + assignOffsets(*Context, TableOffsets); + + for (std::unique_ptr &CU : Context->ModulesCompileUnits) + assignOffsets(*CU, TableOffsets); + + for (std::unique_ptr &CU : Context->CompileUnits) + assignOffsets(*CU, TableOffsets); + + uint64_t DebugInfoSizeInTheEndOfContext = TableOffsets[static_cast< + typename std::underlying_type::type>( + CompileUnit::DebugTableKind::DebugInfo)]; + + SizeByObject[Context->File.FileName].Input = Context->OriginalDebugInfoSize; + SizeByObject[Context->File.FileName].Output = + (DebugInfoSizeInTheEndOfContext - DebugInfoSizeInTheStartOfContext); + } +} + +void DWARFLinker::assignOffsets( + LinkContext &Context, + std::array &TableOffsets) { + for (size_t i = 0; i < TableOffsets.size(); i++) + Context.setStartOffset(static_cast(i), + TableOffsets[i]); + + if (Context.OutDebugInfoBytes.empty()) + return; + + MemoryBufferRef Mem(Context.OutDebugInfoBytes, "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + return; + } + + // track sizes of the tables of current unit. + enumerateDebugSections(**Obj, [&](StringRef Name, StringRef Data) { + if (Optional TableKind = + CompileUnit::parseDebugTableName(Name)) { + TableOffsets[static_cast::type>(*TableKind)] += Data.size(); + } + }); +} + +void DWARFLinker::assignOffsets( + CompileUnit &CU, + std::array &TableOffsets) { + // set offsets to the current unit. + for (size_t i = 0; i < TableOffsets.size(); i++) + CU.setStartOffset(static_cast(i), + TableOffsets[i]); + + if (CU.getDwarfBits().empty()) + return; + + MemoryBufferRef Mem(CU.getDwarfBits(), "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + return; + } + + // track sizes of the tables of current unit. + enumerateDebugSections(**Obj, [&](StringRef Name, StringRef Data) { + if (Optional TableKind = + CompileUnit::parseDebugTableName(Name)) { + TableOffsets[static_cast::type>(*TableKind)] += Data.size(); + } + }); +} + +void DWARFLinker::patchOffsetsAndSizes() { + if (Options.NoOutput) + return; + + // TODO: offsets might be patched in parrallel + for (const std::unique_ptr &Context : ObjectContexts) { + patchOffsetsAndSizes(*Context); + + for (std::unique_ptr &CU : Context->ModulesCompileUnits) + patchOffsetsAndSizes(*CU); + + for (std::unique_ptr &CU : Context->CompileUnits) + patchOffsetsAndSizes(*CU); + } +} + +void DWARFLinker::patchOffsetsAndSizes(LinkContext &Context) { + MemoryBufferRef Mem(Context.OutDebugInfoBytes, "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + return; + } + + enumerateDebugSections(**Obj, [&](StringRef Name, StringRef Data) { + if (Name == "debug_frame") + patchFrames(Context, Data); + }); +} + +void DWARFLinker::patchOffsetsAndSizes(CompileUnit &CU) { + if (CU.getDwarfBits().empty()) + return; + + MemoryBufferRef Mem(CU.getDwarfBits(), "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + return; + } + + enumerateDebugSections(**Obj, [&](StringRef Name, StringRef Data) { + if (Name == "debug_info") + patchCompileUnit(CU, Data); + else if (Name == "debug_aranges") + patchAranges(CU, Data); + }); +} + +void DWARFLinker::buildAcceleratorTables() { + for (const std::unique_ptr &Context : ObjectContexts) { + for (std::unique_ptr &CU : Context->ModulesCompileUnits) + buildAcceleratorTables(*CU); + + for (std::unique_ptr &CU : Context->CompileUnits) + buildAcceleratorTables(*CU); + } +} + +void DWARFLinker::buildAcceleratorTables(CompileUnit &CU) { + // TODO: index tables might be generated in parallel. + if (Options.GenerateAppleNamespaces) + for (const auto &Namespace : CU.getNamespaces()) + AppleNamespaces.addName( + TheOffsetsStringPool.getEntry(Namespace.Name.getString()), + Namespace.OutOffset + + CU.getStartOffset(CompileUnit::DebugTableKind::DebugInfo)); + + if (Options.GenerateAppleNames) + for (const auto &Pubname : CU.getPubnames()) + AppleNames.addName( + TheOffsetsStringPool.getEntry(Pubname.Name.getString()), + Pubname.OutOffset + + CU.getStartOffset(CompileUnit::DebugTableKind::DebugInfo)); + + if (Options.GenerateAppleTypes) + for (const auto &Pubtype : CU.getPubtypes()) + AppleTypes.addName( + TheOffsetsStringPool.getEntry(Pubtype.Name.getString()), + Pubtype.OutOffset + + CU.getStartOffset(CompileUnit::DebugTableKind::DebugInfo), + Pubtype.Tag, + Pubtype.ObjcClassImplementation ? dwarf::DW_FLAG_type_implementation + : 0, + Pubtype.QualifiedNameHash); + + if (Options.GenerateAppleObjC) + for (const auto &ObjC : CU.getObjC()) + AppleObjc.addName( + TheOffsetsStringPool.getEntry(ObjC.Name.getString()), + ObjC.OutOffset + + CU.getStartOffset(CompileUnit::DebugTableKind::DebugInfo)); + + if (Options.GeneratePubNames && TheDwarfEmitter) + TheDwarfEmitter->emitPubNamesForUnit(CU); + + if (Options.GeneratePubTypes && TheDwarfEmitter) + TheDwarfEmitter->emitPubTypesForUnit(CU); + + if (Options.GenerateDebugNames) { + for (const auto &Namespace : CU.getNamespaces()) + DebugNames.addName( + TheOffsetsStringPool.getEntry(Namespace.Name.getString()), + Namespace.OutOffset, Namespace.Tag, CU.getUniqueID()); + for (const auto &Pubname : CU.getPubnames()) + DebugNames.addName( + TheOffsetsStringPool.getEntry(Pubname.Name.getString()), + Pubname.OutOffset, Pubname.Tag, CU.getUniqueID()); + for (const auto &Pubtype : CU.getPubtypes()) + DebugNames.addName( + TheOffsetsStringPool.getEntry(Pubtype.Name.getString()), + Pubtype.OutOffset, Pubtype.Tag, CU.getUniqueID()); + } + + CU.cleanupAcceleratorTables(); +} + +void DWARFLinker::writeDWARFToTheOutput() { + bool HasAbbreviations = false; + + for (const std::unique_ptr &Context : ObjectContexts) { + writeDWARFToTheOutput(*Context); + + for (std::unique_ptr &CU : Context->ModulesCompileUnits) + writeDWARFToTheOutput(*CU, HasAbbreviations); + + for (std::unique_ptr &CU : Context->CompileUnits) + writeDWARFToTheOutput(*CU, HasAbbreviations); + } + + // generate empty abbrev + if (!HasAbbreviations) { + std::vector> Abbreviations; + TheDwarfEmitter->emitAbbrevs(Abbreviations, 3); + } + + // put sections common to all compilation units. + TheDwarfEmitter->emitStrings(TheOffsetsStringPool); + + if (Options.GenerateAppleNamespaces) + TheDwarfEmitter->emitAppleNamespaces(AppleNamespaces); + + if (Options.GenerateAppleNames) + TheDwarfEmitter->emitAppleNames(AppleNames); + + if (Options.GenerateAppleTypes) + TheDwarfEmitter->emitAppleTypes(AppleTypes); + + if (Options.GenerateAppleObjC) + TheDwarfEmitter->emitAppleObjc(AppleObjc); + + if (Options.GenerateDebugNames) + TheDwarfEmitter->emitDebugNames(DebugNames); +} + +void DWARFLinker::writeDWARFToTheOutput(CompileUnit &CU, + bool &HasAbbreviations) { + if (CU.getDwarfBits().empty()) + return; + + if (!HasAbbreviations && CU.getAbbreviations().size() > 0) + HasAbbreviations = true; + + MemoryBufferRef Mem(CU.getDwarfBits(), "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + return; + } + + // now glue sections and put them into output DwarfEmitter. + enumerateDebugSections(**Obj, [&](StringRef Name, StringRef Data) { + TheDwarfEmitter->emitSectionContents(Data, Name); + }); + + CU.eraseDwarfBits(); +} + +void DWARFLinker::writeDWARFToTheOutput(LinkContext &Context) { + if (Context.OutDebugInfoBytes.empty()) + return; + + MemoryBufferRef Mem(Context.OutDebugInfoBytes, "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + return; + } + + // now glue sections and put them into output DwarfEmitter. + enumerateDebugSections(**Obj, [&](StringRef Name, StringRef Data) { + TheDwarfEmitter->emitSectionContents(Data, Name); + }); + + Context.OutDebugInfoBytes = SmallString<0>(); +} + +} // end of namespace dwarflinker + +} // namespace llvm diff --git a/llvm/lib/DWARFLinkerNext/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinkerNext/DWARFLinkerCompileUnit.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerNext/DWARFLinkerCompileUnit.cpp @@ -0,0 +1,329 @@ +//===- DWARFLinkerCompileUnit.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h" +#include "llvm/DWARFLinkerNext/DWARFLinker.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h" + +namespace llvm { + +namespace dwarflinker { + +/// 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; +} + +CompileUnit::CompileUnit(unsigned ID, bool, StringRef ClangModuleName, + DWARFFile &File) + : ID(ID), Ranges(RangeAlloc), ClangModuleName(ClangModuleName), + ContaingFile(File), Stage(Stages::Created) { + Info.resize(1); + Version = 3; +} + +CompileUnit::CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, + StringRef ClangModuleName, DWARFFile &File) + : OrigUnit(&OrigUnit), ID(ID), Ranges(RangeAlloc), + ClangModuleName(ClangModuleName), ContaingFile(File), + Stage(Stages::Created) { + auto CUDie = OrigUnit.getUnitDIE(); + if (!CUDie) + return; + + if (File.Dwarf) + Endianess = File.Dwarf->isLittleEndian() ? support::endianness::little + : support::endianness::big; + + Version = OrigUnit.getVersion(); + + if (auto Lang = dwarf::toUnsigned(CUDie.find(dwarf::DW_AT_language))) + HasODR = CanUseODR && (*Lang == dwarf::DW_LANG_C_plus_plus || + *Lang == dwarf::DW_LANG_C_plus_plus_03 || + *Lang == dwarf::DW_LANG_C_plus_plus_11 || + *Lang == dwarf::DW_LANG_C_plus_plus_14 || + *Lang == dwarf::DW_LANG_ObjC_plus_plus); + + DeclContextData.reserve(1000); + StringOffsetsData.reserve(1000); +} + +uint16_t CompileUnit::getLanguage() { + if (!Language) { + DWARFDie CU = getOrigUnit().getUnitDIE(); + Language = dwarf::toUnsigned(CU.find(dwarf::DW_AT_language), 0); + } + return Language; +} + +StringRef CompileUnit::getSysRoot() { + if (SysRoot.empty()) { + DWARFDie CU = getOrigUnit().getUnitDIE(); + SysRoot = dwarf::toStringRef(CU.find(dwarf::DW_AT_LLVM_sysroot)).str(); + } + return SysRoot; +} + +void CompileUnit::noteDieReference(CompileUnit::DieRefAttrPatchInfo Data) { + DIEReferences.emplace_back(Data); +} + +std::vector CompileUnit::getDiesReferences() { + return std::move(DIEReferences); +} + +void CompileUnit::initIteration() { + // if exist external references to this compile unit, then we need to + // begin processing for this unit again, starting from TypesDeclarationsFound + // stage. + if (getStage() == CompileUnit::Stages::Cloned) { + if (hasExternalReferences()) + setStage(CompileUnit::Stages::TypesDeclarationsFound); + else + return; + } + Info.resize(getOrigUnit().getNumDIEs()); + + eraseDwarfBits(); + OutDebugInfoBytes.reserve(4000); + + if (getStage() < Stages::LivenessAnalysisDone) { + Ranges.clear(); + Labels.clear(); + } + + if (getStage() < Stages::Cloned) { + Pubnames.clear(); + Pubtypes.clear(); + Namespaces.clear(); + ObjC.clear(); + RangeAttributes.clear(); + UnitRangeAttribute.reset(); + LocationAttributes.clear(); + } +} + +void CompileUnit::cleanupDataAfterClonning() { + assert(DeclContextData.size() == 0); + assert(StringOffsetsData.size() == 0); + + NewUnit.reset(); + ResolvedFullPaths.shrink_and_clear(); + ResolvedParentPaths.clear(); + Keys.clear(); + Allocator.Reset(); + + if (!needToKeepInputDies() && OrigUnit) + (*OrigUnit)->clear(); + + AbbreviationsSet.clear(); +} + +void CompileUnit::cleanupAcceleratorTables() { + Pubnames.clear(); + Pubnames.shrink_to_fit(); + + Pubtypes.clear(); + Pubtypes.shrink_to_fit(); + + Namespaces.clear(); + Namespaces.shrink_to_fit(); + + ObjC.clear(); + ObjC.shrink_to_fit(); + + Strings.clear(); +} + +void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { + Labels.insert({LabelLowPc, PcOffset}); +} + +void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, + int64_t PcOffset) { + // Don't add empty ranges to the interval map. They are a problem because + // the interval map expects half open intervals. This is safe because they + // are empty anyway. + if (FuncHighPc != FuncLowPc) + Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); + this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); + this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); +} + +void CompileUnit::noteRangeAttribute(const DIE &Die, + RangeAttributePatchInfo AttrOutOffsets) { + if (Die.getTag() != dwarf::DW_TAG_compile_unit) + RangeAttributes.push_back(AttrOutOffsets); + else + UnitRangeAttribute = AttrOutOffsets; +} + +void CompileUnit::noteLocationAttribute(uint64_t AttrOutOffset, uint64_t Offset, + int64_t PcOffset) { + LocationAttributes.emplace_back( + LocationAttributePatchInfo{AttrOutOffset, Offset, PcOffset}); +} + +void CompileUnit::markEverythingAsKept() { + unsigned Idx = 0; + + for (auto &I : Info) { + // Mark everything that wasn't explicit marked for pruning. + I.Keep = !I.Prune; + auto DIE = getOrigUnit().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 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() > getOrigUnit().getAddressByteSize() && + (*Block)[0] == dwarf::DW_OP_addr) + I.InDebugMap = true; + } + } +} + +void CompileUnit::addNamespaceAccelerator(const DIE *Die, StringRef Name) { + Namespaces.emplace_back(Strings.getEntry(Name), Die->getTag(), + Die->getOffset()); +} + +void CompileUnit::addObjCAccelerator(const DIE *Die, StringRef Name, + bool SkipPubSection) { + ObjC.emplace_back(Strings.getEntry(Name), Die->getTag(), Die->getOffset(), + SkipPubSection); +} + +void CompileUnit::addNameAccelerator(const DIE *Die, StringRef Name, + bool SkipPubSection) { + Pubnames.emplace_back(Strings.getEntry(Name), Die->getTag(), Die->getOffset(), + SkipPubSection); +} + +void CompileUnit::addTypeAccelerator(const DIE *Die, StringRef Name, + bool ObjcClassImplementation, + uint32_t QualifiedNameHash) { + Pubtypes.emplace_back(Strings.getEntry(Name), Die->getTag(), Die->getOffset(), + QualifiedNameHash, ObjcClassImplementation); +} + +Optional +CompileUnit::parseDebugTableName(StringRef Name) { + if (Name == "debug_info") + return CompileUnit::DebugTableKind::DebugInfo; + else if (Name == "debug_line") + return CompileUnit::DebugTableKind::DebugLine; + else if (Name == "debug_frame") + return CompileUnit::DebugTableKind::DebugFrame; + else if (Name == "debug_ranges") + return CompileUnit::DebugTableKind::DebugRange; + else if (Name == "debug_loc") + return CompileUnit::DebugTableKind::DebugLoc; + else if (Name == "debug_aranges") + return CompileUnit::DebugTableKind::DebugARanges; + else if (Name == "debug_abbrev") + return CompileUnit::DebugTableKind::DebugAbbrev; + + return None; +} + +void CompileUnit::assignAbbrev(DIEAbbrev &Abbrev) { + // Check the set for priors. + FoldingSetNodeID ID; + Abbrev.Profile(ID); + void *InsertToken; + + DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken); + // If it's newly added. + if (InSet) { + // Assign existing abbreviation number. + Abbrev.setNumber(InSet->getNumber()); + } else { + // Add to abbreviation list. + Abbreviations.push_back( + std::make_unique(Abbrev.getTag(), Abbrev.hasChildren())); + for (const auto &Attr : Abbrev.getData()) + Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm()); + AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken); + // Assign the unique abbreviation number. + Abbrev.setNumber(Abbreviations.size()); + Abbreviations.back()->setNumber(Abbreviations.size()); + } +} + +Optional CompileUnit::getFileName(unsigned FileIdx) { + if (const auto *LT = + getOrigUnit().getContext().getLineTableForUnit(&getOrigUnit())) { + if (LT->hasFileAtIndex(FileIdx)) { + + // Cache the resolved paths based on the index in the line table, + // because calling realpath is expensive. + ResolvedPathsMap::const_iterator It = ResolvedFullPaths.find(FileIdx); + if (It == ResolvedFullPaths.end()) { + std::string OrigFileName; + bool FoundFileName = LT->getFileNameByIndex( + FileIdx, getOrigUnit().getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + OrigFileName); + (void)FoundFileName; + assert(FoundFileName && "Must get file name from line table"); + + // Second level of caching, this time based on the file's parent + // path. + StringRef FileName = sys::path::filename(OrigFileName); + StringRef ParentPath = sys::path::parent_path(OrigFileName); + + // If the ParentPath has not yet been resolved, resolve and cache it for + // future look-ups. + StringMap::iterator ParentIt = + ResolvedParentPaths.find(ParentPath); + if (ParentIt == ResolvedParentPaths.end()) { + SmallString<256> RealPath; + sys::fs::real_path(ParentPath, RealPath); + ParentIt = ResolvedParentPaths + .insert({ParentPath, std::string(RealPath.c_str(), + RealPath.size())}) + .first; + } + + // Join the file name again with the resolved path. + SmallString<256> ResolvedPath(ParentIt->second); + sys::path::append(ResolvedPath, FileName); + + It = ResolvedFullPaths + .insert(std::make_pair(FileIdx, + Strings.internString(ResolvedPath))) + .first; + } + + return It->second; + } + } + + return None; +} + +} // end of namespace dwarflinker + +} // namespace llvm diff --git a/llvm/lib/DWARFLinkerNext/DWARFLinkerDeclContext.cpp b/llvm/lib/DWARFLinkerNext/DWARFLinkerDeclContext.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerNext/DWARFLinkerDeclContext.cpp @@ -0,0 +1,305 @@ +//===- DWARFLinkerDeclContext.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/DWARFLinkerNext/DWARFLinker.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +namespace llvm { + +namespace dwarflinker { + +// DieNameResolver is a helper class which searches for type name +// and checks for unresolved inter-CU references. +class DieNameResolver { + const LinkContext &Context; + CompileUnit &CU; + const DWARFDie &DIE; + bool IsUnresolved = false; + +public: + DieNameResolver(const LinkContext &Context, CompileUnit &CU, + const DWARFDie &DIE) + : Context(Context), CU(CU), DIE(DIE) {} + + StringRef resolve() { + // Check for linkage name. + if (Optional Val = findRecursively( + {dwarf::DW_AT_MIPS_linkage_name, dwarf::DW_AT_linkage_name})) { + if (IsUnresolved) + return StringRef(); + + if (Optional String = dwarf::toString(*Val)) + return StringRef(*String); + } + + // Check for short name. + if (Optional Val = findRecursively(dwarf::DW_AT_name)) { + if (IsUnresolved) + return StringRef(); + + if (Optional String = dwarf::toString(*Val)) + return StringRef(*String); + } + + return StringRef(); + } + + bool isUnresolved() const { return IsUnresolved; } + +protected: + DWARFDie getAttributeValueAsReferencedDie(dwarf::Attribute Attr) { + if (Optional F = DIE.find(Attr)) + return getAttributeValueAsReferencedDie(*F); + return DWARFDie(); + } + + DWARFDie getAttributeValueAsReferencedDie(const DWARFFormValue &V) { + if (auto SpecRef = V.getAsRelativeReference()) { + if (SpecRef->Unit) + return SpecRef->Unit->getDIEForOffset(SpecRef->Unit->getOffset() + + SpecRef->Offset); + + if (CompileUnit *RefCU = Context.getUnitForOffset(CU, SpecRef->Offset)) { + if (RefCU->getStage() < CompileUnit::Stages::SrcDWARFLoaded) { + RefCU->setKeepInputDies(); + IsUnresolved = true; + return DWARFDie(); + } + + return RefCU->getOrigUnit().getDIEForOffset(SpecRef->Offset); + } + } + return DWARFDie(); + } + + Optional findRecursively(ArrayRef Attrs) { + SmallVector Worklist; + Worklist.push_back(DIE); + + // Keep track if DIEs already seen to prevent infinite recursion. + // Empirically we rarely see a depth of more than 3 when dealing with valid + // DWARF. This corresponds to following the DW_AT_abstract_origin and + // DW_AT_specification just once. + SmallSet Seen; + Seen.insert(DIE); + + while (!Worklist.empty()) { + DWARFDie Die = Worklist.pop_back_val(); + + if (!Die.isValid()) + continue; + + if (auto Value = Die.find(Attrs)) + return Value; + + if (auto D = getAttributeValueAsReferencedDie( + llvm::dwarf::DW_AT_abstract_origin)) { + if (IsUnresolved) + return None; + + if (Seen.insert(D).second) + Worklist.push_back(D); + } + + if (auto D = getAttributeValueAsReferencedDie( + llvm::dwarf::DW_AT_specification)) { + if (IsUnresolved) + return None; + + if (Seen.insert(D).second) + Worklist.push_back(D); + } + } + + return None; + } +}; + +DeclContextKey *DeclContextTree::getChildDeclContextKey( + const LinkContext &Context, DeclContextKey *ContextKey, const DWARFDie &DIE, + CompileUnit &CU, bool DefinedInClangModule, bool &IsUnresolved) { + assert(ContextKey); + + IsUnresolved = false; + unsigned Tag = DIE.getTag(); + + // FIXME: dsymutil-classic compat: We should bail out here if we + // have a specification or an abstract_origin. We will get the + // parent context wrong here. + + switch (Tag) { + default: + // By default stop gathering child contexts. + return nullptr; + case dwarf::DW_TAG_module: + break; + case dwarf::DW_TAG_compile_unit: { + ContextKey->IsInvalid = true; + return ContextKey; + } + case dwarf::DW_TAG_subprogram: + // Do not unique anything inside CU local functions. + if ((ContextKey->Tag == dwarf::DW_TAG_namespace || + ContextKey->Tag == dwarf::DW_TAG_compile_unit) && + !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0)) + return nullptr; + LLVM_FALLTHROUGH; + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_typedef: + // Artificial things might be ambiguous, because they might be created on + // demand. For example implicitly defined constructors are ambiguous + // because of the way we identify contexts, and they won't be generated + // every time everywhere. + if (dwarf::toUnsigned(DIE.find(dwarf::DW_AT_artificial), 0)) + return nullptr; + break; + } + + StringRef NameRef; + StringRef FileRef; + + DieNameResolver NameResolver(Context, CU, DIE); + NameRef = NameResolver.resolve(); + + if (NameResolver.isUnresolved()) { + // DIE references other compile unit, which is not loaded yet. + // Stop type declaration analyzing for now. + CU.setKeepInputDies(); + IsUnresolved = true; + return nullptr; + } + + bool IsAnonymousNamespace = NameRef.empty() && Tag == dwarf::DW_TAG_namespace; + if (IsAnonymousNamespace) { + // FIXME: For dsymutil-classic compatibility. I think uniquing within + // anonymous namespaces is wrong. There is no ODR guarantee there. + NameRef = "(anonymous namespace)"; + } + + if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type && + Tag != dwarf::DW_TAG_union_type && + Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty()) + return nullptr; + + unsigned Line = 0; + unsigned ByteSize = std::numeric_limits::max(); + + if (!DefinedInClangModule) { + // Gather some discriminating data about the DeclContext we will be + // creating: File, line number and byte size. This shouldn't be necessary, + // because the ODR is just about names, but given that we do some + // approximations with overloaded functions and anonymous namespaces, use + // these additional data points to make the process safer. + // + // This is disabled for clang modules, because forward declarations of + // module-defined types do not have a file and line. + ByteSize = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_byte_size), + std::numeric_limits::max()); + if (Tag != dwarf::DW_TAG_namespace || IsAnonymousNamespace) { + if (unsigned FileNum = + dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) { + // FIXME: dsymutil-classic compatibility. I'd rather not + // unique anything in anonymous namespaces, but if we do, then + // verify that the file and line correspond. + if (IsAnonymousNamespace) + FileNum = 1; + + if (Optional FileName = CU.getFileName(FileNum)) { + Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0); + FileRef = *FileName; + } + } + } + } + + if (!Line && NameRef.empty()) + return nullptr; + + // We hash NameRef, which is the mangled name, in order to get most + // overloaded functions resolve correctly. + // + // Strictly speaking, hashing the Tag is only necessary for a + // DW_TAG_module, to prevent uniquing of a module and a namespace + // with the same name. + // + // FIXME: dsymutil-classic won't unique the same type presented + // once as a struct and once as a class. Using the Tag in the fully + // qualified name hash to get the same effect. + unsigned Hash = hash_combine(ContextKey->Hash, Tag, NameRef); + + // FIXME: dsymutil-classic compatibility: when we don't have a name, + // use the filename. + if (IsAnonymousNamespace) + Hash = hash_combine(Hash, FileRef); + + bool IsInvalid = false; + // FIXME: dsymutil-classic compatibility. Union types aren't + // uniques, but their children might be. + if ((Tag == dwarf::DW_TAG_subprogram && + ContextKey->Tag != dwarf::DW_TAG_structure_type && + ContextKey->Tag != dwarf::DW_TAG_class_type) || + (Tag == dwarf::DW_TAG_union_type)) { + IsInvalid = true; + } + + DeclContextKey NewKey(Hash, Line, ByteSize, Tag, NameRef, FileRef, + ContextKey->Hash, IsInvalid, DefinedInClangModule); + return CU.insertKey(NewKey); +} + +DeclContext * +DeclContextTree::getChildDeclContext(const DeclContextKey &ContextKey) { + DeclContext Key(ContextKey); + auto ContextIter = Contexts.find(&Key); + + if (ContextIter == Contexts.end()) { + // The context wasn't found. + // We need to store ContextKey.Name and ContextKey.File in the StringPool + // since lifetime of DeclContextTree is longer than lifetime + // of ContextKey which points to CU local data. + bool Inserted; + DeclContext *NewContext = new (Allocator) + DeclContext(ContextKey.Hash, ContextKey.Line, ContextKey.ByteSize, + ContextKey.Tag, StringPool.internString(ContextKey.Name), + StringPool.internString(ContextKey.File), + ContextKey.ParentQualifiedNameHash); + std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); + assert(Inserted && "Failed to insert DeclContext"); + (void)Inserted; + } else if (ContextKey.Tag != dwarf::DW_TAG_namespace) { + /// In the current implementation, we don't handle overloaded functions + /// well, because the argument types are not taken into account when + /// computing the DeclContext tree. + /// + /// Some of this is mitigated byt using mangled names that do contain the + /// arguments types, but sometimes (e.g. with function templates) we don't + /// have that. In that case, just do not unique anything that refers to the + /// contexts we are not able to distinguish. + /// + /// If a context that is not a namespace appears twice in the same CU, we + /// know it is ambiguous. + /// + /// TODO: implement building unambiguous type declarations. + } + assert(ContextIter != Contexts.end()); + + return *ContextIter; +} + +} // end of namespace dwarflinker + +} // namespace llvm diff --git a/llvm/lib/DWARFLinkerNext/DWARFStreamer.cpp b/llvm/lib/DWARFLinkerNext/DWARFStreamer.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerNext/DWARFStreamer.cpp @@ -0,0 +1,797 @@ +//===- DwarfStreamer.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DWARFLinkerNext/DWARFStreamer.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetOptions.h" + +namespace llvm { + +namespace dwarflinker { + +bool DwarfStreamer::init(Triple TheTriple) { + std::string ErrorStr; + std::string TripleName; + StringRef Context = "dwarf streamer init"; + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); + if (!TheTarget) + return error(ErrorStr, Context), false; + TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return error(Twine("no register info for target ") + TripleName, Context), + false; + + MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + if (!MAI) + return error("no asm info for target " + TripleName, Context), false; + + MOFI.reset(new MCObjectFileInfo); + MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); + MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC); + + MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return error("no subtarget info for target " + TripleName, Context), false; + + MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions); + if (!MAB) + return error("no asm backend for target " + TripleName, Context), false; + + MII.reset(TheTarget->createMCInstrInfo()); + if (!MII) + return error("no instr info info for target " + TripleName, Context), false; + + MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); + if (!MCE) + return error("no code emitter for target " + TripleName, Context), false; + + switch (OutFileType) { + case OutputFileType::Assembly: { + MIP = TheTarget->createMCInstPrinter(TheTriple, MAI->getAssemblerDialect(), + *MAI, *MII, *MRI); + MS = TheTarget->createAsmStreamer( + *MC, std::make_unique(OutFile), true, true, MIP, + std::unique_ptr(MCE), std::unique_ptr(MAB), + true); + break; + } + case OutputFileType::Object: { + MS = TheTarget->createMCObjectStreamer( + TheTriple, *MC, std::unique_ptr(MAB), + MAB->createObjectWriter(OutFile), std::unique_ptr(MCE), + *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false); + break; + } + } + + if (!MS) + return error("no object streamer for target " + TripleName, Context), false; + + // Finally create the AsmPrinter we'll use to emit the DIEs. + TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(), + None)); + if (!TM) + return error("no target machine for target " + TripleName, Context), false; + + Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr(MS))); + if (!Asm) + return error("no asm printer for target " + TripleName, Context), false; + + RangesSectionSize = 0; + LocSectionSize = 0; + LineSectionSize = 0; + FrameSectionSize = 0; + DebugInfoSectionSize = 0; + + return true; +} + +void DwarfStreamer::finish() { MS->Finish(); } + +void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + MC->setDwarfVersion(DwarfVersion); +} + +/// Emit the compilation unit header for \p Unit in the debug_info section. +/// +/// A Dwarf 4 section header is encoded as: +/// uint32_t Unit length (omitting this field) +/// uint16_t Version +/// uint32_t Abbreviation table offset +/// uint8_t Address size +/// +/// Leading to a total of 11 bytes. +/// +/// A Dwarf 5 section header is encoded as: +/// uint32_t Unit length (omitting this field) +/// uint16_t Version +/// uint8_t Unit type +/// uint8_t Address size +/// uint32_t Abbreviation table offset +/// Leading to a total of 12 bytes. +void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { + unsigned Version = Unit.getVersion(); + switchToDebugInfoSection(Version); + + /// The start of the unit within its section. + Unit.setLabelBegin(Asm->createTempSymbol("cu_begin")); + Asm->OutStreamer->emitLabel(Unit.getLabelBegin()); + + // Emit size of content not including length itself. The size has already + // been computed in CompileUnit::computeOffsets(). Subtract 4 to that size to + // account for the length field. + Asm->emitInt32(-4); + Asm->emitInt16(Version); + + if (Version >= 5) { + Asm->emitInt8(dwarf::DW_UT_compile); + Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize()); + // We share one abbreviations table across all units so it's always at the + // start of the section. + Asm->emitInt32(0); + } else { + // We share one abbreviations table across all units so it's always at the + // start of the section. + Asm->emitInt32(0); + Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize()); + } + DebugInfoSectionSize += Unit.getHeaderSize(); + + // Remember this CU. + EmittedUnits.push_back({Unit.getUniqueID(), Unit.getLabelBegin()}); +} + +/// Emit the \p Abbrevs array as the shared abbreviation table +/// for the linked Dwarf file. +void DwarfStreamer::emitAbbrevs( + const std::vector> &Abbrevs, + unsigned DwarfVersion) { + MS->SwitchSection(MOFI->getDwarfAbbrevSection()); + MC->setDwarfVersion(DwarfVersion); + Asm->emitDwarfAbbrevs(Abbrevs); +} + +/// Recursively emit the DIE tree rooted at \p Die. +void DwarfStreamer::emitDIE(DIE &Die) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + Asm->emitDwarfDIE(Die); + DebugInfoSectionSize += Die.getSize(); +} + +/// Emit contents of section SecName From Obj. +void DwarfStreamer::emitSectionContents(StringRef SecData, StringRef SecName) { + MCSection *Section = + StringSwitch(SecName) + .Case("debug_info", MC->getObjectFileInfo()->getDwarfInfoSection()) + .Case("debug_line", MC->getObjectFileInfo()->getDwarfLineSection()) + .Case("debug_loc", MC->getObjectFileInfo()->getDwarfLocSection()) + .Case("debug_ranges", + MC->getObjectFileInfo()->getDwarfRangesSection()) + .Case("debug_frame", MC->getObjectFileInfo()->getDwarfFrameSection()) + .Case("debug_aranges", + MC->getObjectFileInfo()->getDwarfARangesSection()) + .Case("debug_abbrev", + MC->getObjectFileInfo()->getDwarfAbbrevSection()) + .Default(nullptr); + + if (Section) { + MS->SwitchSection(Section); + + MS->emitBytes(SecData); + } +} + +/// Emit DIE containing warnings. +void DwarfStreamer::emitPaperTrailWarningsDie(DIE &Die) { + switchToDebugInfoSection(/* Version */ 2); + + Asm->emitInt32(11 + Die.getSize() - 4); + Asm->emitInt16(2); + Asm->emitInt32(0); + Asm->emitInt8(MOFI->getTargetTriple().isArch64Bit() ? 8 : 4); + + DebugInfoSectionSize += 11; + emitDIE(Die); +} + +/// Emit the debug_str section stored in \p Pool. +void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection()); + std::vector Entries = Pool.getEntriesForEmission(); + for (auto Entry : Entries) { + // Emit the string itself. + Asm->OutStreamer->emitBytes(Entry.getString()); + // Emit a null terminator. + Asm->emitInt8(0); + } +} + +void DwarfStreamer::emitDebugNames( + AccelTable &Table) { + if (EmittedUnits.empty()) + return; + + // Build up data structures needed to emit this section. + std::vector CompUnits; + DenseMap UniqueIdToCuMap; + unsigned Id = 0; + for (auto &CU : EmittedUnits) { + CompUnits.push_back(CU.LabelBegin); + // We might be omitting CUs, so we need to remap them. + UniqueIdToCuMap[CU.ID] = Id++; + } + + Asm->OutStreamer->SwitchSection(MOFI->getDwarfDebugNamesSection()); + emitDWARF5AccelTable( + Asm.get(), Table, CompUnits, + [&UniqueIdToCuMap](const DWARF5AccelTableStaticData &Entry) { + return UniqueIdToCuMap[Entry.getCUIndex()]; + }); +} + +void DwarfStreamer::emitAppleNamespaces( + AccelTable &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelNamespaceSection()); + auto *SectionBegin = Asm->createTempSymbol("namespac_begin"); + Asm->OutStreamer->emitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "namespac", SectionBegin); +} + +void DwarfStreamer::emitAppleNames( + AccelTable &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelNamesSection()); + auto *SectionBegin = Asm->createTempSymbol("names_begin"); + Asm->OutStreamer->emitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "names", SectionBegin); +} + +void DwarfStreamer::emitAppleObjc( + AccelTable &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelObjCSection()); + auto *SectionBegin = Asm->createTempSymbol("objc_begin"); + Asm->OutStreamer->emitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "objc", SectionBegin); +} + +void DwarfStreamer::emitAppleTypes( + AccelTable &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelTypesSection()); + auto *SectionBegin = Asm->createTempSymbol("types_begin"); + Asm->OutStreamer->emitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "types", SectionBegin); +} + +/// Emit the swift_ast section stored in \p Buffers. +void DwarfStreamer::emitSwiftAST(StringRef Buffer) { + MCSection *SwiftASTSection = MOFI->getDwarfSwiftASTSection(); + SwiftASTSection->setAlignment(Align(32)); + MS->SwitchSection(SwiftASTSection); + MS->emitBytes(Buffer); +} + +/// Emit the debug_range section contents for \p FuncRange by +/// translating the original \p Entries. The debug_range section +/// format is totally trivial, consisting just of pairs of address +/// sized addresses describing the ranges. +void DwarfStreamer::emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector &Entries, + unsigned AddressSize) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + + // Offset each range by the right amount. + int64_t PcOffset = Entries.empty() ? 0 : FuncRange.value() + UnitPcOffset; + for (const auto &Range : Entries) { + if (Range.isBaseAddressSelectionEntry(AddressSize)) { + warn("unsupported base address selection operation", + "emitting debug_ranges"); + break; + } + // Do not emit empty ranges. + if (Range.StartAddress == Range.EndAddress) + continue; + + // All range entries should lie in the function range. + if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && + Range.EndAddress + OrigLowPc <= FuncRange.stop())) + warn("inconsistent range data.", "emitting debug_ranges"); + MS->emitIntValue(Range.StartAddress + PcOffset, AddressSize); + MS->emitIntValue(Range.EndAddress + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->emitIntValue(0, AddressSize); + MS->emitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// Emit the debug_aranges contribution of a unit and +/// if \p DoDebugRanges is true the debug_range contents for a +/// compile_unit level DW_AT_ranges attribute (Which are basically the +/// same thing with a different base address). +/// Just aggregate all the ranges gathered inside that unit. +void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit, + bool DoDebugRanges) { + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + // Gather the ranges in a vector, so that we can simplify them. The + // IntervalMap will have coalesced the non-linked ranges, but here + // we want to coalesce the linked addresses. + std::vector> Ranges; + const auto &FunctionRanges = Unit.getFunctionRanges(); + + for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); + Range != End; ++Range) + Ranges.push_back(std::make_pair(Range.start() + Range.value(), + Range.stop() + Range.value())); + + // The object addresses where sorted, but again, the linked + // addresses might end up in a different order. + llvm::sort(Ranges); + + if (!Ranges.empty()) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); + + MCSymbol *BeginLabel = Asm->createTempSymbol("Barange"); + MCSymbol *EndLabel = Asm->createTempSymbol("Earange"); + + unsigned HeaderSize = + sizeof(int32_t) + // Size of contents (w/o this field + sizeof(int16_t) + // DWARF ARange version number + sizeof(int32_t) + // Offset of CU in the .debug_info section + sizeof(int8_t) + // Pointer Size (in bytes) + sizeof(int8_t); // Segment Size (in bytes) + + unsigned TupleSize = AddressSize * 2; + unsigned Padding = offsetToAlignment(HeaderSize, Align(TupleSize)); + + Asm->emitLabelDifference(EndLabel, BeginLabel, 4); // Arange length + Asm->OutStreamer->emitLabel(BeginLabel); + Asm->emitInt16(dwarf::DW_ARANGES_VERSION); // Version number + Asm->emitInt32(Unit.getStartOffset( + CompileUnit::DebugTableKind::DebugInfo)); // Corresponding unit's offset + Asm->emitInt8(AddressSize); // Address size + Asm->emitInt8(0); // Segment size + + Asm->OutStreamer->emitFill(Padding, 0x0); + + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; + ++Range) { + uint64_t RangeStart = Range->first; + MS->emitIntValue(RangeStart, AddressSize); + while ((Range + 1) != End && Range->second == (Range + 1)->first) + ++Range; + MS->emitIntValue(Range->second - RangeStart, AddressSize); + } + + // Emit terminator + Asm->OutStreamer->emitIntValue(0, AddressSize); + Asm->OutStreamer->emitIntValue(0, AddressSize); + Asm->OutStreamer->emitLabel(EndLabel); + } + + if (!DoDebugRanges) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + // Offset each range by the right amount. + int64_t PcOffset = -Unit.getLowPc(); + // Emit coalesced ranges. + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { + MS->emitIntValue(Range->first + PcOffset, AddressSize); + while (Range + 1 != End && Range->second == (Range + 1)->first) + ++Range; + MS->emitIntValue(Range->second + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->emitIntValue(0, AddressSize); + MS->emitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// Emit location lists for \p Unit and update attributes to point to the new +/// entries. +void DwarfStreamer::emitLocationsForUnit( + CompileUnit &Unit, DWARFContext &Dwarf, + std::function &)> ProcessExpr) { + auto &Attributes = Unit.getLocationAttributes(); + + if (Attributes.empty()) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + uint64_t BaseAddressMarker = (AddressSize == 8) + ? std::numeric_limits::max() + : std::numeric_limits::max(); + const DWARFSection &InputSec = Dwarf.getDWARFObj().getLocSection(); + DataExtractor Data(InputSec.Data, Dwarf.isLittleEndian(), AddressSize); + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + auto OrigUnitDie = OrigUnit.getUnitDIE(false); + int64_t UnitPcOffset = 0; + if (auto OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc))) + UnitPcOffset = int64_t(*OrigLowPc) - Unit.getLowPc(); + + SmallVector Buffer; + for (auto &Attr : Attributes) { + uint64_t Offset = Attr.CULocalOffsetValue; + Attr.CULocalOffsetValue = LocSectionSize; + // This is the quantity to add to the old location address to get + // the correct address for the new one. + int64_t LocPcOffset = Attr.PCValue + UnitPcOffset; + while (Data.isValidOffset(Offset)) { + uint64_t Low = Data.getUnsigned(&Offset, AddressSize); + uint64_t High = Data.getUnsigned(&Offset, AddressSize); + LocSectionSize += 2 * AddressSize; + // End of list entry. + if (Low == 0 && High == 0) { + Asm->OutStreamer->emitIntValue(0, AddressSize); + Asm->OutStreamer->emitIntValue(0, AddressSize); + break; + } + // Base address selection entry. + if (Low == BaseAddressMarker) { + Asm->OutStreamer->emitIntValue(BaseAddressMarker, AddressSize); + Asm->OutStreamer->emitIntValue(High + Attr.PCValue, AddressSize); + LocPcOffset = 0; + continue; + } + // Location list entry. + Asm->OutStreamer->emitIntValue(Low + LocPcOffset, AddressSize); + Asm->OutStreamer->emitIntValue(High + LocPcOffset, AddressSize); + uint64_t Length = Data.getU16(&Offset); + Asm->OutStreamer->emitIntValue(Length, 2); + // Copy the bytes into to the buffer, process them, emit them. + Buffer.reserve(Length); + Buffer.resize(0); + StringRef Input = InputSec.Data.substr(Offset, Length); + ProcessExpr(Input, Buffer); + Asm->OutStreamer->emitBytes( + StringRef((const char *)Buffer.data(), Length)); + Offset += Length; + LocSectionSize += Length + 2; + } + } +} + +void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, + unsigned MinInstLength, + std::vector &Rows, + unsigned PointerSize) { + // Switch to the section where the table will be emitted into. + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + MCSymbol *LineStartSym = MC->createTempSymbol(); + MCSymbol *LineEndSym = MC->createTempSymbol(); + + // The first 4 bytes is the total length of the information for this + // compilation unit (not including these 4 bytes for the length). + Asm->emitLabelDifference(LineEndSym, LineStartSym, 4); + Asm->OutStreamer->emitLabel(LineStartSym); + // Copy Prologue. + MS->emitBytes(PrologueBytes); + LineSectionSize += PrologueBytes.size() + 4; + + SmallString<128> EncodingBuffer; + raw_svector_ostream EncodingOS(EncodingBuffer); + + if (Rows.empty()) { + // We only have the dummy entry, dsymutil emits an entry with a 0 + // address in that case. + MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits::max(), 0, + EncodingOS); + MS->emitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + MS->emitLabel(LineEndSym); + return; + } + + // Line table state machine fields + unsigned FileNum = 1; + unsigned LastLine = 1; + unsigned Column = 0; + unsigned IsStatement = 1; + unsigned Isa = 0; + uint64_t Address = -1ULL; + + unsigned RowsSinceLastSequence = 0; + + for (unsigned Idx = 0; Idx < Rows.size(); ++Idx) { + auto &Row = Rows[Idx]; + + int64_t AddressDelta; + if (Address == -1ULL) { + MS->emitIntValue(dwarf::DW_LNS_extended_op, 1); + MS->emitULEB128IntValue(PointerSize + 1); + MS->emitIntValue(dwarf::DW_LNE_set_address, 1); + MS->emitIntValue(Row.Address.Address, PointerSize); + LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); + AddressDelta = 0; + } else { + AddressDelta = (Row.Address.Address - Address) / MinInstLength; + } + + // FIXME: code copied and transformed from MCDwarf.cpp::EmitDwarfLineTable. + // We should find a way to share this code, but the current compatibility + // requirement with classic dsymutil makes it hard. Revisit that once this + // requirement is dropped. + + if (FileNum != Row.File) { + FileNum = Row.File; + MS->emitIntValue(dwarf::DW_LNS_set_file, 1); + MS->emitULEB128IntValue(FileNum); + LineSectionSize += 1 + getULEB128Size(FileNum); + } + if (Column != Row.Column) { + Column = Row.Column; + MS->emitIntValue(dwarf::DW_LNS_set_column, 1); + MS->emitULEB128IntValue(Column); + LineSectionSize += 1 + getULEB128Size(Column); + } + + // FIXME: We should handle the discriminator here, but dsymutil doesn't + // consider it, thus ignore it for now. + + if (Isa != Row.Isa) { + Isa = Row.Isa; + MS->emitIntValue(dwarf::DW_LNS_set_isa, 1); + MS->emitULEB128IntValue(Isa); + LineSectionSize += 1 + getULEB128Size(Isa); + } + if (IsStatement != Row.IsStmt) { + IsStatement = Row.IsStmt; + MS->emitIntValue(dwarf::DW_LNS_negate_stmt, 1); + LineSectionSize += 1; + } + if (Row.BasicBlock) { + MS->emitIntValue(dwarf::DW_LNS_set_basic_block, 1); + LineSectionSize += 1; + } + + if (Row.PrologueEnd) { + MS->emitIntValue(dwarf::DW_LNS_set_prologue_end, 1); + LineSectionSize += 1; + } + + if (Row.EpilogueBegin) { + MS->emitIntValue(dwarf::DW_LNS_set_epilogue_begin, 1); + LineSectionSize += 1; + } + + int64_t LineDelta = int64_t(Row.Line) - LastLine; + if (!Row.EndSequence) { + MCDwarfLineAddr::Encode(*MC, Params, LineDelta, AddressDelta, EncodingOS); + MS->emitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + Address = Row.Address.Address; + LastLine = Row.Line; + RowsSinceLastSequence++; + } else { + if (LineDelta) { + MS->emitIntValue(dwarf::DW_LNS_advance_line, 1); + MS->emitSLEB128IntValue(LineDelta); + LineSectionSize += 1 + getSLEB128Size(LineDelta); + } + if (AddressDelta) { + MS->emitIntValue(dwarf::DW_LNS_advance_pc, 1); + MS->emitULEB128IntValue(AddressDelta); + LineSectionSize += 1 + getULEB128Size(AddressDelta); + } + MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits::max(), + 0, EncodingOS); + MS->emitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + Address = -1ULL; + LastLine = FileNum = IsStatement = 1; + RowsSinceLastSequence = Column = Isa = 0; + } + } + + if (RowsSinceLastSequence) { + MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits::max(), 0, + EncodingOS); + MS->emitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + } + + MS->emitLabel(LineEndSym); +} + +/// Copy the debug_line over to the updated binary while unobfuscating the file +/// names and directories. +void DwarfStreamer::translateLineTable(DataExtractor Data, uint64_t Offset) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + StringRef Contents = Data.getData(); + + // We have to deconstruct the line table header, because it contains to + // length fields that will need to be updated when we change the length of + // the files and directories in there. + unsigned UnitLength = Data.getU32(&Offset); + uint64_t UnitEnd = Offset + UnitLength; + MCSymbol *BeginLabel = MC->createTempSymbol(); + MCSymbol *EndLabel = MC->createTempSymbol(); + unsigned Version = Data.getU16(&Offset); + + if (Version > 5) { + warn("Unsupported line table version: dropping contents and not " + "unobfsucating line table."); + return; + } + + Asm->emitLabelDifference(EndLabel, BeginLabel, 4); + Asm->OutStreamer->emitLabel(BeginLabel); + Asm->emitInt16(Version); + LineSectionSize += 6; + + MCSymbol *HeaderBeginLabel = MC->createTempSymbol(); + MCSymbol *HeaderEndLabel = MC->createTempSymbol(); + Asm->emitLabelDifference(HeaderEndLabel, HeaderBeginLabel, 4); + Asm->OutStreamer->emitLabel(HeaderBeginLabel); + Offset += 4; + LineSectionSize += 4; + + uint64_t AfterHeaderLengthOffset = Offset; + // Skip to the directories. + Offset += (Version >= 4) ? 5 : 4; + unsigned OpcodeBase = Data.getU8(&Offset); + Offset += OpcodeBase - 1; + Asm->OutStreamer->emitBytes(Contents.slice(AfterHeaderLengthOffset, Offset)); + LineSectionSize += Offset - AfterHeaderLengthOffset; + + // Offset points to the first directory. + while (const char *Dir = Data.getCStr(&Offset)) { + if (Dir[0] == 0) + break; + + StringRef Translated = Translator(Dir); + Asm->OutStreamer->emitBytes(Translated); + Asm->emitInt8(0); + LineSectionSize += Translated.size() + 1; + } + Asm->emitInt8(0); + LineSectionSize += 1; + + while (const char *File = Data.getCStr(&Offset)) { + if (File[0] == 0) + break; + + StringRef Translated = Translator(File); + Asm->OutStreamer->emitBytes(Translated); + Asm->emitInt8(0); + LineSectionSize += Translated.size() + 1; + + uint64_t OffsetBeforeLEBs = Offset; + Asm->emitULEB128(Data.getULEB128(&Offset)); + Asm->emitULEB128(Data.getULEB128(&Offset)); + Asm->emitULEB128(Data.getULEB128(&Offset)); + LineSectionSize += Offset - OffsetBeforeLEBs; + } + Asm->emitInt8(0); + LineSectionSize += 1; + + Asm->OutStreamer->emitLabel(HeaderEndLabel); + + // Copy the actual line table program over. + Asm->OutStreamer->emitBytes(Contents.slice(Offset, UnitEnd)); + LineSectionSize += UnitEnd - Offset; + + Asm->OutStreamer->emitLabel(EndLabel); + Offset = UnitEnd; +} + +/// Emit the pubnames or pubtypes section contribution for \p +/// Unit into \p Sec. The data is provided in \p Names. +void DwarfStreamer::emitPubSectionForUnit( + MCSection *Sec, StringRef SecName, const CompileUnit &Unit, + const std::vector &Names) { + if (Names.empty()) + return; + + // Start the dwarf pubnames section. + Asm->OutStreamer->SwitchSection(Sec); + MCSymbol *BeginLabel = Asm->createTempSymbol("pub" + SecName + "_begin"); + MCSymbol *EndLabel = Asm->createTempSymbol("pub" + SecName + "_end"); + + bool HeaderEmitted = false; + // Emit the pubnames for this compilation unit. + for (const auto &Name : Names) { + if (Name.SkipPubSection) + continue; + + if (!HeaderEmitted) { + // Emit the header. + Asm->emitLabelDifference(EndLabel, BeginLabel, 4); // Length + Asm->OutStreamer->emitLabel(BeginLabel); + Asm->emitInt16(dwarf::DW_PUBNAMES_VERSION); // Version + Asm->emitInt32(Unit.getStartOffset( + CompileUnit::DebugTableKind::DebugInfo)); // Unit offset + Asm->emitInt32(Unit.getUnitSize()); // Size + HeaderEmitted = true; + } + Asm->emitInt32(Name.OutOffset); + + // Emit the string itself. + Asm->OutStreamer->emitBytes(Name.Name.getString()); + // Emit a null terminator. + Asm->emitInt8(0); + } + + if (!HeaderEmitted) + return; + Asm->emitInt32(0); // End marker. + Asm->OutStreamer->emitLabel(EndLabel); +} + +/// Emit .debug_pubnames for \p Unit. +void DwarfStreamer::emitPubNamesForUnit(const CompileUnit &Unit) { + emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubNamesSection(), + "names", Unit, Unit.getPubnames()); +} + +/// Emit .debug_pubtypes for \p Unit. +void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { + emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubTypesSection(), + "types", Unit, Unit.getPubtypes()); +} + +/// Emit a CIE into the debug_frame section. +void DwarfStreamer::emitCIE(StringRef CIEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->emitBytes(CIEBytes); + FrameSectionSize += CIEBytes.size(); +} + +/// Emit a FDE into the debug_frame section. \p FDEBytes +/// contains the FDE data without the length, CIE offset and address +/// which will be replaced with the parameter values. +void DwarfStreamer::emitFDE(uint32_t CIEOffset, uint32_t AddrSize, + uint32_t Address, StringRef FDEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->emitIntValue(FDEBytes.size() + 4 + AddrSize, 4); + MS->emitIntValue(CIEOffset, 4); + MS->emitIntValue(Address, AddrSize); + MS->emitBytes(FDEBytes); + FrameSectionSize += FDEBytes.size() + 8 + AddrSize; +} + +} // end of namespace dwarflinker + +} // namespace llvm diff --git a/llvm/test/tools/dsymutil/X86/basic-linking-x86.test b/llvm/test/tools/dsymutil/X86/basic-linking-x86.test --- a/llvm/test/tools/dsymutil/X86/basic-linking-x86.test +++ b/llvm/test/tools/dsymutil/X86/basic-linking-x86.test @@ -12,7 +12,7 @@ CHECK: debug_info contents -CHECK: Compile Unit: +CHECK: 0x[[CU1_OFFSET:[0-9a-f]*]]: Compile Unit: CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") @@ -26,7 +26,7 @@ CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") CHECK: DW_AT_decl_line (23) CHECK: DW_AT_prototyped (0x01) -CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_type (0x[[INT_OFFSET:[0-9a-f]*]] CHECK: DW_AT_external (0x01) CHECK: DW_AT_accessibility (DW_ACCESS_public) CHECK: DW_AT_low_pc (0x0000000100000ea0) @@ -36,13 +36,13 @@ CHECK: DW_AT_name ("argc") CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") CHECK: DW_AT_decl_line (23) -CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_type (0x[[INT_OFFSET]] CHECK: DW_AT_location (DW_OP_fbreg -8) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("argv") CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") CHECK: DW_AT_decl_line (23) -CHECK: DW_AT_type (0x0000006a +CHECK: DW_AT_type (0x[[CONSTCHARPTR_PTR_OFFSET:[0-9a-f]*]] CHECK: DW_AT_location (DW_OP_fbreg -16) CHECK: NULL CHECK: DW_TAG_base_type @@ -50,18 +50,18 @@ CHECK: DW_AT_encoding (DW_ATE_signed) CHECK: DW_AT_byte_size (0x04) CHECK: DW_TAG_pointer_type -CHECK: DW_AT_type (0x0000006f +CHECK: DW_AT_type (0x[[CONSTCHARPTR_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_pointer_type -CHECK: DW_AT_type (0x00000074 +CHECK: DW_AT_type (0x[[CONSTCHAR_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_const_type -CHECK: DW_AT_type (0x00000079 +CHECK: DW_AT_type (0x[[CHAR_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_base_type CHECK: DW_AT_name ("char") CHECK: DW_AT_encoding (DW_ATE_signed_char) CHECK: DW_AT_byte_size (0x01) CHECK: NULL -CHECK: Compile Unit: +CHECK: 0x[[CU2_OFFSET:[0-9a-f]*]]: Compile Unit: CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") @@ -73,36 +73,36 @@ CHECK: DW_AT_name ("int") CHECK: DW_TAG_variable CHECK: DW_AT_name ("private_int") -CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_type (0x[[PRIVATE_INT_OFFSET:[0-9a-f]*]] CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") BASIC: DW_AT_location (DW_OP_addr 0x100001008) ARCHIVE: DW_AT_location (DW_OP_addr 0x100001004) CHECK: DW_TAG_variable CHECK: DW_AT_name ("baz") -CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_type (0x[[PRIVATE_INT_OFFSET]] CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") CHECK: DW_AT_location (DW_OP_addr 0x100001000) CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("foo") CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") -CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_type (0x[[PRIVATE_INT_OFFSET]] CHECK: DW_AT_low_pc (0x0000000100000ed0) CHECK: DW_AT_high_pc (0x0000000100000f19) CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("arg") -CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_type (0x[[PRIVATE_INT_OFFSET]] CHECK: DW_AT_location (DW_OP_fbreg -4) CHECK: NULL CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("inc") -CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_type (0x[[PRIVATE_INT_OFFSET]] CHECK: DW_AT_low_pc (0x0000000100000f20) CHECK: DW_AT_high_pc (0x0000000100000f37) CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) CHECK: NULL -CHECK: Compile Unit: +CHECK: 0x[[CU3_OFFSET:[0-9a-f]*]]: Compile Unit: CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") @@ -112,28 +112,28 @@ CHECK: DW_AT_low_pc (0x0000000100000f40) CHECK: DW_TAG_variable CHECK: DW_AT_name ("val") -CHECK: DW_AT_type (0x00000162 +CHECK: DW_AT_type (0x[[VAL_OFFSET:[0-9a-f]*]] CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c") BASIC: DW_AT_location (DW_OP_addr 0x100001004) ARCHIVE: DW_AT_location (DW_OP_addr 0x100001008) CHECK: DW_TAG_volatile_type -CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_type (0x[[VOLATILE_INT_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_base_type CHECK: DW_AT_name ("int") CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("bar") -CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_type (0x[[VOLATILE_INT_OFFSET]] CHECK: DW_AT_low_pc (0x0000000100000f40) CHECK: DW_AT_high_pc (0x0000000100000f84) CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("arg") -CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_type (0x[[VOLATILE_INT_OFFSET]] CHECK: DW_AT_location (DW_OP_fbreg -8) CHECK: NULL CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("inc") -CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_type (0x[[VOLATILE_INT_OFFSET]] CHECK: DW_AT_low_pc (0x0000000100000f90) CHECK: DW_AT_high_pc (0x0000000100000fa9) CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) @@ -143,12 +143,12 @@ CHECK-NOT: .debug_loc contents CHECK:.debug_aranges contents: -CHECK-NEXT:Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT:Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU1_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT:[0x0000000100000ea0, 0x0000000100000ec4) -CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x00000081, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU2_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT:[0x0000000100000ed0, 0x0000000100000f19) CHECK-NEXT:[0x0000000100000f20, 0x0000000100000f37) -CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x00000126, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU3_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT:[0x0000000100000f40, 0x0000000100000f84) CHECK-NEXT:[0x0000000100000f90, 0x0000000100000fa9) diff --git a/llvm/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test b/llvm/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test --- a/llvm/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test +++ b/llvm/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test @@ -4,7 +4,7 @@ CHECK: debug_info contents -CHECK: Compile Unit: {{.*}} version = 0x0004 +CHECK: 0x[[CU1_OFFSET:[0-9a-f]*]]: Compile Unit: {{.*}} version = 0x0004 CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("clang version 3.7.0 ") CHECK: DW_AT_language (DW_LANG_C99) @@ -20,20 +20,20 @@ CHECK: DW_AT_name ("main") CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") CHECK: DW_AT_prototyped (true) -CHECK: DW_AT_type (0x00000000000000a1 +CHECK: DW_AT_type (0x[[SUBPROGRAM_OFFSET:[0-9a-f]*]] CHECK: DW_AT_external (true) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_location (DW_OP_reg5 RDI, DW_OP_piece 0x4) CHECK: DW_AT_name ("argc") CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") -CHECK: DW_AT_type (0x00000000000000a1 +CHECK: DW_AT_type (0x[[SUBPROGRAM_OFFSET]] CHECK: DW_TAG_formal_parameter CHECK: DW_AT_location (DW_OP_reg4 RSI) CHECK: DW_AT_name ("argv") -CHECK: DW_AT_type (0x00000060 +CHECK: DW_AT_type (0x[[CONSTCHARPTR_PTR_OFFSET:[0-9a-f]*]] CHECK: NULL CHECK: DW_TAG_pointer_type -CHECK: DW_AT_type (0x00000065 +CHECK: DW_AT_type (0x[[PTR_TYPE:[0-9a-f]*]] CHECK: DW_TAG_pointer_type CHECK: DW_TAG_const_type CHECK: DW_TAG_base_type @@ -42,7 +42,7 @@ CHECK: DW_AT_byte_size (0x01) CHECK: NULL -CHECK: Compile Unit:{{.*}} version = 0x0004 +CHECK: 0x[[CU2_OFFSET:[0-9a-f]*]]: Compile Unit:{{.*}} version = 0x0004 CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("clang version 3.7.0 ") @@ -62,7 +62,7 @@ CHECK: DW_AT_location (DW_OP_addr 0x100001008) CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("inc") -CHECK: DW_AT_type (0x000000a1 +CHECK: DW_AT_type (0x[[INC_OFFSET:[0-9a-f]*]] CHECK: DW_AT_inline (DW_INL_inlined) CHECK: DW_TAG_subprogram CHECK: DW_AT_low_pc (0x0000000100000f50) @@ -71,20 +71,20 @@ CHECK: DW_AT_name ("foo") CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") CHECK: DW_AT_prototyped (true) -CHECK: DW_AT_type (0x000000a1 +CHECK: DW_AT_type (0x[[INC_OFFSET]] CHECK: DW_TAG_formal_parameter CHECK: DW_AT_location (0x00000000 CHECK: [0x0000000100000f50, 0x0000000100000f5c): DW_OP_reg5 RDI, DW_OP_piece 0x4) CHECK: DW_AT_name ("arg") -CHECK: DW_AT_type (0x000000a1 +CHECK: DW_AT_type (0x[[INC_OFFSET]] CHECK: DW_TAG_inlined_subroutine -CHECK: DW_AT_abstract_origin (0x000000d2 "inc") +CHECK: DW_AT_abstract_origin (0x[[ABSTRACT_OFFSET:[0-9a-f]*]] "inc") CHECK: DW_AT_low_pc (0x0000000100000f61) CHECK: DW_AT_high_pc (0x0000000100000f70) CHECK: NULL CHECK: NULL -CHECK: Compile Unit: {{.*}} version = 0x0004 +CHECK: 0x[[CU3_OFFSET:[0-9a-f]*]]: Compile Unit: {{.*}} version = 0x0004 CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("clang version 3.7.0 ") @@ -111,7 +111,7 @@ CHECK: [0x0000000100000fa9, 0x0000000100000fad): DW_OP_reg5 RDI, DW_OP_piece 0x4) CHECK: DW_AT_name ("arg") CHECK: DW_TAG_inlined_subroutine -CHECK: DW_AT_abstract_origin (0x0000015f "inc") +CHECK: DW_AT_abstract_origin (0x[[ABSTRACT2_OFFSET:[0-9a-f]*]] "inc") CHECK: DW_AT_ranges (0x00000000 CHECK: [0x0000000100000f94, 0x0000000100000f9a) CHECK: [0x0000000100000f9f, 0x0000000100000fa7)) @@ -130,11 +130,11 @@ CHECK: .debug_aranges contents: -CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU1_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT: [0x0000000100000f40, 0x0000000100000f4b) -CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000077, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU2_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT: [0x0000000100000f50, 0x0000000100000f87) -CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x0000011b, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU3_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT: [0x0000000100000f90, 0x0000000100000fb4) CHECK: .debug_line contents: diff --git a/llvm/test/tools/dsymutil/X86/basic-lto-linking-x86.test b/llvm/test/tools/dsymutil/X86/basic-lto-linking-x86.test --- a/llvm/test/tools/dsymutil/X86/basic-lto-linking-x86.test +++ b/llvm/test/tools/dsymutil/X86/basic-lto-linking-x86.test @@ -5,7 +5,7 @@ CHECK: debug_info contents -CHECK: Compile Unit: +CHECK: 0x[[CU1_OFFSET:[0-9a-f]*]]: Compile Unit: CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") @@ -19,7 +19,7 @@ CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") CHECK: DW_AT_decl_line (23) CHECK: DW_AT_prototyped (0x01) -CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_type (0x[[INT_OFFSET:[0-9a-f]*]] CHECK: DW_AT_external (0x01) CHECK: DW_AT_accessibility (DW_ACCESS_public) CHECK: DW_AT_low_pc (0x0000000100000f40) @@ -27,11 +27,11 @@ CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("argc") -CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_type (0x[[INT_OFFSET]] CHECK: DW_AT_location (DW_OP_reg5 RDI, DW_OP_piece 0x4) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("argv") -CHECK: DW_AT_type (0x0000006a +CHECK: DW_AT_type (0x[[CONSTCHARPTR_PTR_OFFSET:[0-9a-f]*]] CHECK: DW_AT_location (DW_OP_reg4 RSI) CHECK: NULL CHECK: DW_TAG_base_type @@ -39,18 +39,18 @@ CHECK: DW_AT_encoding (DW_ATE_signed) CHECK: DW_AT_byte_size (0x04) CHECK: DW_TAG_pointer_type -CHECK: DW_AT_type (0x0000006f +CHECK: DW_AT_type (0x[[CONSTCHARPTR_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_pointer_type -CHECK: DW_AT_type (0x00000074 +CHECK: DW_AT_type (0x[[CONSTCHAR_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_const_type -CHECK: DW_AT_type (0x00000079 +CHECK: DW_AT_type (0x[[CHAR_OFFSET:[0-9a-f]*]] CHECK: DW_TAG_base_type CHECK: DW_AT_name ("char") CHECK: DW_AT_encoding (DW_ATE_signed_char) CHECK: DW_AT_byte_size (0x01) CHECK: NULL -CHECK: Compile Unit: +CHECK: 0x[[CU2_OFFSET:[0-9a-f]*]]: Compile Unit: CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") @@ -60,37 +60,37 @@ CHECK: DW_AT_low_pc (0x0000000100000f50) CHECK: DW_TAG_variable CHECK: DW_AT_name ("private_int") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") CHECK: DW_AT_location (DW_OP_addr 0x100001008) CHECK: DW_TAG_variable CHECK: DW_AT_name ("baz") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_location (DW_OP_addr 0x100001000) CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("foo") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_low_pc (0x0000000100000f50) CHECK: DW_AT_high_pc (0x0000000100000f89) CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("arg") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_location (0x00000000 CHECK: [0x0000000100000f50, 0x0000000100000f5e): DW_OP_reg5 RDI, DW_OP_piece 0x4) CHECK:[[INC1:0x[0-9a-f]*]]{{.*}}DW_TAG_inlined_subroutine -CHECK: DW_AT_abstract_origin (0x00000128 "inc") +CHECK: DW_AT_abstract_origin (0x[[INC_OFFSET:[0-9a-f]*]] "inc") CHECK: DW_AT_low_pc (0x0000000100000f63) CHECK: DW_AT_high_pc (0x0000000100000f72) CHECK: DW_AT_call_line (20) CHECK: NULL CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("inc") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_inline (DW_INL_inlined) CHECK: NULL -CHECK: Compile Unit: +CHECK: 0x[[CU3_OFFSET:[0-9a-f]*]]: Compile Unit: CHECK: DW_TAG_compile_unit CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") @@ -100,20 +100,20 @@ CHECK: DW_AT_low_pc (0x0000000100000f90) CHECK: DW_TAG_variable CHECK: DW_AT_name ("val") -CHECK: DW_AT_type (0x00000176 +CHECK: DW_AT_type (0x[[VOLATILE_INT_OFFSET:[0-9a-f]*]] CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c") CHECK: DW_AT_location (DW_OP_addr 0x100001004) CHECK: DW_TAG_volatile_type -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("bar") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_low_pc (0x0000000100000f90) CHECK: DW_AT_high_pc (0x0000000100000fb4) CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) CHECK: DW_TAG_formal_parameter CHECK: DW_AT_name ("arg") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: DW_AT_location (0x00000025 CHECK: [0x0000000100000f90, 0x0000000100000f9f): DW_OP_reg5 RDI, DW_OP_piece 0x4 CHECK: [0x0000000100000fa9, 0x0000000100000fad): DW_OP_reg5 RDI, DW_OP_piece 0x4) @@ -121,7 +121,7 @@ CHECK: DW_AT_low_pc (0x0000000100000f94) CHECK: DW_AT_high_pc (0x0000000100000fa7) CHECK:[[INC2:0x[0-9a-f]*]]{{.*}}DW_TAG_inlined_subroutine -CHECK: DW_AT_abstract_origin (0x000001d4 "inc") +CHECK: DW_AT_abstract_origin (0x[[INC_OFFSET:[0-9a-f]*]] "inc") CHECK: DW_AT_ranges (0x00000000 CHECK: [0x0000000100000f94, 0x0000000100000f9a) CHECK: [0x0000000100000f9f, 0x0000000100000fa7)) @@ -129,7 +129,7 @@ CHECK: NULL CHECK: DW_TAG_subprogram CHECK: DW_AT_name ("inc") -CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_type (0x{{0*}}[[INT_OFFSET]] CHECK: NULL CHECK: .debug_loc contents: @@ -141,11 +141,11 @@ CHECK-NEXT: (0x0000000000000019, 0x000000000000001d): DW_OP_reg5 RDI, DW_OP_piece 0x4 CHECK: .debug_aranges contents: -CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU1_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT: [0x0000000100000f40, 0x0000000100000f4b) -CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000081, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU2_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT: [0x0000000100000f50, 0x0000000100000f89) -CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x0000013a, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x[[CU3_OFFSET]], addr_size = 0x08, seg_size = 0x00 CHECK-NEXT: [0x0000000100000f90, 0x0000000100000fb4) @@ -195,7 +195,7 @@ CHECK: .debug_pubtypes contents: CHECK-NEXT: length = 0x0000001f, format = DWARF32, version = 0x0002, unit_offset = 0x00000000, unit_size = 0x00000081 CHECK-NEXT: Offset Name -CHECK-NEXT: 0x00000063 "int" +CHECK-NEXT: 0x[[INT_OFFSET]] "int" CHECK-NEXT: 0x00000079 "char" CHECK: .apple_names contents: @@ -218,7 +218,7 @@ CHECK-NEXT: Bucket 0 [ CHECK-NEXT: Hash 0xb8860c2 [ CHECK-NEXT: Name@0x74 { -CHECK-NEXT: String: 0x0000007e "baz" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "baz" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x000000c0 CHECK-NEXT: ] @@ -226,7 +226,7 @@ CHECK-NEXT: ] CHECK-NEXT: Hash 0xb88801f [ CHECK-NEXT: Name@0x84 { -CHECK-NEXT: String: 0x0000008a "inc" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "inc" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x00000110 CHECK-NEXT: ] @@ -242,7 +242,7 @@ CHECK-NEXT: Bucket 2 [ CHECK-NEXT: Hash 0xfed12c6a [ CHECK-NEXT: Name@0x98 { -CHECK-NEXT: String: 0x00000072 "private_int" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "private_int" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x000000a7 CHECK-NEXT: ] @@ -252,7 +252,7 @@ CHECK-NEXT: Bucket 3 [ CHECK-NEXT: Hash 0xb88b5c8 [ CHECK-NEXT: Name@0xa8 { -CHECK-NEXT: String: 0x00000097 "val" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "val" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x00000160 CHECK-NEXT: ] @@ -260,7 +260,7 @@ CHECK-NEXT: ] CHECK-NEXT: Hash 0x7c9a7f6a [ CHECK-NEXT: Name@0xb8 { -CHECK-NEXT: String: 0x00000051 "main" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "main" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x00000026 CHECK-NEXT: ] @@ -273,7 +273,7 @@ CHECK-NEXT: Bucket 5 [ CHECK-NEXT: Hash 0xb887389 [ CHECK-NEXT: Name@0xc8 { -CHECK-NEXT: String: 0x00000082 "foo" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "foo" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x000000d9 CHECK-NEXT: ] @@ -283,7 +283,7 @@ CHECK-NEXT: Bucket 6 [ CHECK-NEXT: Hash 0xb8860ba [ CHECK-NEXT: Name@0xd8 { -CHECK-NEXT: String: 0x0000009b "bar" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "bar" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x0000017f CHECK-NEXT: ] @@ -323,9 +323,9 @@ CHECK-NEXT: Bucket 0 [ CHECK-NEXT: Hash 0xb888030 [ CHECK-NEXT: Name@0x44 { -CHECK-NEXT: String: 0x00000060 "int" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "int" CHECK-NEXT: Data 0 [ -CHECK-NEXT: Atom[0]: 0x00000063 +CHECK-NEXT: Atom[0]: 0x[[INT_OFFSET]] CHECK-NEXT: Atom[1]: 0x0024 CHECK-NEXT: Atom[2]: 0x00 CHECK-NEXT: Atom[3]: 0x0c3a28a4 @@ -336,7 +336,7 @@ CHECK-NEXT: Bucket 1 [ CHECK-NEXT: Hash 0x7c952063 [ CHECK-NEXT: Name@0x5b { -CHECK-NEXT: String: 0x00000064 "char" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "char" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x00000079 CHECK-NEXT: Atom[1]: 0x0024 diff --git a/llvm/test/tools/dsymutil/X86/objc.test b/llvm/test/tools/dsymutil/X86/objc.test --- a/llvm/test/tools/dsymutil/X86/objc.test +++ b/llvm/test/tools/dsymutil/X86/objc.test @@ -2,7 +2,7 @@ RUN: | llvm-dwarfdump -apple-types -apple-objc - | FileCheck %s CHECK: .apple_types contents: -CHECK: String: 0x00000066 "A" +CHECK: String: 0x{{[0-9a-f]*}} "A" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x0000012d CHECK-NEXT: Atom[1]: 0x0013 (DW_TAG_structure_type) @@ -30,7 +30,7 @@ CHECK-NEXT: Bucket 0 [ CHECK-NEXT: Hash 0x2b5e6 [ CHECK-NEXT: Name@0x38 { -CHECK-NEXT: String: 0x00000066 "A" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "A" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x00000027 CHECK-NEXT: ] @@ -43,7 +43,7 @@ CHECK-NEXT: Bucket 1 [ CHECK-NEXT: Hash 0x3fa0f4b5 [ CHECK-NEXT: Name@0x4c { -CHECK-NEXT: String: 0x0000009d "A(Category)" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "A(Category)" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x0000007a CHECK-NEXT: ] diff --git a/llvm/test/tools/dsymutil/X86/update-one-CU.test b/llvm/test/tools/dsymutil/X86/update-one-CU.test --- a/llvm/test/tools/dsymutil/X86/update-one-CU.test +++ b/llvm/test/tools/dsymutil/X86/update-one-CU.test @@ -5,7 +5,7 @@ CHECK: .apple_types contents: CHECK: Hash 0x2b5e6 [ CHECK-NEXT: Name@0x145 { -CHECK-NEXT: String: 0x00000066 "A" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "A" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x0000012d CHECK-NEXT: Atom[1]: 0x0013 @@ -18,7 +18,7 @@ CHECK: .apple_objc contents: CHECK: Hash 0x2b5e6 CHECK-NEXT: Name@0x38 { -CHECK-NEXT: String: 0x00000066 "A" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "A" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x00000027 CHECK-NEXT: ] @@ -28,7 +28,7 @@ CHECK-NEXT: } CHECK: Hash 0x3fa0f4b5 CHECK-NEXT: Name@0x4c { -CHECK-NEXT: String: 0x0000009d "A(Category)" +CHECK-NEXT: String: 0x{{[0-9a-f]*}} "A(Category)" CHECK-NEXT: Data 0 [ CHECK-NEXT: Atom[0]: 0x0000007a CHECK-NEXT: ] diff --git a/llvm/tools/dsymutil/BinaryHolder.h b/llvm/tools/dsymutil/BinaryHolder.h --- a/llvm/tools/dsymutil/BinaryHolder.h +++ b/llvm/tools/dsymutil/BinaryHolder.h @@ -124,6 +124,8 @@ Expected getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy()); + void eraseObjectEntry(StringRef Filename, + TimestampTy Timestamp = TimestampTy()); void clear(); diff --git a/llvm/tools/dsymutil/BinaryHolder.cpp b/llvm/tools/dsymutil/BinaryHolder.cpp --- a/llvm/tools/dsymutil/BinaryHolder.cpp +++ b/llvm/tools/dsymutil/BinaryHolder.cpp @@ -274,6 +274,21 @@ return ObjectCache[Filename]; } +void BinaryHolder::eraseObjectEntry(StringRef Filename, TimestampTy Timestamp) { + if (Verbose) + WithColor::note() << "erasing '" << Filename << "' from cache\n"; + + if (isArchive(Filename)) { + StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; + std::lock_guard Lock(ArchiveCacheMutex); + ArchiveCache.erase(ArchiveFilename); + return; + } + + std::lock_guard Lock(ObjectCacheMutex); + ObjectCache.erase(Filename); +} + void BinaryHolder::clear() { std::lock_guard ArchiveLock(ArchiveCacheMutex); std::lock_guard ObjectLock(ObjectCacheMutex); diff --git a/llvm/tools/dsymutil/CMakeLists.txt b/llvm/tools/dsymutil/CMakeLists.txt --- a/llvm/tools/dsymutil/CMakeLists.txt +++ b/llvm/tools/dsymutil/CMakeLists.txt @@ -9,6 +9,7 @@ AsmPrinter DebugInfoDWARF DWARFLinker + DWARFLinkerNext MC Object CodeGen diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.h b/llvm/tools/dsymutil/DwarfLinkerForBinary.h --- a/llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.h @@ -16,6 +16,10 @@ #include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" #include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" #include "llvm/DWARFLinker/DWARFStreamer.h" +#include "llvm/DWARFLinkerNext/DWARFLinker.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerCompileUnit.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h" +#include "llvm/DWARFLinkerNext/DWARFStreamer.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Remarks/RemarkFormat.h" #include "llvm/Remarks/RemarkLinker.h" @@ -54,9 +58,12 @@ }; private: + bool linkImpl(const DebugMap &); + bool newLinkImpl(const DebugMap &); /// Keeps track of relocations. - class AddressManager : public AddressesMap { + class AddressManager : public virtual AddressesMap, + public virtual dwarflinker::AddressesMap { struct ValidReloc { uint64_t Offset; uint32_t Size; @@ -83,6 +90,7 @@ /// } RangesTy AddressRanges; + dwarflinker::RangesTy AddressRangesForNewLibrary; StringRef SrcFileName; @@ -101,13 +109,15 @@ /// Fill \p Info with address information for the specified \p Reloc. void fillDieInfo(const ValidReloc &Reloc, CompileUnit::DIEInfo &Info); + void fillDieInfo(const ValidReloc &Reloc, + dwarflinker::CompileUnit::DIEInfo &Info); /// Print contents of debug map entry for the specified \p Reloc. void printReloc(const ValidReloc &Reloc); public: - AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj, - const DebugMapObject &DMO) + AddressManager(bool isNewDwarfLinkerLibrary, DwarfLinkerForBinary &Linker, + const object::ObjectFile &Obj, const DebugMapObject &DMO) : Linker(Linker), SrcFileName(DMO.getObjectFilename()) { findValidRelocsInDebugSections(Obj, DMO); @@ -128,12 +138,23 @@ // 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 : DMO.symbols()) { - const auto &Mapping = Entry.getValue(); - if (Mapping.Size && Mapping.ObjectAddress) - AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( - *Mapping.ObjectAddress + Mapping.Size, - int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + if (isNewDwarfLinkerLibrary) { + for (const auto &Entry : DMO.symbols()) { + const auto &Mapping = Entry.getValue(); + if (Mapping.Size && Mapping.ObjectAddress) + AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( + *Mapping.ObjectAddress + Mapping.Size, + int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + } + } else { + for (const auto &Entry : DMO.symbols()) { + const auto &Mapping = Entry.getValue(); + if (Mapping.Size && Mapping.ObjectAddress) + AddressRangesForNewLibrary[*Mapping.ObjectAddress] = + dwarflinker::ObjFileAddressRange( + *Mapping.ObjectAddress + Mapping.Size, + int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + } } } virtual ~AddressManager() override { clear(); } @@ -169,22 +190,37 @@ bool hasValidRelocationAt(const std::vector &Relocs, uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info); + bool hasValidRelocationAt(const std::vector &Relocs, + uint64_t StartOffset, uint64_t EndOffset, + dwarflinker::CompileUnit::DIEInfo &Info); bool hasLiveMemoryLocation(const DWARFDie &DIE, CompileUnit::DIEInfo &Info) override; bool hasLiveAddressRange(const DWARFDie &DIE, CompileUnit::DIEInfo &Info) override; + bool + hasLiveMemoryLocation(const DWARFDie &DIE, + dwarflinker::CompileUnit::DIEInfo &Info) override; + bool hasLiveAddressRange(const DWARFDie &DIE, + dwarflinker::CompileUnit::DIEInfo &Info) override; + bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) override; llvm::Expected relocateIndexedAddr(uint64_t StartOffset, uint64_t EndOffset) override; - RangesTy &getValidAddressRanges() override { return AddressRanges; } + void getValidAddressRanges(RangesTy *&Ranges) override { + Ranges = &AddressRanges; + } + void getValidAddressRanges(dwarflinker::RangesTy *&Ranges) override { + Ranges = &AddressRangesForNewLibrary; + } void clear() override { AddressRanges.clear(); + AddressRangesForNewLibrary.clear(); ValidDebugInfoRelocs.clear(); ValidDebugAddrRelocs.clear(); } @@ -194,22 +230,23 @@ /// \defgroup Helpers Various helper methods. /// /// @{ - bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); + template + bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile, + std::unique_ptr &Streamer, OFT OutFileType); /// Attempt to load a debug object from disk. ErrorOr loadObject(const DebugMapObject &Obj, const Triple &triple); - ErrorOr loadObject(const DebugMapObject &Obj, - const DebugMap &DebugMap, - remarks::RemarkLinker &RL); + + template + ErrorOr loadObject(const DebugMapObject &Obj, const DebugMap &DebugMap, + remarks::RemarkLinker &RL, + std::vector> &ObjectsForLinking, + bool isNewDwarfLinkerLibrary); raw_fd_ostream &OutFile; BinaryHolder &BinHolder; LinkOptions Options; - std::unique_ptr Streamer; - std::vector> ObjectsForLinking; - std::vector> ContextForLinking; - std::vector> AddressMapForLinking; std::vector EmptyWarnings; /// A list of all .swiftinterface files referenced by the debug @@ -220,6 +257,8 @@ bool ModuleCacheHintDisplayed = false; bool ArchiveHintDisplayed = false; + + std::mutex ObjLoaderMutex; }; } // end namespace dsymutil diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp --- a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -36,6 +36,7 @@ #include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/Config/config.h" #include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" +#include "llvm/DWARFLinkerNext/DWARFLinkerDeclContext.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" @@ -59,6 +60,7 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" @@ -83,7 +85,6 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include #include #include @@ -161,13 +162,16 @@ DIE->dump(errs(), 6 /* Indent */, DumpOpts); } +template bool DwarfLinkerForBinary::createStreamer(const Triple &TheTriple, - raw_fd_ostream &OutFile) { + raw_fd_ostream &OutFile, + std::unique_ptr &Streamer, + OFT OutFileType) { if (Options.NoOutput) return true; - Streamer = std::make_unique( - Options.FileType, OutFile, Options.Translator, Options.Minimize, + Streamer = std::make_unique( + OutFileType, OutFile, Options.Translator, Options.Minimize, [&](const Twine &Error, StringRef Context, const DWARFDie *) { error(Error, Context); }, @@ -262,22 +266,25 @@ return Error::success(); } -ErrorOr -DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, - const DebugMap &DebugMap, - remarks::RemarkLinker &RL) { +template +ErrorOr DwarfLinkerForBinary::loadObject( + const DebugMapObject &Obj, const DebugMap &DebugMap, + remarks::RemarkLinker &RL, + std::vector> &ObjectsForLinking, + bool isNewDwarfLinkerLibrary) { auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple()); if (ErrorOrObj) { - ContextForLinking.push_back( - std::unique_ptr(DWARFContext::create(*ErrorOrObj))); - AddressMapForLinking.push_back( - std::make_unique(*this, *ErrorOrObj, Obj)); - - ObjectsForLinking.push_back(std::make_unique( - Obj.getObjectFilename(), ContextForLinking.back().get(), - AddressMapForLinking.back().get(), - Obj.empty() ? Obj.getWarnings() : EmptyWarnings)); + ObjectsForLinking.push_back(std::make_unique( + Obj.getObjectFilename(), Obj.getTimestamp(), + DWARFContext::create(*ErrorOrObj), + std::make_unique(isNewDwarfLinkerLibrary, *this, + *ErrorOrObj, Obj), + Obj.empty() ? Obj.getWarnings() : EmptyWarnings, + [&](StringRef FileName, + sys::TimePoint Timestamp) { + BinHolder.eraseObjectEntry(FileName, Timestamp); + })); Error E = RL.link(*ErrorOrObj); if (Error NewE = handleErrors( @@ -293,15 +300,31 @@ } bool DwarfLinkerForBinary::link(const DebugMap &Map) { - if (!createStreamer(Map.getTriple(), OutFile)) - return false; + if (Options.UseDwarfLinkerNext) + return newLinkImpl(Map); + + return linkImpl(Map); +} - ObjectsForLinking.clear(); - ContextForLinking.clear(); - AddressMapForLinking.clear(); +bool DwarfLinkerForBinary::linkImpl(const DebugMap &Map) { + std::unique_ptr Streamer; + if (!Options.NoOutput) { + Streamer = std::make_unique( + Options.FileType, OutFile, Options.Translator, Options.Minimize, + [&](const Twine &Error, StringRef Context, const DWARFDie *) { + error(Error, Context); + }, + [&](const Twine &Warning, StringRef Context, const DWARFDie *) { + warn(Warning, Context); + }); + if (!Streamer->init(Map.getTriple())) + return false; + } DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); + std::vector> ObjectsForLinking; + DWARFLinker GeneralLinker(Streamer.get(), DwarfLinkerClient::Dsymutil); remarks::RemarkLinker RL; @@ -333,12 +356,14 @@ error(Error, Context); }); GeneralLinker.setObjFileLoader( - [&DebugMap, &RL, this](StringRef ContainerName, - StringRef Path) -> ErrorOr { + [&](StringRef ContainerName, StringRef Path) -> ErrorOr { + std::unique_lock Guard(ObjLoaderMutex); + auto &Obj = DebugMap.addDebugMapObject( Path, sys::TimePoint(), MachO::N_OSO); - if (auto ErrorOrObj = loadObject(Obj, DebugMap, RL)) { + if (ErrorOr ErrorOrObj = loadObject( + Obj, DebugMap, RL, ObjectsForLinking, false)) { return *ErrorOrObj; } else { // Try and emit more helpful warnings by applying some heuristics. @@ -427,12 +452,13 @@ continue; } - if (auto ErrorOrObj = loadObject(*Obj, Map, RL)) + if (auto ErrorOrObj = loadObject(*Obj, Map, RL, ObjectsForLinking, false)) GeneralLinker.addObjectFile(*ErrorOrObj); else { ObjectsForLinking.push_back(std::make_unique( - Obj->getObjectFilename(), nullptr, nullptr, - Obj->empty() ? Obj->getWarnings() : EmptyWarnings)); + Obj->getObjectFilename(), Obj->getTimestamp(), nullptr, nullptr, + Obj->empty() ? Obj->getWarnings() : EmptyWarnings, + [&](StringRef, sys::TimePoint) {})); GeneralLinker.addObjectFile(*ObjectsForLinking.back()); } } @@ -464,6 +490,245 @@ return true; } +bool DwarfLinkerForBinary::newLinkImpl(const DebugMap &Map) { + dwarflinker::OutputFileType OutFileType; + if (Options.FileType == OutputFileType::Object) + OutFileType = dwarflinker::OutputFileType::Object; + else + OutFileType = dwarflinker::OutputFileType::Assembly; + + std::unique_ptr Streamer; + + if (!Options.NoOutput) { + Streamer = std::make_unique( + OutFileType, OutFile, Options.Translator, + [&](const Twine &Error, StringRef Context, const DWARFDie *) { + error(Error, Context); + }, + [&](const Twine &Warning, StringRef Context, const DWARFDie *) { + warn(Warning, Context); + }); + if (!Streamer->init(Map.getTriple())) + return false; + } + + DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); + + std::vector> ObjectsForLinking; + + bool AtLeastOneDwarfAccelTable = false; + bool AtLeastOneAppleAccelTable = false; + + std::function TranslationLambda = [&](StringRef Input) { + assert(Options.Translator); + return Options.Translator(Input); + }; + + dwarflinker::DWARFLinker GeneralLinker( + Streamer.get(), dwarflinker::DwarfLinkerClient::Dsymutil, + Options.Translator ? TranslationLambda : nullptr); + + remarks::RemarkLinker RL; + if (!Options.RemarksPrependPath.empty()) + RL.setExternalFilePrependPath(Options.RemarksPrependPath); + GeneralLinker.setObjectPrefixMap(&Options.ObjectPrefixMap); + + GeneralLinker.setVerbosity(Options.Verbose); + GeneralLinker.setStatistics(Options.Statistics); + GeneralLinker.setNoOutput(Options.NoOutput); + GeneralLinker.setNoODR(Options.NoODR); + GeneralLinker.setRemoveObsoleteDebugInfo(!Options.Update); + GeneralLinker.setNumThreads(Options.Threads); + GeneralLinker.setPrependPath(Options.PrependPath); + GeneralLinker.setWarningHandler( + [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { + reportWarning(Warning, Context, DIE); + }); + GeneralLinker.setErrorHandler( + [&](const Twine &Error, StringRef Context, const DWARFDie *) { + error(Error, Context); + }); + GeneralLinker.setObjFileLoader( + [&](StringRef ContainerName, + StringRef Path) -> ErrorOr { + std::unique_lock Guard(ObjLoaderMutex); + + auto &Obj = DebugMap.addDebugMapObject( + Path, sys::TimePoint(), MachO::N_OSO); + + if (ErrorOr ErrorOrObj = + loadObject(Obj, DebugMap, RL, + ObjectsForLinking, true)) { + return *ErrorOrObj; + } else { + // Try and emit more helpful warnings by applying some heuristics. + StringRef ObjFile = ContainerName; + bool IsClangModule = sys::path::extension(Path).equals(".pcm"); + bool IsArchive = ObjFile.endswith(")"); + + if (IsClangModule) { + StringRef ModuleCacheDir = sys::path::parent_path(Path); + if (sys::fs::exists(ModuleCacheDir)) { + // If the module's parent directory exists, we assume that the + // module cache has expired and was pruned by clang. A more + // adventurous dsymutil would invoke clang to rebuild the module + // now. + if (!ModuleCacheHintDisplayed) { + WithColor::note() + << "The clang module cache may have expired since " + "this object file was built. Rebuilding the " + "object file will rebuild the module cache.\n"; + ModuleCacheHintDisplayed = true; + } + } else if (IsArchive) { + // If the module cache directory doesn't exist at all and the + // object file is inside a static library, we assume that the + // static library was built on a different machine. We don't want + // to discourage module debugging for convenience libraries within + // a project though. + if (!ArchiveHintDisplayed) { + WithColor::note() + << "Linking a static library that was built with " + "-gmodules, but the module cache was not found. " + "Redistributable static libraries should never be " + "built with module debugging enabled. The debug " + "experience will be degraded due to incomplete " + "debug information.\n"; + ArchiveHintDisplayed = true; + } + } + } + + return ErrorOrObj.getError(); + } + + llvm_unreachable("Unhandled DebugMap object"); + }); + GeneralLinker.setSwiftInterfacesMap(&ParseableSwiftInterfaces); + + for (const auto &Obj : Map.objects()) { + // N_AST objects (swiftmodule files) should get dumped directly into the + // appropriate DWARF section. + if (Obj->getType() == MachO::N_AST) { + if (Options.Verbose) + outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; + + StringRef File = Obj->getObjectFilename(); + auto ErrorOrMem = MemoryBuffer::getFile(File); + if (!ErrorOrMem) { + warn("Could not open '" + File + "'\n"); + continue; + } + sys::fs::file_status Stat; + if (auto Err = sys::fs::status(File, Stat)) { + warn(Err.message()); + continue; + } + if (!Options.NoTimestamp) { + // The modification can have sub-second precision so we need to cast + // away the extra precision that's not present in the debug map. + auto ModificationTime = + std::chrono::time_point_cast( + Stat.getLastModificationTime()); + if (ModificationTime != Obj->getTimestamp()) { + // Not using the helper here as we can easily stream TimePoint<>. + WithColor::warning() + << File << ": timestamp mismatch between swift interface file (" + << sys::TimePoint<>(Obj->getTimestamp()) << ") and debug map (" + << sys::TimePoint<>(Obj->getTimestamp()) << ")\n"; + continue; + } + } + + // Copy the module into the .swift_ast section. + if (!Options.NoOutput) + Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer()); + + continue; + } + + if (auto ErrorOrObj = loadObject(*Obj, Map, RL, ObjectsForLinking, true)) { + GeneralLinker.addObjectFile(*ErrorOrObj); + + if (Options.TheAccelTableKind == AccelTableKind::Default && + ErrorOrObj->Dwarf) { + auto &DwarfObj = ErrorOrObj->Dwarf->getDWARFObj(); + + if (!AtLeastOneDwarfAccelTable && + (!DwarfObj.getAppleNamesSection().Data.empty() || + !DwarfObj.getAppleTypesSection().Data.empty() || + !DwarfObj.getAppleNamespacesSection().Data.empty() || + !DwarfObj.getAppleObjCSection().Data.empty())) + AtLeastOneAppleAccelTable = true; + + if (!AtLeastOneDwarfAccelTable && + !DwarfObj.getNamesSection().Data.empty()) + AtLeastOneDwarfAccelTable = true; + } + } else { + ObjectsForLinking.push_back(std::make_unique( + Obj->getObjectFilename(), Obj->getTimestamp(), nullptr, nullptr, + Obj->empty() ? Obj->getWarnings() : EmptyWarnings, + [&](StringRef, sys::TimePoint) {})); + GeneralLinker.addObjectFile(*ObjectsForLinking.back()); + } + } + + // If we haven't decided on an accelerator table kind yet, we base ourselves + // on the DWARF we have seen so far. At this point we haven't pulled in debug + // information from modules yet, so it is technically possible that they + // would affect the decision. However, as they're built with the same + // compiler and flags, it is safe to assume that they will follow the + // decision made here. + if (Options.TheAccelTableKind == llvm::AccelTableKind::Apple) { + GeneralLinker.setGenerateAppleNamespaces(true); + GeneralLinker.setGenerateAppleNames(true); + GeneralLinker.setGenerateAppleTypes(true); + GeneralLinker.setGenerateAppleObjC(true); + GeneralLinker.setGeneratePubNames(!Options.Minimize); + GeneralLinker.setGeneratePubTypes(!Options.Minimize); + } else if (Options.TheAccelTableKind == llvm::AccelTableKind::Dwarf) { + GeneralLinker.setGenerateDebugNames(true); + } else { + if (AtLeastOneDwarfAccelTable && !AtLeastOneAppleAccelTable) { + GeneralLinker.setGenerateDebugNames(true); + } else { + GeneralLinker.setGenerateAppleNamespaces(true); + GeneralLinker.setGenerateAppleNames(true); + GeneralLinker.setGenerateAppleTypes(true); + GeneralLinker.setGenerateAppleObjC(true); + GeneralLinker.setGeneratePubNames(!Options.Minimize); + GeneralLinker.setGeneratePubTypes(!Options.Minimize); + } + } + + // link debug info for loaded object files. + GeneralLinker.link(); + + StringRef ArchName = Map.getTriple().getArchName(); + if (Error E = emitRemarks(Options, Map.getBinaryPath(), ArchName, RL)) + return error(toString(std::move(E))); + + if (Options.NoOutput) + return true; + + if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { + StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); + if (auto E = + copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options)) + return error(toString(std::move(E))); + } + + if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() && + Options.FileType == OutputFileType::Object) + return MachOUtils::generateDsymCompanion( + Options.VFS, Map, Options.Translator, + *Streamer->getAsmPrinter().OutStreamer, OutFile); + + Streamer->finish(); + return true; +} + static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) { switch (Arch) { case Triple::x86: @@ -648,6 +913,14 @@ Info.InDebugMap = true; } +void DwarfLinkerForBinary::AddressManager::fillDieInfo( + const ValidReloc &Reloc, dwarflinker::CompileUnit::DIEInfo &Info) { + Info.AddrAdjust = relocate(Reloc); + if (Reloc.Mapping->getValue().ObjectAddress) + Info.AddrAdjust -= uint64_t(*Reloc.Mapping->getValue().ObjectAddress); + Info.InDebugMap = true; +} + bool DwarfLinkerForBinary::AddressManager::hasValidRelocationAt( const std::vector &AllRelocs, uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info) { @@ -664,6 +937,22 @@ return true; } +bool DwarfLinkerForBinary::AddressManager::hasValidRelocationAt( + const std::vector &AllRelocs, uint64_t StartOffset, + uint64_t EndOffset, dwarflinker::CompileUnit::DIEInfo &Info) { + std::vector Relocs = + getRelocations(AllRelocs, StartOffset, EndOffset); + + if (Relocs.size() == 0) + return false; + + if (Linker.Options.Verbose) + printReloc(Relocs[0]); + fillDieInfo(Relocs[0], Info); + + return true; +} + /// Get the starting and ending (exclusive) offset for the /// attribute with index \p Idx descibed by \p Abbrev. \p Offset is /// supposed to point to the position of the first attribute described @@ -739,6 +1028,61 @@ return false; } +bool DwarfLinkerForBinary::AddressManager::hasLiveMemoryLocation( + const DWARFDie &DIE, dwarflinker::CompileUnit::DIEInfo &MyInfo) { + + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + Optional LocationIdx = + Abbrev->findAttributeIndex(dwarf::DW_AT_location); + if (!LocationIdx) + return false; + + uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + uint64_t LocationOffset, LocationEndOffset; + std::tie(LocationOffset, LocationEndOffset) = + getAttributeOffsets(Abbrev, *LocationIdx, Offset, *DIE.getDwarfUnit()); + + // FIXME: Support relocations debug_addr. + return hasValidRelocationAt(ValidDebugInfoRelocs, LocationOffset, + LocationEndOffset, MyInfo); +} + +bool DwarfLinkerForBinary::AddressManager::hasLiveAddressRange( + const DWARFDie &DIE, dwarflinker::CompileUnit::DIEInfo &MyInfo) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + Optional LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc); + if (!LowPcIdx) + return false; + + dwarf::Form Form = Abbrev->getFormByIndex(*LowPcIdx); + + if (Form == dwarf::DW_FORM_addr) { + uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + uint64_t LowPcOffset, LowPcEndOffset; + std::tie(LowPcOffset, LowPcEndOffset) = + getAttributeOffsets(Abbrev, *LowPcIdx, Offset, *DIE.getDwarfUnit()); + return hasValidRelocationAt(ValidDebugInfoRelocs, LowPcOffset, + LowPcEndOffset, MyInfo); + } + + if (Form == dwarf::DW_FORM_addrx) { + Optional AddrValue = DIE.find(dwarf::DW_AT_low_pc); + if (Optional AddrOffsetSectionBase = + DIE.getDwarfUnit()->getAddrOffsetSectionBase()) { + uint64_t StartOffset = *AddrOffsetSectionBase + AddrValue->getRawUValue(); + uint64_t EndOffset = + StartOffset + DIE.getDwarfUnit()->getAddressByteSize(); + return hasValidRelocationAt(ValidDebugAddrRelocs, StartOffset, EndOffset, + MyInfo); + } else + Linker.reportWarning("no base offset for address table", SrcFileName); + } + + return false; +} + uint64_t DwarfLinkerForBinary::AddressManager::relocate(const ValidReloc &Reloc) const { return Reloc.Mapping->getValue().BinaryAddress + Reloc.Addend; diff --git a/llvm/tools/dsymutil/LinkUtils.h b/llvm/tools/dsymutil/LinkUtils.h --- a/llvm/tools/dsymutil/LinkUtils.h +++ b/llvm/tools/dsymutil/LinkUtils.h @@ -45,6 +45,9 @@ /// Do not check swiftmodule timestamp bool NoTimestamp = false; + /// Use DWARFLinkerNext + bool UseDwarfLinkerNext = false; + /// Number of threads. unsigned Threads = 1; diff --git a/llvm/tools/dsymutil/Options.td b/llvm/tools/dsymutil/Options.td --- a/llvm/tools/dsymutil/Options.td +++ b/llvm/tools/dsymutil/Options.td @@ -185,3 +185,7 @@ HelpText<"Specify the format to be used when serializing the linked remarks.">, Group; def: Joined<["--", "-"], "remarks-output-format=">, Alias; + +def use_dlnext: F<"use-dlnext">, + HelpText<"Use DWARFLinkerNext.">, + Group; diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -229,6 +229,7 @@ Options.LinkOpts.Update = Args.hasArg(OPT_update); Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics); + Options.LinkOpts.UseDwarfLinkerNext = Args.hasArg(OPT_use_dlnext); if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) { Options.ReproMode = ReproducerMode::Use;