Index: llvm/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/tools/dsymutil/CMakeLists.txt +++ llvm/tools/dsymutil/CMakeLists.txt @@ -12,8 +12,11 @@ dsymutil.cpp BinaryHolder.cpp CFBundle.cpp + CompileUnit.cpp DebugMap.cpp + DeclContext.cpp DwarfLinker.cpp + DwarfStreamer.cpp MachODebugMapParser.cpp MachOUtils.cpp NonRelocatableStringpool.cpp Index: llvm/tools/dsymutil/CompileUnit.h =================================================================== --- /dev/null +++ llvm/tools/dsymutil/CompileUnit.h @@ -0,0 +1,323 @@ +//===- tools/dsymutil/CompileUnit.h - Dwarf debug info linker ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/IntervalMap.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +#ifndef LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H +#define LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H + +namespace llvm { +namespace dsymutil { + +class DeclContext; + +template +using HalfOpenIntervalMap = + IntervalMap::LeafSize, + IntervalMapHalfOpenInfo>; + +using FunctionIntervals = HalfOpenIntervalMap; + +// FIXME: Delete this structure. +struct PatchLocation { + DIE::value_iterator I; + + PatchLocation() = default; + PatchLocation(DIE::value_iterator I) : I(I) {} + + void set(uint64_t New) const { + assert(I); + const auto &Old = *I; + assert(Old.getType() == DIEValue::isInteger); + *I = DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(New)); + } + + uint64_t get() const { + assert(I); + return I->getDIEInteger().getValue(); + } +}; + +/// Stores all information relating to a compile unit, be it in its original +/// instance in the object file to its brand new cloned and linked DIE tree. +class CompileUnit { +public: + /// Information gathered about a DIE in the object file. + struct DIEInfo { + /// Address offset to apply to the described entity. + int64_t AddrAdjust; + + /// ODR Declaration context. + DeclContext *Ctxt; + + /// Cloned version of that DIE. + DIE *Clone; + + /// The index of this DIE's parent. + uint32_t ParentIdx; + + /// 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 Incomplete : 1; + }; + + CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, + StringRef ClangModuleName) + : OrigUnit(OrigUnit), ID(ID), Ranges(RangeAlloc), + ClangModuleName(ClangModuleName) { + Info.resize(OrigUnit.getNumDIEs()); + + auto CUDie = OrigUnit.getUnitDIE(false); + if (!CUDie) { + HasODR = false; + return; + } + 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); + else + HasODR = false; + } + + DWARFUnit &getOrigUnit() const { return OrigUnit; } + + unsigned getUniqueID() const { return ID; } + + void createOutputDIE() { + NewUnit.emplace(OrigUnit.getVersion(), OrigUnit.getAddressByteSize(), + OrigUnit.getUnitDIE().getTag()); + } + + DIE *getOutputUnitDIE() const { + if (NewUnit) + return &const_cast(*NewUnit).getUnitDie(); + return nullptr; + } + + bool hasODR() const { return HasODR; } + bool isClangModule() const { return !ClangModuleName.empty(); } + const std::string &getClangModuleName() const { return ClangModuleName; } + + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } + const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } + + uint64_t getStartOffset() const { return StartOffset; } + uint64_t getNextUnitOffset() const { return NextUnitOffset; } + void setStartOffset(uint64_t DebugInfoSize) { StartOffset = DebugInfoSize; } + + uint64_t getLowPc() const { return LowPc; } + uint64_t getHighPc() const { return HighPc; } + bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } + + Optional getUnitRangesAttribute() const { + return UnitRangeAttribute; + } + + const FunctionIntervals &getFunctionRanges() const { return Ranges; } + + const std::vector &getRangesAttributes() const { + return RangeAttributes; + } + + const std::vector> & + getLocationAttributes() const { + return LocationAttributes; + } + + void setHasInterestingContent() { HasInterestingContent = true; } + bool hasInterestingContent() { return HasInterestingContent; } + + /// 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(); + + /// Compute the end offset for this unit. Must be called after the CU's DIEs + /// have been cloned. \returns the next unit offset (which is also the + /// current debug_info section size). + uint64_t computeNextUnitOffset(); + + /// Keep track of a forward reference to DIE \p Die 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 noteForwardReference(DIE *Die, const CompileUnit *RefUnit, + DeclContext *Ctxt, PatchLocation Attr); + + /// Apply all fixups recorded by noteForwardReference(). + void fixupForwardReferences(); + + /// 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, PatchLocation Attr); + + /// Keep track of a location attribute pointing to a location list in the + /// debug_loc section. + void noteLocationAttribute(PatchLocation Attr, int64_t PcOffset); + + /// Add a name accelerator entry for \a Die with \a Name. + void addNamespaceAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name); + + /// Add a name accelerator entry for \a Die with \a Name. + void addNameAccelerator(const DIE *Die, DwarfStringPoolEntryRef 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, DwarfStringPoolEntryRef 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, DwarfStringPoolEntryRef Name, + bool ObjcClassImplementation, + uint32_t QualifiedNameHash); + + struct AccelInfo { + /// Name of the entry. + DwarfStringPoolEntryRef Name; + + /// DIE this entry describes. + const DIE *Die; + + /// 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, const DIE *Die, + bool SkipPubSection = false) + : Name(Name), Die(Die), SkipPubSection(SkipPubSection) {} + + AccelInfo(DwarfStringPoolEntryRef Name, const DIE *Die, + uint32_t QualifiedNameHash, bool ObjCClassIsImplementation) + : Name(Name), Die(Die), QualifiedNameHash(QualifiedNameHash), + SkipPubSection(false), + ObjcClassImplementation(ObjCClassIsImplementation) {} + }; + + const std::vector &getPubnames() const { return Pubnames; } + const std::vector &getPubtypes() const { return Pubtypes; } + const std::vector &getNamespaces() const { return Namespaces; } + const std::vector &getObjC() const { return ObjC; } + + /// Get the full path for file \a FileNum in the line table + StringRef getResolvedPath(unsigned FileNum) { + if (FileNum >= ResolvedPaths.size()) + return StringRef(); + return ResolvedPaths[FileNum]; + } + + /// Set the fully resolved path for the line-table's file \a FileNum + /// to \a Path. + void setResolvedPath(unsigned FileNum, StringRef Path) { + if (ResolvedPaths.size() <= FileNum) + ResolvedPaths.resize(FileNum + 1); + ResolvedPaths[FileNum] = Path; + } + +private: + DWARFUnit &OrigUnit; + unsigned ID; + std::vector Info; ///< DIE info indexed by DIE index. + Optional NewUnit; + + uint64_t StartOffset; + uint64_t NextUnitOffset; + + uint64_t LowPc = std::numeric_limits::max(); + uint64_t HighPc = 0; + + /// 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 cross-cu forward references the target DIE's offset + /// isn't known you emit the reference attribute. + std::vector< + std::tuple> + ForwardDIEReferences; + + 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 liked 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; + /// @} + + /// Cached resolved paths from the line table. + /// Note, the StringRefs here point in to the intern (uniquing) string pool. + /// This means that a StringRef returned here doesn't need to then be uniqued + /// for the purposes of getting a unique address for each string. + std::vector ResolvedPaths; + + /// Is this unit subject to the ODR rule? + bool HasODR; + + /// Did a DIE actually contain a valid reloc? + bool HasInterestingContent; + + /// If this is a Clang module, this holds the module's name. + std::string ClangModuleName; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H Index: llvm/tools/dsymutil/CompileUnit.cpp =================================================================== --- /dev/null +++ llvm/tools/dsymutil/CompileUnit.cpp @@ -0,0 +1,136 @@ +//===- tools/dsymutil/CompileUnit.h - Dwarf compile unit ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompileUnit.h" +#include "DeclContext.h" + +namespace llvm { +namespace dsymutil { + +/// 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; +} + +void CompileUnit::markEverythingAsKept() { + unsigned Idx = 0; + + setHasInterestingContent(); + + for (auto &I : Info) { + // Mark everything that wasn't explicit marked for pruning. + I.Keep = !I.Prune; + auto DIE = OrigUnit.getDIEAtIndex(Idx++); + + // Try to guess which DIEs must go to the accelerator tables. We do that + // just for variables, because functions will be handled depending on + // whether they carry a DW_AT_low_pc attribute or not. + if (DIE.getTag() != dwarf::DW_TAG_variable && + DIE.getTag() != dwarf::DW_TAG_constant) + continue; + + Optional Value; + if (!(Value = DIE.find(dwarf::DW_AT_location))) { + if ((Value = DIE.find(dwarf::DW_AT_const_value)) && + !inFunctionScope(*this, I.ParentIdx)) + I.InDebugMap = true; + continue; + } + if (auto Block = Value->getAsBlock()) { + if (Block->size() > OrigUnit.getAddressByteSize() && + (*Block)[0] == dwarf::DW_OP_addr) + I.InDebugMap = true; + } + } +} + +uint64_t CompileUnit::computeNextUnitOffset() { + NextUnitOffset = StartOffset + 11 /* Header size */; + // The root DIE might be null, meaning that the Unit had nothing to + // contribute to the linked output. In that case, we will emit the + // unit header without any actual DIE. + if (NewUnit) + NextUnitOffset += NewUnit->getUnitDie().getSize(); + return NextUnitOffset; +} + +/// Keep track of a forward cross-cu reference from this unit +/// to \p Die that lives in \p RefUnit. +void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, + DeclContext *Ctxt, PatchLocation Attr) { + ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); +} + +void CompileUnit::fixupForwardReferences() { + for (const auto &Ref : ForwardDIEReferences) { + DIE *RefDie; + const CompileUnit *RefUnit; + PatchLocation Attr; + DeclContext *Ctxt; + std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) + Attr.set(Ctxt->getCanonicalDIEOffset()); + else + Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); + } +} + +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) { + 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, PatchLocation Attr) { + if (Die.getTag() != dwarf::DW_TAG_compile_unit) + RangeAttributes.push_back(Attr); + else + UnitRangeAttribute = Attr; +} + +void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) { + LocationAttributes.emplace_back(Attr, PcOffset); +} + +void CompileUnit::addNamespaceAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name) { + Namespaces.emplace_back(Name, Die); +} + +void CompileUnit::addObjCAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name, + bool SkipPubSection) { + ObjC.emplace_back(Name, Die, SkipPubSection); +} + +void CompileUnit::addNameAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name, + bool SkipPubSection) { + Pubnames.emplace_back(Name, Die, SkipPubSection); +} + +void CompileUnit::addTypeAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name, + bool ObjcClassImplementation, + uint32_t QualifiedNameHash) { + Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation); +} + +} // namespace dsymutil +} // namespace llvm Index: llvm/tools/dsymutil/DebugMap.h =================================================================== --- llvm/tools/dsymutil/DebugMap.h +++ llvm/tools/dsymutil/DebugMap.h @@ -1,6 +1,6 @@ //=- tools/dsymutil/DebugMap.h - Generic debug map representation -*- C++ -*-=// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. Index: llvm/tools/dsymutil/DebugMap.cpp =================================================================== --- llvm/tools/dsymutil/DebugMap.cpp +++ llvm/tools/dsymutil/DebugMap.cpp @@ -1,6 +1,6 @@ //===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. Index: llvm/tools/dsymutil/DeclContext.h =================================================================== --- /dev/null +++ llvm/tools/dsymutil/DeclContext.h @@ -0,0 +1,172 @@ +//===- tools/dsymutil/DeclContext.h - Dwarf debug info linker ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompileUnit.h" +#include "NonRelocatableStringpool.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/Support/Path.h" + +#ifndef LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H +#define LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H + +namespace llvm { +namespace dsymutil { + +struct DeclMapInfo; + +/// Small helper that resolves and caches file paths. This helps reduce the +/// number of calls to realpath which is expensive. We assume the input are +/// files, and cache the realpath of their parent. This way we can quickly +/// resolve different files under the same path. +class CachedPathResolver { +public: + /// Resolve a path by calling realpath and cache its result. The returned + /// StringRef is interned in the given \p StringPool. + StringRef resolve(std::string Path, NonRelocatableStringpool &StringPool) { + StringRef FileName = sys::path::filename(Path); + SmallString<256> ParentPath = sys::path::parent_path(Path); + + // If the ParentPath has not yet been resolved, resolve and cache it for + // future look-ups. + if (!ResolvedPaths.count(ParentPath)) { + SmallString<256> RealPath; + sys::fs::real_path(ParentPath, RealPath); + ResolvedPaths.insert({ParentPath, StringRef(RealPath).str()}); + } + + // Join the file name again with the resolved path. + SmallString<256> ResolvedPath(ResolvedPaths[ParentPath]); + sys::path::append(ResolvedPath, FileName); + return StringPool.internString(ResolvedPath); + } + +private: + StringMap ResolvedPaths; +}; + +/// 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), Parent(*this) {} + + DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, + StringRef Name, StringRef File, const DeclContext &Parent, + DWARFDie LastSeenDIE = DWARFDie(), unsigned CUId = 0) + : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), + DefinedInClangModule(0), Name(Name), File(File), Parent(Parent), + LastSeenDIE(LastSeenDIE), LastSeenCompileUnitID(CUId) {} + + uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } + + bool setLastSeenDIE(CompileUnit &U, const DWARFDie &Die); + + uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } + void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } + + bool isDefinedInClangModule() const { return DefinedInClangModule; } + void setDefinedInClangModule(bool Val) { DefinedInClangModule = Val; } + + uint16_t getTag() const { return Tag; } + StringRef getName() const { return Name; } + +private: + friend DeclMapInfo; + + unsigned QualifiedNameHash = 0; + uint32_t Line = 0; + uint32_t ByteSize = 0; + uint16_t Tag = dwarf::DW_TAG_compile_unit; + unsigned DefinedInClangModule : 1; + StringRef Name; + StringRef File; + const DeclContext &Parent; + DWARFDie LastSeenDIE; + uint32_t LastSeenCompileUnitID = 0; + uint32_t CanonicalDIEOffset = 0; +}; + +/// 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: + /// 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. + /// + /// FIXME: The invalid bit along the return value is to emulate some + /// dsymutil-classic functionality. + PointerIntPair + getChildDeclContext(DeclContext &Context, const DWARFDie &DIE, + CompileUnit &Unit, UniquingStringPool &StringPool, + bool InClangModule); + + DeclContext &getRoot() { return Root; } + +private: + BumpPtrAllocator Allocator; + DeclContext Root; + DeclContext::Map Contexts; + + /// Cache resolved paths from the line table. + CachedPathResolver PathResolver; +}; + +/// 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->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && + LHS->Name.data() == RHS->Name.data() && + LHS->File.data() == RHS->File.data() && + LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; + } +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H Index: llvm/tools/dsymutil/DeclContext.cpp =================================================================== --- /dev/null +++ llvm/tools/dsymutil/DeclContext.cpp @@ -0,0 +1,211 @@ +//===- tools/dsymutil/DeclContext.cpp - Declaration context ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DeclContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +namespace llvm { +namespace dsymutil { + +/// Set the last DIE/CU a context was seen in and, possibly invalidate the +/// context if it is ambiguous. +/// +/// 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. Make it invalid. +bool DeclContext::setLastSeenDIE(CompileUnit &U, const DWARFDie &Die) { + if (LastSeenCompileUnitID == U.getUniqueID()) { + DWARFUnit &OrigUnit = U.getOrigUnit(); + uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE); + U.getInfo(FirstIdx).Ctxt = nullptr; + return false; + } + + LastSeenCompileUnitID = U.getUniqueID(); + LastSeenDIE = Die; + return true; +} + +PointerIntPair DeclContextTree::getChildDeclContext( + DeclContext &Context, const DWARFDie &DIE, CompileUnit &U, + UniquingStringPool &StringPool, bool InClangModule) { + 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 PointerIntPair(nullptr); + case dwarf::DW_TAG_module: + break; + case dwarf::DW_TAG_compile_unit: + return PointerIntPair(&Context); + case dwarf::DW_TAG_subprogram: + // Do not unique anything inside CU local functions. + if ((Context.getTag() == dwarf::DW_TAG_namespace || + Context.getTag() == dwarf::DW_TAG_compile_unit) && + !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0)) + return PointerIntPair(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 PointerIntPair(nullptr); + break; + } + + const char *Name = DIE.getName(DINameKind::LinkageName); + const char *ShortName = DIE.getName(DINameKind::ShortName); + StringRef NameRef; + StringRef ShortNameRef; + StringRef FileRef; + + if (Name) + NameRef = StringPool.internString(Name); + else if (Tag == dwarf::DW_TAG_namespace) + // FIXME: For dsymutil-classic compatibility. I think uniquing within + // anonymous namespaces is wrong. There is no ODR guarantee there. + NameRef = StringPool.internString("(anonymous namespace)"); + + if (ShortName && ShortName != Name) + ShortNameRef = StringPool.internString(ShortName); + else + ShortNameRef = NameRef; + + 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 PointerIntPair(nullptr); + + unsigned Line = 0; + unsigned ByteSize = std::numeric_limits::max(); + + if (!InClangModule) { + // 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 || !Name) { + if (unsigned FileNum = + dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) { + if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( + &U.getOrigUnit())) { + // 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 (!Name && Tag == dwarf::DW_TAG_namespace) + FileNum = 1; + + if (LT->hasFileAtIndex(FileNum)) { + Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0); + // Cache the resolved paths based on the index in the line table, + // because calling realpath is expansive. + StringRef ResolvedPath = U.getResolvedPath(FileNum); + if (!ResolvedPath.empty()) { + FileRef = ResolvedPath; + } else { + std::string File; + bool FoundFileName = LT->getFileNameByIndex( + FileNum, U.getOrigUnit().getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + File); + (void)FoundFileName; + assert(FoundFileName && "Must get file name from line table"); + // Second level of caching, this time based on the file's parent + // path. + FileRef = PathResolver.resolve(File, StringPool); + U.setResolvedPath(FileNum, FileRef); + } + } + } + } + } + } + + if (!Line && NameRef.empty()) + return PointerIntPair(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(Context.getQualifiedNameHash(), Tag, NameRef); + + // FIXME: dsymutil-classic compatibility: when we don't have a name, + // use the filename. + if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)") + Hash = hash_combine(Hash, FileRef); + + // Now look if this context already exists. + DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); + auto ContextIter = Contexts.find(&Key); + + if (ContextIter == Contexts.end()) { + // The context wasn't found. + bool Inserted; + DeclContext *NewContext = + new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, + Context, DIE, U.getUniqueID()); + std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); + assert(Inserted && "Failed to insert DeclContext"); + (void)Inserted; + } else if (Tag != dwarf::DW_TAG_namespace && + !(*ContextIter)->setLastSeenDIE(U, DIE)) { + // The context was found, but it is ambiguous with another context + // in the same file. Mark it invalid. + return PointerIntPair(*ContextIter, /* Invalid= */ 1); + } + + assert(ContextIter != Contexts.end()); + // FIXME: dsymutil-classic compatibility. Union types aren't + // uniques, but their children might be. + if ((Tag == dwarf::DW_TAG_subprogram && + Context.getTag() != dwarf::DW_TAG_structure_type && + Context.getTag() != dwarf::DW_TAG_class_type) || + (Tag == dwarf::DW_TAG_union_type)) + return PointerIntPair(*ContextIter, /* Invalid= */ 1); + + return PointerIntPair(*ContextIter); +} +} // namespace dsymutil +} // namespace llvm Index: llvm/tools/dsymutil/DwarfLinker.h =================================================================== --- /dev/null +++ llvm/tools/dsymutil/DwarfLinker.h @@ -0,0 +1,480 @@ +//===- tools/dsymutil/DwarfLinker.h - Dwarf debug info linker ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H +#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H + +#include "BinaryHolder.h" +#include "CompileUnit.h" +#include "DebugMap.h" +#include "DeclContext.h" +#include "DwarfStreamer.h" +#include "LinkUtils.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" + +namespace llvm { +namespace dsymutil { + +/// Partial address range for debug map objects. Besides an offset, only the +/// HighPC is stored. The structure is stored in a map where the LowPC is the +/// key. +struct DebugMapObjectRange { + /// Function HighPC. + uint64_t HighPC; + /// Offset to apply to the linked address. + int64_t Offset; + + DebugMapObjectRange(uint64_t EndPC, int64_t Offset) + : HighPC(EndPC), Offset(Offset) {} + + DebugMapObjectRange() : HighPC(0), Offset(0) {} +}; + +/// Map LowPC to DebugMapObjectRange. +using RangesTy = std::map; +using UnitListTy = std::vector>; + +/// The core of the Dwarf linking logic. +/// +/// The link 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 are present in the linked +/// binary (and thus have entries in the debug map). All the debug +/// information that will be linked (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 +/// correspond to a debug map entry at specific places (the low_pc for +/// a function, the location for a variable). These relocations are +/// called ValidRelocs in the DwarfLinker and are gathered as a very +/// first step when we start processing a DebugMapObject. +class DwarfLinker { +public: + DwarfLinker(raw_fd_ostream &OutFile, const LinkOptions &Options) + : OutFile(OutFile), Options(Options) {} + + /// Link the contents of the DebugMap. + bool link(const DebugMap &); + + void reportWarning(const Twine &Warning, const DebugMapObject &DMO, + const DWARFDie *DIE = nullptr) const; + +private: + /// Remembers the newest DWARF version we've seen in a unit. + void maybeUpdateMaxDwarfVersion(unsigned Version) { + if (MaxDwarfVersion < Version) + MaxDwarfVersion = Version; + } + + /// Emit warnings as Dwarf compile units to leave a trail after linking. + bool emitPaperTrailWarnings(const DebugMapObject &DMO, const DebugMap &Map, + OffsetsStringPool &StringPool); + + /// Keeps track of relocations. + class RelocationManager { + struct ValidReloc { + uint32_t Offset; + uint32_t Size; + uint64_t Addend; + const DebugMapObject::DebugMapEntry *Mapping; + + ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, + const DebugMapObject::DebugMapEntry *Mapping) + : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} + + bool operator<(const ValidReloc &RHS) const { + return Offset < RHS.Offset; + } + }; + + const DwarfLinker &Linker; + + /// The valid relocations for the current DebugMapObject. + /// This vector is sorted by relocation offset. + std::vector ValidRelocs; + + /// Index into ValidRelocs of the next relocation to consider. As we walk + /// the DIEs in acsending file offset and as ValidRelocs is sorted by file + /// offset, keeping this index up to date is all we have to do to have a + /// cheap lookup during the root DIE selection and during DIE cloning. + unsigned NextValidReloc = 0; + + public: + RelocationManager(DwarfLinker &Linker) : Linker(Linker) {} + + bool hasValidRelocs() const { return !ValidRelocs.empty(); } + + /// Reset the NextValidReloc counter. + void resetValidRelocs() { NextValidReloc = 0; } + + /// \defgroup FindValidRelocations Translate debug map into a list + /// of relevant relocations + /// + /// @{ + bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + bool findValidRelocs(const object::SectionRef &Section, + const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + void findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, + const DebugMapObject &DMO); + /// @} + + bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info); + + bool applyValidRelocs(MutableArrayRef Data, uint32_t BaseOffset, + bool isLittleEndian); + }; + + /// Keeps track of data associated with one object during linking. + struct LinkContext { + DebugMapObject &DMO; + BinaryHolder BinHolder; + const object::ObjectFile *ObjectFile; + RelocationManager RelocMgr; + std::unique_ptr DwarfContext; + RangesTy Ranges; + UnitListTy CompileUnits; + + LinkContext(const DebugMap &Map, DwarfLinker &Linker, DebugMapObject &DMO, + bool Verbose = false) + : DMO(DMO), BinHolder(Verbose), RelocMgr(Linker) { + // Swift ASTs are not object files. + if (DMO.getType() == MachO::N_AST) { + ObjectFile = nullptr; + return; + } + auto ErrOrObj = Linker.loadObject(BinHolder, DMO, Map); + ObjectFile = ErrOrObj ? &*ErrOrObj : nullptr; + DwarfContext = ObjectFile ? DWARFContext::create(*ObjectFile) : nullptr; + } + + /// Clear compile units and ranges. + void Clear() { + CompileUnits.clear(); + Ranges.clear(); + } + }; + + /// Called at the start of a debug object link. + void startDebugObject(LinkContext &Context); + + /// Called at the end of a debug object link. + void endDebugObject(LinkContext &Context); + + /// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries. + /// + /// @{ + /// 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. + bool lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, + UnitListTy &Units, const DWARFDie &DIE, + const DebugMapObject &DMO, 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(const DWARFDie &CUDie, const DWARFUnit &Unit, + DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, + OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPoolStringPool, + DeclContextTree &ODRContexts, unsigned &UnitID, + unsigned Indent = 0); + + /// Recursively add the debug info in this clang module .pcm + /// file (and all the modules imported by it in a bottom-up fashion) + /// to Units. + Error loadClangModule(StringRef Filename, StringRef ModulePath, + StringRef ModuleName, uint64_t DwoId, + DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, unsigned &UnitID, + unsigned Indent = 0); + + /// Flags passed to DwarfLinker::lookForDIEsToKeep + enum TraversalFlags { + TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. + TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope. + TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. + TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. + TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents. + TF_SkipPC = 1 << 5, ///< Skip all location attributes. + }; + + /// Mark the passed DIE as well as all the ones it depends on as kept. + void keepDIEAndDependencies(RelocationManager &RelocMgr, RangesTy &Ranges, + UnitListTy &Units, const DWARFDie &DIE, + CompileUnit::DIEInfo &MyInfo, + const DebugMapObject &DMO, CompileUnit &CU, + bool UseODR); + + unsigned shouldKeepDIE(RelocationManager &RelocMgr, RangesTy &Ranges, + const DWARFDie &DIE, const DebugMapObject &DMO, + CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr, + const DWARFDie &DIE, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, unsigned Flags); + + unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr, + RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info); + /// @} + + /// \defgroup Linking Methods used to link the debug information + /// + /// @{ + + class DIECloner { + DwarfLinker &Linker; + RelocationManager &RelocMgr; + + /// Allocator used for all the DIEValue objects. + BumpPtrAllocator &DIEAlloc; + + std::vector> &CompileUnits; + LinkOptions Options; + + public: + DIECloner(DwarfLinker &Linker, RelocationManager &RelocMgr, + BumpPtrAllocator &DIEAlloc, + std::vector> &CompileUnits, + LinkOptions &Options) + : Linker(Linker), RelocMgr(RelocMgr), DIEAlloc(DIEAlloc), + CompileUnits(CompileUnits), Options(Options) {} + + /// 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 + /// debug map. + /// + /// \param OutOffset is the offset the cloned DIE in the output + /// compile unit. + /// \param PCOffset (while cloning a function scope) is the offset + /// 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 DebugMapObject &DMO, + CompileUnit &U, OffsetsStringPool &StringPool, + int64_t PCOffset, uint32_t OutOffset, unsigned Flags, + DIE *Die = nullptr); + + /// Construct the output DIE tree by cloning the DIEs we + /// chose to keep above. If there are no valid relocs, then there's + /// nothing to clone/emit. + void cloneAllCompileUnits(DWARFContext &DwarfContext, + const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool); + + 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; + + /// 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; + }; + + /// Helper for cloneDIE. + unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, + const DebugMapObject &DMO, CompileUnit &U, + OffsetsStringPool &StringPool, + const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, + AttributesInfo &AttrInfo); + + /// 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, + AttributesInfo &Info); + + /// 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 AttrSize, + const DWARFFormValue &Val, + const DebugMapObject &DMO, + CompileUnit &Unit); + + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize); + + /// 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 DebugMapObject &DMO, CompileUnit &U, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize, + AttributesInfo &Info); + + /// Get the potential name and mangled name for the entity + /// described by \p Die and store them in \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); + + uint32_t hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, + const DebugMapObject &DMO, + int RecurseDepth = 0); + + /// Helper for cloneDIE. + void addObjCAccelerator(CompileUnit &Unit, const DIE *Die, + DwarfStringPoolEntryRef Name, + OffsetsStringPool &StringPool, bool SkipPubSection); + }; + + /// Assign an abbreviation number to \p Abbrev + void AssignAbbrev(DIEAbbrev &Abbrev); + + /// Compute and emit debug_ranges section for \p Unit, and + /// patch the attributes referencing it. + void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf, + const DebugMapObject &DMO) const; + + /// Generate and emit the DW_AT_ranges attribute for a compile_unit if it had + /// one. + void generateUnitRanges(CompileUnit &Unit) 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, + RangesTy &Ranges, const DebugMapObject &DMO); + + /// Emit the accelerator entries for \p Unit. + void emitAcceleratorEntriesForUnit(CompileUnit &Unit); + + /// Patch the frame info for an object file and emit it. + void patchFrameInfoForObject(const DebugMapObject &, RangesTy &Ranges, + DWARFContext &, unsigned AddressSize); + + /// 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; + + /// 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; + /// @} + + /// \defgroup Helpers Various helper methods. + /// + /// @{ + bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); + + /// Attempt to load a debug object from disk. + ErrorOr loadObject(BinaryHolder &BinaryHolder, + const DebugMapObject &Obj, + const DebugMap &Map); + /// @} + + raw_fd_ostream &OutFile; + LinkOptions Options; + std::unique_ptr Streamer; + uint64_t OutputDebugInfoSize; + unsigned MaxDwarfVersion = 0; + + /// The CIEs that have been emitted in the output section. The actual CIE + /// data serves a the key to this StringMap, this takes care of comparing the + /// semantics of CIEs defined in different object files. + StringMap EmittedCIEs; + + /// Offset of the last CIE that has been emitted in the output + /// debug_frame section. + uint32_t LastCIEOffset = 0; + + /// Apple accelerator tables. + AccelTable AppleNames; + AccelTable AppleNamespaces; + AccelTable AppleObjc; + AccelTable AppleTypes; + + /// Mapping the PCM filename to the DwoId. + StringMap ClangModules; + + bool ModuleCacheHintDisplayed = false; + bool ArchiveHintDisplayed = false; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -1,14 +1,17 @@ //===- tools/dsymutil/DwarfLinker.cpp - Dwarf debug info linker -----------===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +#include "DwarfLinker.h" #include "BinaryHolder.h" #include "DebugMap.h" +#include "DeclContext.h" +#include "DwarfStreamer.h" #include "MachOUtils.h" #include "NonRelocatableStringpool.h" #include "dsymutil.h" @@ -58,7 +61,6 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.inc" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" @@ -102,1794 +104,6 @@ namespace llvm { namespace dsymutil { -namespace { - -/// Helper for making strong types. -template class StrongType : public T { -public: - template - explicit StrongType(Args... A) : T(std::forward(A)...) {} -}; - -/// It's very easy to introduce bugs by passing the wrong string pool. By using -/// strong types the interface enforces that the right kind of pool is used. -struct UniqueTag {}; -struct OffsetsTag {}; -using UniquingStringPool = StrongType; -using OffsetsStringPool = StrongType; - -/// Small helper that resolves and caches file paths. This helps reduce the -/// number of calls to realpath which is expensive. We assume the input are -/// files, and cache the realpath of their parent. This way we can quickly -/// resolve different files under the same path. -class CachedPathResolver { -public: - /// Resolve a path by calling realpath and cache its result. The returned - /// StringRef is interned in the given \p StringPool. - StringRef resolve(std::string Path, NonRelocatableStringpool &StringPool) { - StringRef FileName = sys::path::filename(Path); - SmallString<256> ParentPath = sys::path::parent_path(Path); - - // If the ParentPath has not yet been resolved, resolve and cache it for - // future look-ups. - if (!ResolvedPaths.count(ParentPath)) { - SmallString<256> RealPath; - sys::fs::real_path(ParentPath, RealPath); - ResolvedPaths.insert({ParentPath, StringRef(RealPath).str()}); - } - - // Join the file name again with the resolved path. - SmallString<256> ResolvedPath(ResolvedPaths[ParentPath]); - sys::path::append(ResolvedPath, FileName); - return StringPool.internString(ResolvedPath); - } - -private: - StringMap ResolvedPaths; -}; - -/// Retrieve the section named \a SecName in \a Obj. -/// -/// To accommodate for platform discrepancies, the name passed should be -/// (for example) 'debug_info' to match either '__debug_info' or '.debug_info'. -/// This function will strip the initial platform-specific characters. -static Optional -getSectionByName(const object::ObjectFile &Obj, StringRef SecName) { - for (const object::SectionRef &Section : Obj.sections()) { - StringRef SectionName; - Section.getName(SectionName); - SectionName = SectionName.substr(SectionName.find_first_not_of("._")); - if (SectionName != SecName) - continue; - return Section; - } - return None; -} - -template -using HalfOpenIntervalMap = - IntervalMap::LeafSize, - IntervalMapHalfOpenInfo>; - -using FunctionIntervals = HalfOpenIntervalMap; - -// FIXME: Delete this structure. -struct PatchLocation { - DIE::value_iterator I; - - PatchLocation() = default; - PatchLocation(DIE::value_iterator I) : I(I) {} - - void set(uint64_t New) const { - assert(I); - const auto &Old = *I; - assert(Old.getType() == DIEValue::isInteger); - *I = DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(New)); - } - - uint64_t get() const { - assert(I); - return I->getDIEInteger().getValue(); - } -}; - -class CompileUnit; -struct DeclMapInfo; - -/// 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 { - friend DeclMapInfo; - - unsigned QualifiedNameHash = 0; - uint32_t Line = 0; - uint32_t ByteSize = 0; - uint16_t Tag = dwarf::DW_TAG_compile_unit; - unsigned DefinedInClangModule : 1; - StringRef Name; - StringRef File; - const DeclContext &Parent; - DWARFDie LastSeenDIE; - uint32_t LastSeenCompileUnitID = 0; - uint32_t CanonicalDIEOffset = 0; - -public: - using Map = DenseSet; - - DeclContext() : DefinedInClangModule(0), Parent(*this) {} - - DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, - StringRef Name, StringRef File, const DeclContext &Parent, - DWARFDie LastSeenDIE = DWARFDie(), unsigned CUId = 0) - : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), - DefinedInClangModule(0), Name(Name), File(File), Parent(Parent), - LastSeenDIE(LastSeenDIE), LastSeenCompileUnitID(CUId) {} - - uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } - - bool setLastSeenDIE(CompileUnit &U, const DWARFDie &Die); - - uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } - void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } - - bool isDefinedInClangModule() const { return DefinedInClangModule; } - void setDefinedInClangModule(bool Val) { DefinedInClangModule = Val; } - - uint16_t getTag() const { return Tag; } - StringRef getName() const { return Name; } -}; - -/// 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->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && - LHS->Name.data() == RHS->Name.data() && - LHS->File.data() == RHS->File.data() && - LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; - } -}; - -/// This class gives a tree-like API to the DenseMap that stores the -/// DeclContext objects. It also holds the BumpPtrAllocator where -/// these objects will be allocated. -class DeclContextTree { - BumpPtrAllocator Allocator; - DeclContext Root; - DeclContext::Map Contexts; - - /// Cache resolved paths from the line table. - CachedPathResolver PathResolver; - -public: - /// 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. - /// - /// FIXME: The invalid bit along the return value is to emulate some - /// dsymutil-classic functionality. - PointerIntPair - getChildDeclContext(DeclContext &Context, const DWARFDie &DIE, - CompileUnit &Unit, UniquingStringPool &StringPool, - bool InClangModule); - - DeclContext &getRoot() { return Root; } -}; - -/// Stores all information relating to a compile unit, be it in its original -/// instance in the object file to its brand new cloned and linked DIE tree. -class CompileUnit { -public: - /// Information gathered about a DIE in the object file. - struct DIEInfo { - /// Address offset to apply to the described entity. - int64_t AddrAdjust; - - /// ODR Declaration context. - DeclContext *Ctxt; - - /// Cloned version of that DIE. - DIE *Clone; - - /// The index of this DIE's parent. - uint32_t ParentIdx; - - /// 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 Incomplete : 1; - }; - - CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, - StringRef ClangModuleName) - : OrigUnit(OrigUnit), ID(ID), Ranges(RangeAlloc), - ClangModuleName(ClangModuleName) { - Info.resize(OrigUnit.getNumDIEs()); - - auto CUDie = OrigUnit.getUnitDIE(false); - if (!CUDie) { - HasODR = false; - return; - } - 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); - else - HasODR = false; - } - - DWARFUnit &getOrigUnit() const { return OrigUnit; } - - unsigned getUniqueID() const { return ID; } - - void createOutputDIE() { - NewUnit.emplace(OrigUnit.getVersion(), OrigUnit.getAddressByteSize(), - OrigUnit.getUnitDIE().getTag()); - } - - DIE *getOutputUnitDIE() const { - if (NewUnit) - return &const_cast(*NewUnit).getUnitDie(); - return nullptr; - } - - bool hasODR() const { return HasODR; } - bool isClangModule() const { return !ClangModuleName.empty(); } - const std::string &getClangModuleName() const { return ClangModuleName; } - - DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } - const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } - - uint64_t getStartOffset() const { return StartOffset; } - uint64_t getNextUnitOffset() const { return NextUnitOffset; } - void setStartOffset(uint64_t DebugInfoSize) { StartOffset = DebugInfoSize; } - - uint64_t getLowPc() const { return LowPc; } - uint64_t getHighPc() const { return HighPc; } - bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } - - Optional getUnitRangesAttribute() const { - return UnitRangeAttribute; - } - - const FunctionIntervals &getFunctionRanges() const { return Ranges; } - - const std::vector &getRangesAttributes() const { - return RangeAttributes; - } - - const std::vector> & - getLocationAttributes() const { - return LocationAttributes; - } - - void setHasInterestingContent() { HasInterestingContent = true; } - bool hasInterestingContent() { return HasInterestingContent; } - - /// 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(); - - /// Compute the end offset for this unit. Must be called after the CU's DIEs - /// have been cloned. \returns the next unit offset (which is also the - /// current debug_info section size). - uint64_t computeNextUnitOffset(); - - /// Keep track of a forward reference to DIE \p Die 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 noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - DeclContext *Ctxt, PatchLocation Attr); - - /// Apply all fixups recorded by noteForwardReference(). - void fixupForwardReferences(); - - /// 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, PatchLocation Attr); - - /// Keep track of a location attribute pointing to a location list in the - /// debug_loc section. - void noteLocationAttribute(PatchLocation Attr, int64_t PcOffset); - - /// Add a name accelerator entry for \a Die with \a Name. - void addNamespaceAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name); - - /// Add a name accelerator entry for \a Die with \a Name. - void addNameAccelerator(const DIE *Die, DwarfStringPoolEntryRef 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, DwarfStringPoolEntryRef 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, DwarfStringPoolEntryRef Name, - bool ObjcClassImplementation, - uint32_t QualifiedNameHash); - - struct AccelInfo { - /// Name of the entry. - DwarfStringPoolEntryRef Name; - - /// DIE this entry describes. - const DIE *Die; - - /// 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, const DIE *Die, - bool SkipPubSection = false) - : Name(Name), Die(Die), SkipPubSection(SkipPubSection) {} - - AccelInfo(DwarfStringPoolEntryRef Name, const DIE *Die, - uint32_t QualifiedNameHash, bool ObjCClassIsImplementation) - : Name(Name), Die(Die), QualifiedNameHash(QualifiedNameHash), - SkipPubSection(false), - ObjcClassImplementation(ObjCClassIsImplementation) {} - }; - - const std::vector &getPubnames() const { return Pubnames; } - const std::vector &getPubtypes() const { return Pubtypes; } - const std::vector &getNamespaces() const { return Namespaces; } - const std::vector &getObjC() const { return ObjC; } - - /// Get the full path for file \a FileNum in the line table - StringRef getResolvedPath(unsigned FileNum) { - if (FileNum >= ResolvedPaths.size()) - return StringRef(); - return ResolvedPaths[FileNum]; - } - - /// Set the fully resolved path for the line-table's file \a FileNum - /// to \a Path. - void setResolvedPath(unsigned FileNum, StringRef Path) { - if (ResolvedPaths.size() <= FileNum) - ResolvedPaths.resize(FileNum + 1); - ResolvedPaths[FileNum] = Path; - } - -private: - DWARFUnit &OrigUnit; - unsigned ID; - std::vector Info; ///< DIE info indexed by DIE index. - Optional NewUnit; - - uint64_t StartOffset; - uint64_t NextUnitOffset; - - uint64_t LowPc = std::numeric_limits::max(); - uint64_t HighPc = 0; - - /// 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 cross-cu forward references the target DIE's offset - /// isn't known you emit the reference attribute. - std::vector< - std::tuple> - ForwardDIEReferences; - - 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 liked 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; - /// @} - - /// Cached resolved paths from the line table. - /// Note, the StringRefs here point in to the intern (uniquing) string pool. - /// This means that a StringRef returned here doesn't need to then be uniqued - /// for the purposes of getting a unique address for each string. - std::vector ResolvedPaths; - - /// Is this unit subject to the ODR rule? - bool HasODR; - - /// Did a DIE actually contain a valid reloc? - bool HasInterestingContent; - - /// If this is a Clang module, this holds the module's name. - std::string ClangModuleName; -}; - -/// Check if the DIE at \p Idx is in the scope of a function. -static bool inFunctionScope(CompileUnit &U, unsigned Idx) { - while (Idx) { - if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram) - return true; - Idx = U.getInfo(Idx).ParentIdx; - } - return false; -} -} // namespace - -void warn(Twine Warning, Twine Context) { - WithColor::warning() << Warning + "\n"; - if (!Context.isTriviallyEmpty()) - WithColor::note() << Twine("while processing ") + Context + "\n"; -} - -bool error(Twine Error, Twine Context) { - WithColor::error() << Error + "\n"; - if (!Context.isTriviallyEmpty()) - WithColor::note() << Twine("while processing ") + Context + "\n"; - return false; -} - -void CompileUnit::markEverythingAsKept() { - unsigned Idx = 0; - - setHasInterestingContent(); - - for (auto &I : Info) { - // Mark everything that wasn't explicit marked for pruning. - I.Keep = !I.Prune; - auto DIE = OrigUnit.getDIEAtIndex(Idx++); - - // Try to guess which DIEs must go to the accelerator tables. We do that - // just for variables, because functions will be handled depending on - // whether they carry a DW_AT_low_pc attribute or not. - if (DIE.getTag() != dwarf::DW_TAG_variable && - DIE.getTag() != dwarf::DW_TAG_constant) - continue; - - Optional Value; - if (!(Value = DIE.find(dwarf::DW_AT_location))) { - if ((Value = DIE.find(dwarf::DW_AT_const_value)) && - !inFunctionScope(*this, I.ParentIdx)) - I.InDebugMap = true; - continue; - } - if (auto Block = Value->getAsBlock()) { - if (Block->size() > OrigUnit.getAddressByteSize() && - (*Block)[0] == dwarf::DW_OP_addr) - I.InDebugMap = true; - } - } -} - -uint64_t CompileUnit::computeNextUnitOffset() { - NextUnitOffset = StartOffset + 11 /* Header size */; - // The root DIE might be null, meaning that the Unit had nothing to - // contribute to the linked output. In that case, we will emit the - // unit header without any actual DIE. - if (NewUnit) - NextUnitOffset += NewUnit->getUnitDie().getSize(); - return NextUnitOffset; -} - -/// Keep track of a forward cross-cu reference from this unit -/// to \p Die that lives in \p RefUnit. -void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - DeclContext *Ctxt, PatchLocation Attr) { - ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); -} - -void CompileUnit::fixupForwardReferences() { - for (const auto &Ref : ForwardDIEReferences) { - DIE *RefDie; - const CompileUnit *RefUnit; - PatchLocation Attr; - DeclContext *Ctxt; - std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; - if (Ctxt && Ctxt->getCanonicalDIEOffset()) - Attr.set(Ctxt->getCanonicalDIEOffset()); - else - Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); - } -} - -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) { - 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, PatchLocation Attr) { - if (Die.getTag() != dwarf::DW_TAG_compile_unit) - RangeAttributes.push_back(Attr); - else - UnitRangeAttribute = Attr; -} - -void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) { - LocationAttributes.emplace_back(Attr, PcOffset); -} - -void CompileUnit::addNamespaceAccelerator(const DIE *Die, - DwarfStringPoolEntryRef Name) { - Namespaces.emplace_back(Name, Die); -} - -void CompileUnit::addObjCAccelerator(const DIE *Die, - DwarfStringPoolEntryRef Name, - bool SkipPubSection) { - ObjC.emplace_back(Name, Die, SkipPubSection); -} - -void CompileUnit::addNameAccelerator(const DIE *Die, - DwarfStringPoolEntryRef Name, - bool SkipPubSection) { - Pubnames.emplace_back(Name, Die, SkipPubSection); -} - -void CompileUnit::addTypeAccelerator(const DIE *Die, - DwarfStringPoolEntryRef Name, - bool ObjcClassImplementation, - uint32_t QualifiedNameHash) { - Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation); -} - -namespace { - -/// 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 { - /// \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; - MCCodeEmitter *MCE; // Owned by MCStreamer - MCStreamer *MS; // Owned by AsmPrinter - std::unique_ptr TM; - std::unique_ptr Asm; - /// @} - - /// The file we stream the linked Dwarf to. - raw_fd_ostream &OutFile; - - uint32_t RangesSectionSize; - uint32_t LocSectionSize; - uint32_t LineSectionSize; - uint32_t FrameSectionSize; - - /// 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); - -public: - DwarfStreamer(raw_fd_ostream &OutFile) : OutFile(OutFile) {} - bool init(Triple TheTriple); - - /// Dump the file to the disk. - bool finish(const DebugMap &); - - 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); - - /// Recursively emit the DIE tree rooted at \p Die. - void emitDIE(DIE &Die); - - /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. - void emitAbbrevs(const std::vector> &Abbrevs, - unsigned DwarfVersion); - - /// Emit the string table described by \p Pool. - void emitStrings(const NonRelocatableStringpool &Pool); - - /// 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); - - /// 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); - - uint32_t getRangesSectionSize() const { 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(const CompileUnit &Unit, DWARFContext &Dwarf); - - /// 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); - - /// Copy over the debug sections that are not modified when updating. - void copyInvariantDebugSection(const object::ObjectFile &Obj, LinkOptions &); - - uint32_t getLineSectionSize() const { return LineSectionSize; } - - /// Emit the .debug_pubnames contribution for \p Unit. - void emitPubNamesForUnit(const CompileUnit &Unit); - - /// Emit the .debug_pubtypes contribution for \p Unit. - void emitPubTypesForUnit(const CompileUnit &Unit); - - /// Emit a CIE. - void emitCIE(StringRef CIEBytes); - - /// Emit an FDE with data \p Bytes. - void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, - StringRef Bytes); - - /// Emit Apple namespaces accelerator table. - void emitAppleNamespaces(AccelTable &Table); - - /// Emit Apple names accelerator table. - void emitAppleNames(AccelTable &Table); - - /// Emit Apple Objective-C accelerator table. - void emitAppleObjc(AccelTable &Table); - - /// Emit Apple type accelerator table. - void emitAppleTypes(AccelTable &Table); - - uint32_t getFrameSectionSize() const { return FrameSectionSize; } -}; - -} // end anonymous namespace - -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); - 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); - - MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); - if (!MAI) - return error("no asm info for target " + TripleName, Context); - - 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); - - MCTargetOptions Options; - MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options); - if (!MAB) - return error("no asm backend for target " + TripleName, Context); - - MII.reset(TheTarget->createMCInstrInfo()); - if (!MII) - return error("no instr info info for target " + TripleName, Context); - - MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); - if (!MCE) - return error("no code emitter for target " + TripleName, Context); - - MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); - MS = TheTarget->createMCObjectStreamer( - TheTriple, *MC, std::unique_ptr(MAB), - MAB->createObjectWriter(OutFile), std::unique_ptr(MCE), - *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, - /*DWARFMustBeAtTheEnd*/ false); - if (!MS) - return error("no object streamer for target " + TripleName, Context); - - // 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); - - Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr(MS))); - if (!Asm) - return error("no asm printer for target " + TripleName, Context); - - RangesSectionSize = 0; - LocSectionSize = 0; - LineSectionSize = 0; - FrameSectionSize = 0; - - return true; -} - -bool DwarfStreamer::finish(const DebugMap &DM) { - bool Result = true; - if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty()) - Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile); - else - MS->Finish(); - return Result; -} - -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 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. -void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { - unsigned Version = Unit.getOrigUnit().getVersion(); - switchToDebugInfoSection(Version); - - // 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(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4); - Asm->emitInt16(Version); - // 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()); -} - -/// 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); -} - -/// Emit the debug_str section stored in \p Pool. -void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { - Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection()); - std::vector Entries = Pool.getEntries(); - for (auto Entry : Entries) { - if (Entry.getIndex() == -1U) - break; - // Emit the string itself. - Asm->OutStreamer->EmitBytes(Entry.getString()); - // Emit a null terminator. - Asm->emitInt8(0); - } -} - -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(1 << 5); - 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.begin(), Ranges.end()); - - 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, TupleSize); - - Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Arange length - Asm->OutStreamer->EmitLabel(BeginLabel); - Asm->emitInt16(dwarf::DW_ARANGES_VERSION); // Version number - Asm->emitInt32(Unit.getStartOffset()); // 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(const CompileUnit &Unit, - DWARFContext &Dwarf) { - const auto &Attributes = Unit.getLocationAttributes(); - - if (Attributes.empty()) - return; - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); - - unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); - 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(); - - for (const auto &Attr : Attributes) { - uint32_t Offset = Attr.first.get(); - Attr.first.set(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.second + UnitPcOffset; - while (Data.isValidOffset(Offset)) { - uint64_t Low = Data.getUnsigned(&Offset, AddressSize); - uint64_t High = Data.getUnsigned(&Offset, AddressSize); - LocSectionSize += 2 * AddressSize; - if (Low == 0 && High == 0) { - Asm->OutStreamer->EmitIntValue(0, AddressSize); - Asm->OutStreamer->EmitIntValue(0, AddressSize); - break; - } - Asm->OutStreamer->EmitIntValue(Low + LocPcOffset, AddressSize); - Asm->OutStreamer->EmitIntValue(High + LocPcOffset, AddressSize); - uint64_t Length = Data.getU16(&Offset); - Asm->OutStreamer->EmitIntValue(Length, 2); - // Just copy the bytes over. - Asm->OutStreamer->EmitBytes( - StringRef(InputSec.Data.substr(Offset, 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, PointerSize); - LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); - AddressDelta = 0; - } else { - AddressDelta = (Row.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; - 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); -} - -static void emitSectionContents(const object::ObjectFile &Obj, - StringRef SecName, MCStreamer *MS) { - StringRef Contents; - if (auto Sec = getSectionByName(Obj, SecName)) - if (!Sec->getContents(Contents)) - MS->EmitBytes(Contents); -} - -void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj, - LinkOptions &Options) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); - emitSectionContents(Obj, "debug_line", MS); - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); - emitSectionContents(Obj, "debug_loc", MS); - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); - emitSectionContents(Obj, "debug_ranges", MS); - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); - emitSectionContents(Obj, "debug_frame", MS); - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); - emitSectionContents(Obj, "debug_aranges", MS); -} - -/// Emit the pubnames or pubtypes section contribution for \p -/// Unit into \p Sec. The data is provided in \p Names. -void DwarfStreamer::emitPubSectionForUnit( - 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()); // Unit offset - Asm->emitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset()); // Size - HeaderEmitted = true; - } - Asm->emitInt32(Name.Die->getOffset()); - - // 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; -} - -namespace { - -/// Partial address range for debug map objects. Besides an offset, only the -/// HighPC is stored. The structure is stored in a map where the LowPC is the -/// key. -struct DebugMapObjectRange { - /// Function HighPC. - uint64_t HighPC; - /// Offset to apply to the linked address. - int64_t Offset; - - DebugMapObjectRange(uint64_t EndPC, int64_t Offset) - : HighPC(EndPC), Offset(Offset) {} - - DebugMapObjectRange() : HighPC(0), Offset(0) {} -}; - -/// Map LowPC to DebugMapObjectRange. -using RangesTy = std::map; -using UnitListTy = std::vector>; - -/// The core of the Dwarf linking logic. -/// -/// The link 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 are present in the linked -/// binary (and thus have entries in the debug map). All the debug -/// information that will be linked (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 -/// correspond to a debug map entry at specific places (the low_pc for -/// a function, the location for a variable). These relocations are -/// called ValidRelocs in the DwarfLinker and are gathered as a very -/// first step when we start processing a DebugMapObject. -class DwarfLinker { -public: - DwarfLinker(raw_fd_ostream &OutFile, const LinkOptions &Options) - : OutFile(OutFile), Options(Options) {} - - /// Link the contents of the DebugMap. - bool link(const DebugMap &); - - void reportWarning(const Twine &Warning, const DebugMapObject &DMO, - const DWARFDie *DIE = nullptr) const; - -private: - /// Remembers the newest DWARF version we've seen in a unit. - void maybeUpdateMaxDwarfVersion(unsigned Version) { - if (MaxDwarfVersion < Version) - MaxDwarfVersion = Version; - } - - /// Emit warnings as Dwarf compile units to leave a trail after linking. - bool emitPaperTrailWarnings(const DebugMapObject &DMO, const DebugMap &Map, - OffsetsStringPool &StringPool); - - /// Keeps track of relocations. - class RelocationManager { - struct ValidReloc { - uint32_t Offset; - uint32_t Size; - uint64_t Addend; - const DebugMapObject::DebugMapEntry *Mapping; - - ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, - const DebugMapObject::DebugMapEntry *Mapping) - : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} - - bool operator<(const ValidReloc &RHS) const { - return Offset < RHS.Offset; - } - }; - - const DwarfLinker &Linker; - - /// The valid relocations for the current DebugMapObject. - /// This vector is sorted by relocation offset. - std::vector ValidRelocs; - - /// Index into ValidRelocs of the next relocation to consider. As we walk - /// the DIEs in acsending file offset and as ValidRelocs is sorted by file - /// offset, keeping this index up to date is all we have to do to have a - /// cheap lookup during the root DIE selection and during DIE cloning. - unsigned NextValidReloc = 0; - - public: - RelocationManager(DwarfLinker &Linker) : Linker(Linker) {} - - bool hasValidRelocs() const { return !ValidRelocs.empty(); } - - /// Reset the NextValidReloc counter. - void resetValidRelocs() { NextValidReloc = 0; } - - /// \defgroup FindValidRelocations Translate debug map into a list - /// of relevant relocations - /// - /// @{ - bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, - const DebugMapObject &DMO); - - bool findValidRelocs(const object::SectionRef &Section, - const object::ObjectFile &Obj, - const DebugMapObject &DMO); - - void findValidRelocsMachO(const object::SectionRef &Section, - const object::MachOObjectFile &Obj, - const DebugMapObject &DMO); - /// @} - - bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, - CompileUnit::DIEInfo &Info); - - bool applyValidRelocs(MutableArrayRef Data, uint32_t BaseOffset, - bool isLittleEndian); - }; - - /// Keeps track of data associated with one object during linking. - struct LinkContext { - DebugMapObject &DMO; - BinaryHolder BinHolder; - const object::ObjectFile *ObjectFile; - RelocationManager RelocMgr; - std::unique_ptr DwarfContext; - RangesTy Ranges; - UnitListTy CompileUnits; - - LinkContext(const DebugMap &Map, DwarfLinker &Linker, DebugMapObject &DMO, - bool Verbose = false) - : DMO(DMO), BinHolder(Verbose), RelocMgr(Linker) { - // Swift ASTs are not object files. - if (DMO.getType() == MachO::N_AST) { - ObjectFile = nullptr; - return; - } - auto ErrOrObj = Linker.loadObject(BinHolder, DMO, Map); - ObjectFile = ErrOrObj ? &*ErrOrObj : nullptr; - DwarfContext = ObjectFile ? DWARFContext::create(*ObjectFile) : nullptr; - } - - /// Clear compile units and ranges. - void Clear() { - CompileUnits.clear(); - Ranges.clear(); - } - }; - - /// Called at the start of a debug object link. - void startDebugObject(LinkContext &Context); - - /// Called at the end of a debug object link. - void endDebugObject(LinkContext &Context); - - /// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries. - /// - /// @{ - /// 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. - bool lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, - UnitListTy &Units, const DWARFDie &DIE, - const DebugMapObject &DMO, 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(const DWARFDie &CUDie, const DWARFUnit &Unit, - DebugMap &ModuleMap, const DebugMapObject &DMO, - RangesTy &Ranges, - OffsetsStringPool &OffsetsStringPool, - UniquingStringPool &UniquingStringPoolStringPool, - DeclContextTree &ODRContexts, unsigned &UnitID, - unsigned Indent = 0); - - /// Recursively add the debug info in this clang module .pcm - /// file (and all the modules imported by it in a bottom-up fashion) - /// to Units. - Error loadClangModule(StringRef Filename, StringRef ModulePath, - StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, const DebugMapObject &DMO, - RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, - UniquingStringPool &UniquingStringPool, - DeclContextTree &ODRContexts, unsigned &UnitID, - unsigned Indent = 0); - - /// Flags passed to DwarfLinker::lookForDIEsToKeep - enum TraversalFlags { - TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. - TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope. - TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. - TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. - TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents. - TF_SkipPC = 1 << 5, ///< Skip all location attributes. - }; - - /// Mark the passed DIE as well as all the ones it depends on as kept. - void keepDIEAndDependencies(RelocationManager &RelocMgr, RangesTy &Ranges, - UnitListTy &Units, const DWARFDie &DIE, - CompileUnit::DIEInfo &MyInfo, - const DebugMapObject &DMO, CompileUnit &CU, - bool UseODR); - - unsigned shouldKeepDIE(RelocationManager &RelocMgr, RangesTy &Ranges, - const DWARFDie &DIE, const DebugMapObject &DMO, - CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, - unsigned Flags); - - unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, unsigned Flags); - - unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr, - RangesTy &Ranges, const DWARFDie &DIE, - const DebugMapObject &DMO, CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, - unsigned Flags); - - bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, - CompileUnit::DIEInfo &Info); - /// @} - - /// \defgroup Linking Methods used to link the debug information - /// - /// @{ - - class DIECloner { - DwarfLinker &Linker; - RelocationManager &RelocMgr; - - /// Allocator used for all the DIEValue objects. - BumpPtrAllocator &DIEAlloc; - - std::vector> &CompileUnits; - LinkOptions Options; - - public: - DIECloner(DwarfLinker &Linker, RelocationManager &RelocMgr, - BumpPtrAllocator &DIEAlloc, - std::vector> &CompileUnits, - LinkOptions &Options) - : Linker(Linker), RelocMgr(RelocMgr), DIEAlloc(DIEAlloc), - CompileUnits(CompileUnits), Options(Options) {} - - /// 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 - /// debug map. - /// - /// \param OutOffset is the offset the cloned DIE in the output - /// compile unit. - /// \param PCOffset (while cloning a function scope) is the offset - /// 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 DebugMapObject &DMO, - CompileUnit &U, OffsetsStringPool &StringPool, - int64_t PCOffset, uint32_t OutOffset, unsigned Flags, - DIE *Die = nullptr); - - /// Construct the output DIE tree by cloning the DIEs we - /// chose to keep above. If there are no valid relocs, then there's - /// nothing to clone/emit. - void cloneAllCompileUnits(DWARFContext &DwarfContext, - const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool); - - 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; - - /// 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; - }; - - /// Helper for cloneDIE. - unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, - const DebugMapObject &DMO, CompileUnit &U, - OffsetsStringPool &StringPool, - const DWARFFormValue &Val, - const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &AttrInfo); - - /// 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, - AttributesInfo &Info); - - /// 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 AttrSize, - const DWARFFormValue &Val, - const DebugMapObject &DMO, - CompileUnit &Unit); - - /// Clone an attribute referencing another DIE and add - /// it to \p Die. - /// \returns the size of the new attribute. - unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize); - - /// 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 DebugMapObject &DMO, CompileUnit &U, - AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize, - AttributesInfo &Info); - - /// Get the potential name and mangled name for the entity - /// described by \p Die and store them in \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); - - uint32_t hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, - const DebugMapObject &DMO, - int RecurseDepth = 0); - - /// Helper for cloneDIE. - void addObjCAccelerator(CompileUnit &Unit, const DIE *Die, - DwarfStringPoolEntryRef Name, - OffsetsStringPool &StringPool, bool SkipPubSection); - }; - - /// Assign an abbreviation number to \p Abbrev - void AssignAbbrev(DIEAbbrev &Abbrev); - - /// Compute and emit debug_ranges section for \p Unit, and - /// patch the attributes referencing it. - void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf, - const DebugMapObject &DMO) const; - - /// Generate and emit the DW_AT_ranges attribute for a compile_unit if it had - /// one. - void generateUnitRanges(CompileUnit &Unit) 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, - RangesTy &Ranges, const DebugMapObject &DMO); - - /// Emit the accelerator entries for \p Unit. - void emitAcceleratorEntriesForUnit(CompileUnit &Unit); - - /// Patch the frame info for an object file and emit it. - void patchFrameInfoForObject(const DebugMapObject &, RangesTy &Ranges, - DWARFContext &, unsigned AddressSize); - - /// 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; - - /// 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; - /// @} - - /// \defgroup Helpers Various helper methods. - /// - /// @{ - bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); - - /// Attempt to load a debug object from disk. - ErrorOr loadObject(BinaryHolder &BinaryHolder, - const DebugMapObject &Obj, - const DebugMap &Map); - /// @} - - raw_fd_ostream &OutFile; - LinkOptions Options; - std::unique_ptr Streamer; - uint64_t OutputDebugInfoSize; - unsigned MaxDwarfVersion = 0; - - /// The CIEs that have been emitted in the output section. The actual CIE - /// data serves a the key to this StringMap, this takes care of comparing the - /// semantics of CIEs defined in different object files. - StringMap EmittedCIEs; - - /// Offset of the last CIE that has been emitted in the output - /// debug_frame section. - uint32_t LastCIEOffset = 0; - - /// Apple accelerator tables. - AccelTable AppleNames; - AccelTable AppleNamespaces; - AccelTable AppleObjc; - AccelTable AppleTypes; - - /// Mapping the PCM filename to the DwoId. - StringMap ClangModules; - - bool ModuleCacheHintDisplayed = false; - bool ArchiveHintDisplayed = false; -}; - -} // end anonymous namespace - /// Similar to DWARFUnitSection::getUnitForOffset(), but returning our /// CompileUnit object instead. static CompileUnit * @@ -1942,199 +156,6 @@ llvm_unreachable("Improper attribute."); } -/// Set the last DIE/CU a context was seen in and, possibly invalidate the -/// context if it is ambiguous. -/// -/// 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. Make it invalid. -bool DeclContext::setLastSeenDIE(CompileUnit &U, const DWARFDie &Die) { - if (LastSeenCompileUnitID == U.getUniqueID()) { - DWARFUnit &OrigUnit = U.getOrigUnit(); - uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE); - U.getInfo(FirstIdx).Ctxt = nullptr; - return false; - } - - LastSeenCompileUnitID = U.getUniqueID(); - LastSeenDIE = Die; - return true; -} - -PointerIntPair DeclContextTree::getChildDeclContext( - DeclContext &Context, const DWARFDie &DIE, CompileUnit &U, - UniquingStringPool &StringPool, bool InClangModule) { - 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 PointerIntPair(nullptr); - case dwarf::DW_TAG_module: - break; - case dwarf::DW_TAG_compile_unit: - return PointerIntPair(&Context); - case dwarf::DW_TAG_subprogram: - // Do not unique anything inside CU local functions. - if ((Context.getTag() == dwarf::DW_TAG_namespace || - Context.getTag() == dwarf::DW_TAG_compile_unit) && - !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0)) - return PointerIntPair(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 PointerIntPair(nullptr); - break; - } - - const char *Name = DIE.getName(DINameKind::LinkageName); - const char *ShortName = DIE.getName(DINameKind::ShortName); - StringRef NameRef; - StringRef ShortNameRef; - StringRef FileRef; - - if (Name) - NameRef = StringPool.internString(Name); - else if (Tag == dwarf::DW_TAG_namespace) - // FIXME: For dsymutil-classic compatibility. I think uniquing within - // anonymous namespaces is wrong. There is no ODR guarantee there. - NameRef = StringPool.internString("(anonymous namespace)"); - - if (ShortName && ShortName != Name) - ShortNameRef = StringPool.internString(ShortName); - else - ShortNameRef = NameRef; - - 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 PointerIntPair(nullptr); - - unsigned Line = 0; - unsigned ByteSize = std::numeric_limits::max(); - - if (!InClangModule) { - // 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 || !Name) { - if (unsigned FileNum = - dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) { - if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( - &U.getOrigUnit())) { - // 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 (!Name && Tag == dwarf::DW_TAG_namespace) - FileNum = 1; - - if (LT->hasFileAtIndex(FileNum)) { - Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0); - // Cache the resolved paths based on the index in the line table, - // because calling realpath is expansive. - StringRef ResolvedPath = U.getResolvedPath(FileNum); - if (!ResolvedPath.empty()) { - FileRef = ResolvedPath; - } else { - std::string File; - bool FoundFileName = LT->getFileNameByIndex( - FileNum, U.getOrigUnit().getCompilationDir(), - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, - File); - (void)FoundFileName; - assert(FoundFileName && "Must get file name from line table"); - // Second level of caching, this time based on the file's parent - // path. - FileRef = PathResolver.resolve(File, StringPool); - U.setResolvedPath(FileNum, FileRef); - } - } - } - } - } - } - - if (!Line && NameRef.empty()) - return PointerIntPair(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(Context.getQualifiedNameHash(), Tag, NameRef); - - // FIXME: dsymutil-classic compatibility: when we don't have a name, - // use the filename. - if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)") - Hash = hash_combine(Hash, FileRef); - - // Now look if this context already exists. - DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); - auto ContextIter = Contexts.find(&Key); - - if (ContextIter == Contexts.end()) { - // The context wasn't found. - bool Inserted; - DeclContext *NewContext = - new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, - Context, DIE, U.getUniqueID()); - std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); - assert(Inserted && "Failed to insert DeclContext"); - (void)Inserted; - } else if (Tag != dwarf::DW_TAG_namespace && - !(*ContextIter)->setLastSeenDIE(U, DIE)) { - // The context was found, but it is ambiguous with another context - // in the same file. Mark it invalid. - return PointerIntPair(*ContextIter, /* Invalid= */ 1); - } - - assert(ContextIter != Contexts.end()); - // FIXME: dsymutil-classic compatibility. Union types aren't - // uniques, but their children might be. - if ((Tag == dwarf::DW_TAG_subprogram && - Context.getTag() != dwarf::DW_TAG_structure_type && - Context.getTag() != dwarf::DW_TAG_class_type) || - (Tag == dwarf::DW_TAG_union_type)) - return PointerIntPair(*ContextIter, /* Invalid= */ 1); - - return PointerIntPair(*ContextIter); -} - bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die, AttributesInfo &Info, OffsetsStringPool &StringPool, Index: llvm/tools/dsymutil/DwarfStreamer.h =================================================================== --- /dev/null +++ llvm/tools/dsymutil/DwarfStreamer.h @@ -0,0 +1,171 @@ +//===- tools/dsymutil/DwarfStreamer.h - Dwarf Streamer ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompileUnit.h" +#include "DebugMap.h" +#include "NonRelocatableStringpool.h" +#include "llvm/CodeGen/AccelTable.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +#ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H +#define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H + +namespace llvm { +namespace dsymutil { + +struct LinkOptions; + +/// 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: + DwarfStreamer(raw_fd_ostream &OutFile) : OutFile(OutFile) {} + bool init(Triple TheTriple); + + /// Dump the file to the disk. + bool finish(const DebugMap &); + + 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); + + /// Recursively emit the DIE tree rooted at \p Die. + void emitDIE(DIE &Die); + + /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. + void emitAbbrevs(const std::vector> &Abbrevs, + unsigned DwarfVersion); + + /// Emit the string table described by \p Pool. + void emitStrings(const NonRelocatableStringpool &Pool); + + /// 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); + + /// 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); + + uint32_t getRangesSectionSize() const { 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(const CompileUnit &Unit, DWARFContext &Dwarf); + + /// 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); + + /// Copy over the debug sections that are not modified when updating. + void copyInvariantDebugSection(const object::ObjectFile &Obj, LinkOptions &); + + uint32_t getLineSectionSize() const { return LineSectionSize; } + + /// Emit the .debug_pubnames contribution for \p Unit. + void emitPubNamesForUnit(const CompileUnit &Unit); + + /// Emit the .debug_pubtypes contribution for \p Unit. + void emitPubTypesForUnit(const CompileUnit &Unit); + + /// Emit a CIE. + void emitCIE(StringRef CIEBytes); + + /// Emit an FDE with data \p Bytes. + void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes); + + /// Emit Apple namespaces accelerator table. + void emitAppleNamespaces(AccelTable &Table); + + /// Emit Apple names accelerator table. + void emitAppleNames(AccelTable &Table); + + /// Emit Apple Objective-C accelerator table. + void emitAppleObjc(AccelTable &Table); + + /// Emit Apple type accelerator table. + void emitAppleTypes(AccelTable &Table); + + uint32_t getFrameSectionSize() const { return FrameSectionSize; } + +private: + /// \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; + MCCodeEmitter *MCE; // Owned by MCStreamer + MCStreamer *MS; // Owned by AsmPrinter + std::unique_ptr TM; + std::unique_ptr Asm; + /// @} + + /// The file we stream the linked Dwarf to. + raw_fd_ostream &OutFile; + + uint32_t RangesSectionSize; + uint32_t LocSectionSize; + uint32_t LineSectionSize; + uint32_t FrameSectionSize; + + /// 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); +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H Index: llvm/tools/dsymutil/DwarfStreamer.cpp =================================================================== --- /dev/null +++ llvm/tools/dsymutil/DwarfStreamer.cpp @@ -0,0 +1,638 @@ +//===- tools/dsymutil/DwarfStreamer.cpp - Dwarf Streamer ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DwarfStreamer.h" +#include "LinkUtils.h" +#include "MachOUtils.h" +#include "llvm/ADT/Triple.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.inc" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +namespace llvm { +namespace dsymutil { + +/// Retrieve the section named \a SecName in \a Obj. +/// +/// To accommodate for platform discrepancies, the name passed should be +/// (for example) 'debug_info' to match either '__debug_info' or '.debug_info'. +/// This function will strip the initial platform-specific characters. +static Optional +getSectionByName(const object::ObjectFile &Obj, StringRef SecName) { + for (const object::SectionRef &Section : Obj.sections()) { + StringRef SectionName; + Section.getName(SectionName); + SectionName = SectionName.substr(SectionName.find_first_not_of("._")); + if (SectionName != SecName) + continue; + return Section; + } + return None; +} + +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); + 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); + + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!MAI) + return error("no asm info for target " + TripleName, Context); + + 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); + + MCTargetOptions Options; + MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options); + if (!MAB) + return error("no asm backend for target " + TripleName, Context); + + MII.reset(TheTarget->createMCInstrInfo()); + if (!MII) + return error("no instr info info for target " + TripleName, Context); + + MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); + if (!MCE) + return error("no code emitter for target " + TripleName, Context); + + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MS = TheTarget->createMCObjectStreamer( + TheTriple, *MC, std::unique_ptr(MAB), + MAB->createObjectWriter(OutFile), std::unique_ptr(MCE), + *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false); + if (!MS) + return error("no object streamer for target " + TripleName, Context); + + // 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); + + Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr(MS))); + if (!Asm) + return error("no asm printer for target " + TripleName, Context); + + RangesSectionSize = 0; + LocSectionSize = 0; + LineSectionSize = 0; + FrameSectionSize = 0; + + return true; +} + +bool DwarfStreamer::finish(const DebugMap &DM) { + bool Result = true; + if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty()) + Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile); + else + MS->Finish(); + return Result; +} + +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 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. +void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { + unsigned Version = Unit.getOrigUnit().getVersion(); + switchToDebugInfoSection(Version); + + // 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(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4); + Asm->emitInt16(Version); + // 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()); +} + +/// 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); +} + +/// Emit the debug_str section stored in \p Pool. +void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection()); + std::vector Entries = Pool.getEntries(); + for (auto Entry : Entries) { + if (Entry.getIndex() == -1U) + break; + // Emit the string itself. + Asm->OutStreamer->EmitBytes(Entry.getString()); + // Emit a null terminator. + Asm->emitInt8(0); + } +} + +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(1 << 5); + 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.begin(), Ranges.end()); + + 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, TupleSize); + + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Arange length + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->emitInt16(dwarf::DW_ARANGES_VERSION); // Version number + Asm->emitInt32(Unit.getStartOffset()); // 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(const CompileUnit &Unit, + DWARFContext &Dwarf) { + const auto &Attributes = Unit.getLocationAttributes(); + + if (Attributes.empty()) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + 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(); + + for (const auto &Attr : Attributes) { + uint32_t Offset = Attr.first.get(); + Attr.first.set(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.second + UnitPcOffset; + while (Data.isValidOffset(Offset)) { + uint64_t Low = Data.getUnsigned(&Offset, AddressSize); + uint64_t High = Data.getUnsigned(&Offset, AddressSize); + LocSectionSize += 2 * AddressSize; + if (Low == 0 && High == 0) { + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitIntValue(0, AddressSize); + break; + } + Asm->OutStreamer->EmitIntValue(Low + LocPcOffset, AddressSize); + Asm->OutStreamer->EmitIntValue(High + LocPcOffset, AddressSize); + uint64_t Length = Data.getU16(&Offset); + Asm->OutStreamer->EmitIntValue(Length, 2); + // Just copy the bytes over. + Asm->OutStreamer->EmitBytes( + StringRef(InputSec.Data.substr(Offset, 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, PointerSize); + LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); + AddressDelta = 0; + } else { + AddressDelta = (Row.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; + 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); +} + +static void emitSectionContents(const object::ObjectFile &Obj, + StringRef SecName, MCStreamer *MS) { + StringRef Contents; + if (auto Sec = getSectionByName(Obj, SecName)) + if (!Sec->getContents(Contents)) + MS->EmitBytes(Contents); +} + +void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj, + LinkOptions &Options) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + emitSectionContents(Obj, "debug_line", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + emitSectionContents(Obj, "debug_loc", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + emitSectionContents(Obj, "debug_ranges", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + emitSectionContents(Obj, "debug_frame", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); + emitSectionContents(Obj, "debug_aranges", MS); +} + +/// Emit the pubnames or pubtypes section contribution for \p +/// Unit into \p Sec. The data is provided in \p Names. +void DwarfStreamer::emitPubSectionForUnit( + 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()); // Unit offset + Asm->emitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset()); // Size + HeaderEmitted = true; + } + Asm->emitInt32(Name.Die->getOffset()); + + // 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; +} + +} // namespace dsymutil +} // namespace llvm Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- /dev/null +++ llvm/tools/dsymutil/LinkUtils.h @@ -0,0 +1,64 @@ +//===- tools/dsymutil/LinkUtils.h - Dwarf linker utilities ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H +#define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/WithColor.h" +#include + +namespace llvm { +namespace dsymutil { + +struct LinkOptions { + /// Verbosity + bool Verbose = false; + + /// Skip emitting output + bool NoOutput = false; + + /// Do not unique types according to ODR + bool NoODR = false; + + /// Update + bool Update = false; + + /// Minimize + bool Minimize = false; + + /// Do not check swiftmodule timestamp + bool NoTimestamp = false; + + /// Number of threads. + unsigned Threads = 1; + + /// -oso-prepend-path + std::string PrependPath; + + LinkOptions() = default; +}; + +inline void warn(Twine Warning, Twine Context = {}) { + WithColor::warning() << Warning + "\n"; + if (!Context.isTriviallyEmpty()) + WithColor::note() << Twine("while processing ") + Context + "\n"; +} + +inline bool error(Twine Error, Twine Context = {}) { + WithColor::error() << Error + "\n"; + if (!Context.isTriviallyEmpty()) + WithColor::note() << Twine("while processing ") + Context + "\n"; + return false; +} + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H Index: llvm/tools/dsymutil/MachODebugMapParser.cpp =================================================================== --- llvm/tools/dsymutil/MachODebugMapParser.cpp +++ llvm/tools/dsymutil/MachODebugMapParser.cpp @@ -1,6 +1,6 @@ //===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. Index: llvm/tools/dsymutil/MachOUtils.cpp =================================================================== --- llvm/tools/dsymutil/MachOUtils.cpp +++ llvm/tools/dsymutil/MachOUtils.cpp @@ -1,4 +1,4 @@ -//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +//===-- MachOUtils.cpp - Mach-o specific helpers for dsymutil ------------===// // // The LLVM Compiler Infrastructure // @@ -10,8 +10,8 @@ #include "MachOUtils.h" #include "BinaryHolder.h" #include "DebugMap.h" +#include "LinkUtils.h" #include "NonRelocatableStringpool.h" -#include "dsymutil.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCMachObjectWriter.h" #include "llvm/MC/MCObjectStreamer.h" Index: llvm/tools/dsymutil/NonRelocatableStringpool.h =================================================================== --- llvm/tools/dsymutil/NonRelocatableStringpool.h +++ llvm/tools/dsymutil/NonRelocatableStringpool.h @@ -62,6 +62,21 @@ DwarfStringPoolEntryRef EmptyString; }; +/// Helper for making strong types. +template class StrongType : public T { +public: + template + explicit StrongType(Args... A) : T(std::forward(A)...) {} +}; + +/// It's very easy to introduce bugs by passing the wrong string pool in the +/// dwarf linker. By using strong types the interface enforces that the right +/// kind of pool is used. +struct UniqueTag {}; +struct OffsetsTag {}; +using UniquingStringPool = StrongType; +using OffsetsStringPool = StrongType; + } // end namespace dsymutil } // end namespace llvm Index: llvm/tools/dsymutil/dsymutil.h =================================================================== --- llvm/tools/dsymutil/dsymutil.h +++ llvm/tools/dsymutil/dsymutil.h @@ -1,6 +1,6 @@ //===- tools/dsymutil/dsymutil.h - dsymutil high-level functionality ------===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -18,6 +18,7 @@ #define LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H #include "DebugMap.h" +#include "LinkUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" @@ -29,34 +30,6 @@ namespace llvm { namespace dsymutil { -struct LinkOptions { - /// Verbosity - bool Verbose = false; - - /// Skip emitting output - bool NoOutput = false; - - /// Do not unique types according to ODR - bool NoODR = false; - - /// Update - bool Update = false; - - /// Minimize - bool Minimize = false; - - /// Do not check swiftmodule timestamp - bool NoTimestamp = false; - - /// Number of threads. - unsigned Threads = 1; - - /// -oso-prepend-path - std::string PrependPath; - - LinkOptions() = default; -}; - /// Extract the DebugMaps from the given file. /// The file has to be a MachO object file. Multiple debug maps can be /// returned when the file is universal (aka fat) binary. @@ -74,9 +47,6 @@ bool linkDwarf(raw_fd_ostream &OutFile, const DebugMap &DM, const LinkOptions &Options); -void warn(Twine Warning, Twine Context = {}); -bool error(Twine Error, Twine Context = {}); - } // end namespace dsymutil } // end namespace llvm Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ llvm/tools/dsymutil/dsymutil.cpp @@ -15,6 +15,7 @@ #include "dsymutil.h" #include "CFBundle.h" #include "DebugMap.h" +#include "LinkUtils.h" #include "MachOUtils.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h"