Index: llvm/include/llvm/CodeGen/DIE.h =================================================================== --- llvm/include/llvm/CodeGen/DIE.h +++ llvm/include/llvm/CodeGen/DIE.h @@ -910,6 +910,9 @@ /// unsigned ComputeSize(const AsmPrinter *AP) const; + // TODO: move setSize() and Size to DIEValueList + void setSize(unsigned size) { Size = size; } + /// BestForm - Choose the best form for data. /// dwarf::Form BestForm(unsigned DwarfVersion) const { @@ -944,6 +947,9 @@ /// unsigned ComputeSize(const AsmPrinter *AP) const; + // TODO: move setSize() and Size to DIEValueList + void setSize(unsigned size) { Size = size; } + /// BestForm - Choose the best form for data. /// dwarf::Form BestForm() const { Index: llvm/include/llvm/CodeGen/NonRelocatableStringpool.h =================================================================== --- llvm/include/llvm/CodeGen/NonRelocatableStringpool.h +++ llvm/include/llvm/CodeGen/NonRelocatableStringpool.h @@ -1,4 +1,4 @@ -//===- NonRelocatableStringpool.h - A simple stringpool --------*- C++ -*-===// +//===- llvm/CodeGen/NonRelocatableStringpool.h - A simple stringpool -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,20 +6,15 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H -#define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H +#ifndef LLVM_CODEGEN_NONRELOCATABLESTRINGPOOL_H +#define LLVM_CODEGEN_NONRELOCATABLESTRINGPOOL_H -#include "SymbolMap.h" - -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/DwarfStringPoolEntry.h" #include "llvm/Support/Allocator.h" #include #include namespace llvm { -namespace dsymutil { /// A string table that doesn't need relocations. /// @@ -34,7 +29,7 @@ using MapTy = StringMap; NonRelocatableStringpool( - SymbolMapTranslator Translator = SymbolMapTranslator()) + std::function Translator = nullptr) : Translator(Translator) { // Legacy dsymutil puts an empty string at the start of the line table. EmptyString = getEntry(""); @@ -65,7 +60,7 @@ uint32_t CurrentEndOffset = 0; unsigned NumEntries = 0; DwarfStringPoolEntryRef EmptyString; - SymbolMapTranslator Translator; + std::function Translator; }; /// Helper for making strong types. @@ -75,15 +70,14 @@ 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 +/// 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; -} // end namespace dsymutil } // end namespace llvm -#endif // LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H +#endif // LLVM_CODEGEN_NONRELOCATABLESTRINGPOOL_H Index: llvm/include/llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h @@ -1,4 +1,4 @@ -//===- tools/dsymutil/CompileUnit.h - Dwarf debug info linker ---*- C++ -*-===// +//===- DWARFOptCompileUnit.h ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,15 +6,15 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H -#define LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H +#ifndef LLVM_DEBUGINFO_DWARF_OPT_COMPILEUNIT_H +#define LLVM_DEBUGINFO_DWARF_OPT_COMPILEUNIT_H #include "llvm/ADT/IntervalMap.h" #include "llvm/CodeGen/DIE.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" namespace llvm { -namespace dsymutil { class DeclContext; @@ -46,7 +46,7 @@ }; /// 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. +/// instance in the object file to its brand new cloned and generated DIE tree. class CompileUnit { public: /// Information gathered about a DIE in the object file. @@ -325,7 +325,6 @@ std::string ClangModuleName; }; -} // end namespace dsymutil } // end namespace llvm -#endif // LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H +#endif // LLVM_DEBUGINFO_DWARF_OPT_COMPILEUNIT_H Index: llvm/include/llvm/DebugInfo/DWARF/DWARFOptDeclContext.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFOptDeclContext.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFOptDeclContext.h @@ -1,4 +1,4 @@ -//===- tools/dsymutil/DeclContext.h - Dwarf debug info linker ---*- C++ -*-===// +//==- llvm/DebugInfo/DWARF/DWARFOptDeclContext.h - DI optimizer`s context -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,20 +6,19 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H -#define LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H +#ifndef LLVM_DEBUGINFO_DWARF_OPT_DECLCONTEXT_H +#define LLVM_DEBUGINFO_DWARF_OPT_DECLCONTEXT_H -#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/CodeGen/NonRelocatableStringpool.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h" #include "llvm/Support/Path.h" namespace llvm { -namespace dsymutil { struct DeclMapInfo; @@ -165,7 +164,6 @@ } }; -} // end namespace dsymutil } // end namespace llvm -#endif // LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H +#endif // LLVM_DEBUGINFO_DWARF_OPT_DECLCONTEXT_H Index: llvm/include/llvm/DebugInfo/DWARF/DWARFOptimizer.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/DWARF/DWARFOptimizer.h @@ -0,0 +1,730 @@ +//===- llvm/DebugInfo/DWARF/DWARFOptimizer.h - Dwarf debug info optimizer -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_DWARFOPTIMIZER_H +#define LLVM_DEBUGINFO_DWARFOPTIMIZER_H + +#include "llvm/CodeGen/AccelTable.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFOptDeclContext.h" +#include "llvm/MC/MCDwarf.h" + +namespace llvm { + +enum class DwarfOptimizerClient { Dsymutil, LLD, General }; + +/// The kind of accelerator tables we should emit. +enum class AccelTableKind { + Apple, ///< .apple_names, .apple_namespaces, .apple_types, .apple_objc. + Dwarf, ///< DWARF v5 .debug_names. + Default, ///< Dwarf for DWARF5 or later, Apple otherwise. +}; + +/// Partial address range. Besides an offset, only the +/// HighPC is stored. The structure is stored in a map where the LowPC is the +/// key. +struct ObjFileAddressRange { + /// Function HighPC. + uint64_t HighPC; + /// Offset to apply to the linked address. + /// should be 0 for not-linked object file. + int64_t Offset; + + ObjFileAddressRange(uint64_t EndPC, int64_t Offset) + : HighPC(EndPC), Offset(Offset) {} + + ObjFileAddressRange() : HighPC(0), Offset(0) {} +}; + +/// Map LowPC to ObjFileAddressRange. +using RangesTy = std::map; + +/// AddressesMap represents information about valid addresses used +/// by debug information. Valid addresses are those which points to +/// live code sections. i.e. relocations for these addresses point +/// into sections which would be/are placed into resulting binary. +class AddressesMap { +public: + virtual ~AddressesMap(); + + /// returns true if represented addresses are from linked file. + /// returns false if represented addresses are from not-linked + /// object file. + virtual bool AreRelocationsResolved() const = 0; + + /// Checks that there are valid relocations against a .debug_info + /// section. Reset current relocation pointer if neccessary. + virtual bool hasValidRelocs(bool resetRelocsPtr = true) = 0; + + /// Checks that there is a relocation against .debug_info + /// table between \p StartOffset and \p NextOffset. + /// + /// This function must be called with offsets in strictly ascending + /// order because it never looks back at relocations it already 'went past'. + /// \returns true and sets Info.InDebugMap if it is the case. + virtual bool hasValidRelocationAt(uint64_t StartOffset, uint64_t EndOffset, + CompileUnit::DIEInfo &Info) = 0; + + /// Apply the valid relocations to the buffer \p Data, taking into + /// account that Data is at \p BaseOffset in the debug_info section. + /// + /// This function must be called with monotonic \p BaseOffset values. + /// + /// \returns true whether any reloc has been applied. + virtual bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, + bool IsLittleEndian) = 0; + + /// returns all valid address ranges(i.e. those addresses which points + /// to sections with code) + virtual RangesTy &getValidAddressRanges() = 0; + + /// erase all data + virtual void clear() = 0; +}; + +/// DwarfEmitter presents interface to generate all debug info tables. +class DwarfEmitter { +public: + virtual ~DwarfEmitter(); + + /// Emit DIE containing warnings + virtual void emitPaperTrailWarnings(const Triple &triple, DIE &die) = 0; + + /// Emit section named SecName with content equals to + /// corresponding section in Obj. + virtual void emitSectionContents(const object::ObjectFile &Obj, + StringRef SecName) = 0; + + /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. + virtual void + emitAbbrevs(const std::vector> &Abbrevs, + unsigned DwarfVersion) = 0; + + /// Emit the string table described by \p Pool. + virtual void emitStrings(const NonRelocatableStringpool &Pool) = 0; + + /// Emit DWARF debug names. + virtual void + emitDebugNames(AccelTable &Table) = 0; + + /// Emit Apple namespaces accelerator table. + virtual void + emitAppleNamespaces(AccelTable &Table) = 0; + + /// Emit Apple names accelerator table. + virtual void + emitAppleNames(AccelTable &Table) = 0; + + /// Emit Apple Objective-C accelerator table. + virtual void + emitAppleObjc(AccelTable &Table) = 0; + + /// Emit Apple type accelerator table. + virtual void + emitAppleTypes(AccelTable &Table) = 0; + + /// Emit debug_ranges for \p FuncRange by translating the + /// original \p Entries. + virtual void emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector &Entries, + unsigned AddressSize) = 0; + + /// 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. + virtual void emitUnitRangesEntries(CompileUnit &Unit, + bool DoRangesSection) = 0; + + /// Copy the debug_line over to the updated binary while unobfuscating the + /// file names and directories. + virtual void translateLineTable(DataExtractor LineData, uint64_t Offset) = 0; + + /// Emit the line table described in \p Rows into the debug_line section. + virtual void emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, + unsigned MinInstLength, + std::vector &Rows, + unsigned AdddressSize) = 0; + + /// Emit the .debug_pubnames contribution for \p Unit. + virtual void emitPubNamesForUnit(const CompileUnit &Unit) = 0; + + /// Emit the .debug_pubtypes contribution for \p Unit. + virtual void emitPubTypesForUnit(const CompileUnit &Unit) = 0; + + /// Emit a CIE. + virtual void emitCIE(StringRef CIEBytes) = 0; + + /// Emit an FDE with data \p Bytes. + virtual void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes) = 0; + + /// 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. + virtual void emitLocationsForUnit( + const CompileUnit &Unit, DWARFContext &Dwarf, + std::function &)> + ProcessExpr) = 0; + + /// 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(). + virtual void emitCompileUnitHeader(CompileUnit &Unit) = 0; + + /// Recursively emit the DIE tree rooted at \p Die. + virtual void emitDIE(DIE &Die) = 0; + + virtual uint64_t getLineSectionSize() const = 0; + + virtual uint64_t getFrameSectionSize() const = 0; + + virtual uint64_t getRangesSectionSize() const = 0; + + virtual uint64_t getDebugInfoSectionSize() const = 0; +}; + +using UnitListTy = std::vector>; + +/// this class represents Object File and it`s additional info. +class DwarfOptObjFile { +public: + DwarfOptObjFile(StringRef Name, const object::ObjectFile *File, + const std::vector &Warnings) + : FileName(Name), ObjFile(File), Warnings(Warnings) {} + + /// object file name + StringRef FileName; + /// object file + const object::ObjectFile *ObjFile; + /// helpful address information(list of valid address ranges, relocations) + std::unique_ptr Addresses; + /// warnings for object file + const std::vector &Warnings; +}; + +typedef std::function + messageHandler; +typedef std::function(StringRef ContainerName, + StringRef Path)> + objFileLoader; +typedef std::map swiftInterfacesMap; + +/// The core of the Dwarf optimizing logic. +/// +/// The generation of the dwarf information from the object files will be +/// driven by the selection of 'root DIEs', which are DIEs that +/// describe variables or functions that resolves to the corresponding +/// code section(and thus have entries in the Addresses map). All the debug +/// information that will be generated(the DIEs, but also the line +/// tables, ranges, ...) is derived from that set of root DIEs. +/// +/// The root DIEs are identified because they contain relocations that +/// points to code section(the low_pc for a function, the location for +/// a variable). These relocations are called ValidRelocs in the +/// AddressesInfo and are gathered as a very first step when we start +/// processing a object file. +class DwarfOptimizer { +public: + DwarfOptimizer(const Triple &triple, DwarfEmitter &emitter, + DwarfOptimizerClient ClientID = DwarfOptimizerClient::General) + : triple(triple), dwarfEmitter(emitter), OptimizerClientID(ClientID) {} + + /// Add object file to be optimized. + void addObjectFile(DwarfOptObjFile &objFile); + + /// Optimize debug info for added objFiles. Object + /// files are optimized whole together. + bool optimize(); + + /// A number of methods setting various optimization options: + + /// Allows to generate log of optimizing process to the standard output. + void setVerbosity(bool verbose) { Options.Verbose = verbose; } + + /// Do not emit optimized dwarf info. + void setNoOutput(bool noOut) { Options.NoOutput = noOut; } + + /// Do not unique types according to ODR. + void setNoODR(bool noODR) { Options.NoODR = noODR; } + + /// update existing DWARF info(for the linked binary) + void setUpdate(bool update) { Options.Update = update; } + + /// Use specified number of threads for parallel files optimization. + void setNumThreads(unsigned numThreads) { Options.Threads = numThreads; } + + /// Set kind of accelerator tables to be generated. + void setAccelTableKind(AccelTableKind kind) { + Options.TheAccelTableKind = kind; + } + + /// Set prepend path for clang modules. + void setPrependPath(const std::string &ppath) { Options.PrependPath = ppath; } + + /// Set translator which would be used for strings. + void + setStringsTranslator(std::function StringsTranslator) { + this->StringsTranslator = StringsTranslator; + } + + /// Set estimated objects files amount, for preliminary data allocation. + void setEstimatedObjfilesAmount(unsigned objFilesNum) { + ObjectContexts.reserve(objFilesNum); + } + + /// Set warning handler which would be used to report warnings. + void setWarningHandler(messageHandler handler) { + Options.WarningHandler = handler; + } + + /// Set error handler which would be used to report errors. + void setErrorHandler(messageHandler handler) { + Options.ErrorHandler = handler; + } + + /// Set object files loader which would be used to load + /// additional objects for splitted dwarf. + void setObjFileLoader(objFileLoader loader) { + Options.ObjFileLoader = loader; + } + + /// Set map for Swift interfaces. + void setSwiftInterfacesMap(swiftInterfacesMap *map) { + Options.ParseableSwiftInterfaces = map; + } + + /// returns true if we need to translate strings. + bool NeedToTranslateStrings() { return StringsTranslator != nullptr; } + +private: + void reportWarning(const Twine &Warning, const DwarfOptObjFile &OF, + const DWARFDie *DIE = nullptr) const { + if (Options.WarningHandler != nullptr) + Options.WarningHandler(Warning, OF.FileName, DIE); + } + + void reportError(const Twine &Warning, const DwarfOptObjFile &OF, + const DWARFDie *DIE = nullptr) const { + if (Options.ErrorHandler != nullptr) + Options.ErrorHandler(Warning, OF.FileName, DIE); + } + + /// Remembers the oldest and newest DWARF version we've seen in a unit. + void updateDwarfVersion(unsigned Version) { + MaxDwarfVersion = std::max(MaxDwarfVersion, Version); + MinDwarfVersion = std::min(MinDwarfVersion, Version); + } + + /// Remembers the kinds of accelerator tables we've seen in a unit. + void updateAccelKind(DWARFContext &Dwarf); + + /// Emit warnings as Dwarf compile units to leave a trail after optimization. + bool emitPaperTrailWarnings(const DwarfOptObjFile &OF, + OffsetsStringPool &StringPool); + + void copyInvariantDebugSection(const object::ObjectFile &Obj); + + /// Keeps track of data associated with one object during optimizing. + struct OptContext { + DwarfOptObjFile &objectFile; + std::unique_ptr dwarfContext; + UnitListTy compileUnits; + bool skip = false; + + OptContext(DwarfOptObjFile &objFile) : objectFile(objFile) { + + if (objectFile.ObjFile) + dwarfContext = DWARFContext::create(*objectFile.ObjFile); + } + + /// Clear part of the context that's no longer needed when we're done with + /// the debug object. + void Clear() { + dwarfContext.reset(nullptr); + compileUnits.clear(); + objectFile.Addresses->clear(); + } + }; + + /// Called before emitting object data + void CleanupAuxiliarryData(OptContext &Context); + + /// \defgroup FindRootDIEs Find DIEs corresponding to Address map entries. + /// + /// @{ + /// Recursively walk the \p DIE tree and look for DIEs to + /// keep. Store that information in \p CU's DIEInfo. + /// + /// The return value indicates whether the DIE is incomplete. + void lookForDIEsToKeep(AddressesMap &RelocMgr, RangesTy &Ranges, + const UnitListTy &Units, const DWARFDie &DIE, + const DwarfOptObjFile &OF, CompileUnit &CU, + unsigned Flags); + + /// If this compile unit is really a skeleton CU that points to a + /// clang module, register it in ClangModules and return true. + /// + /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name + /// pointing to the module, and a DW_AT_gnu_dwo_id with the module + /// hash. + bool registerModuleReference(DWARFDie CUDie, const DWARFUnit &Unit, + const DwarfOptObjFile &OF, + OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPoolStringPool, + DeclContextTree &ODRContexts, + uint64_t ModulesEndOffset, unsigned &UnitID, + bool IsLittleEndian, unsigned Indent = 0, + bool Quiet = false); + + /// Recursively add the debug info in this clang module .pcm + /// file (and all the modules imported by it in a bottom-up fashion) + /// to Units. + Error loadClangModule(DWARFDie CUDie, StringRef FilePath, + StringRef ModuleName, uint64_t DwoId, + const DwarfOptObjFile &OF, + OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, + unsigned &UnitID, bool IsLittleEndian, + unsigned Indent = 0, bool Quiet = false); + + /// Flags passed to DwarfOptimizer::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(AddressesMap &RelocMgr, RangesTy &Ranges, + const UnitListTy &Units, const DWARFDie &DIE, + CompileUnit::DIEInfo &MyInfo, + const DwarfOptObjFile &OF, CompileUnit &CU, + bool UseODR); + + unsigned shouldKeepDIE(AddressesMap &RelocMgr, RangesTy &Ranges, + const DWARFDie &DIE, const DwarfOptObjFile &OF, + CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + /// Check if a variable describing DIE should be kept. + /// \returns updated TraversalFlags. + unsigned shouldKeepVariableDIE(AddressesMap &RelocMgr, const DWARFDie &DIE, + CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, unsigned Flags); + + unsigned shouldKeepSubprogramDIE(AddressesMap &RelocMgr, RangesTy &Ranges, + const DWARFDie &DIE, + const DwarfOptObjFile &OF, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + /// Resolve the DIE attribute reference that has been extracted in \p + /// RefValue. The resulting DIE might be in another CompileUnit which is + /// stored into \p ReferencedCU. \returns null if resolving fails for any + /// reason. + DWARFDie resolveDIEReference(const DwarfOptObjFile &OF, + const UnitListTy &Units, + const DWARFFormValue &RefValue, + const DWARFDie &DIE, CompileUnit *&RefCU); + + /// @} + + /// \defgroup Methods used to optimize the debug information + /// + /// @{ + + struct OptimizerOptions; + + class DIECloner { + DwarfOptimizer &Optimizer; + DwarfEmitter &Emitter; + DwarfOptObjFile &ObjFile; + + /// Allocator used for all the DIEValue objects. + BumpPtrAllocator &DIEAlloc; + + std::vector> &CompileUnits; + + bool Update; + + public: + DIECloner(DwarfOptimizer &Optimizer, DwarfEmitter &Emitter, + DwarfOptObjFile &ObjFile, BumpPtrAllocator &DIEAlloc, + std::vector> &CompileUnits, + bool Update) + : Optimizer(Optimizer), Emitter(Emitter), ObjFile(ObjFile), + DIEAlloc(DIEAlloc), CompileUnits(CompileUnits), Update(Update) {} + + /// 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 + /// address 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 DwarfOptObjFile &OF, + CompileUnit &U, OffsetsStringPool &StringPool, + int64_t PCOffset, uint32_t OutOffset, unsigned Flags, + bool IsLittleEndian, DIE *Die = nullptr); + + /// Construct the output DIE tree by cloning the DIEs we + /// chose to keep above. If there are no valid relocs, then there's + /// nothing to clone/emit. + void cloneAllCompileUnits(DWARFContext &DwarfContext, + const DwarfOptObjFile &OF, + OffsetsStringPool &StringPool, + bool IsLittleEndian); + + private: + using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; + + /// Information gathered and exchanged between the various + /// clone*Attributes helpers about the attributes of a particular DIE. + struct AttributesInfo { + /// Names. + DwarfStringPoolEntryRef Name, MangledName, NameWithoutTemplate; + + /// Offsets in the string pool. + uint32_t NameOffset = 0; + uint32_t MangledNameOffset = 0; + + /// Value of AT_low_pc in the input DIE + uint64_t OrigLowPc = std::numeric_limits::max(); + + /// Value of AT_high_pc in the input DIE + uint64_t OrigHighPc = 0; + + /// 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; + }; + + friend DIECloner; + + /// Helper for cloneDIE. + unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, + const DwarfOptObjFile &OF, CompileUnit &U, + OffsetsStringPool &StringPool, + const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, + AttributesInfo &AttrInfo, bool IsLittleEndian); + + /// 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 DwarfOptObjFile &OF, + CompileUnit &Unit); + + /// Clone a DWARF expression that may be referencing another DIE. + void cloneExpression(DataExtractor &Data, DWARFExpression Expression, + const DwarfOptObjFile &OF, CompileUnit &Unit, + SmallVectorImpl &OutputBuffer); + + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneBlockAttribute(DIE &Die, const DwarfOptObjFile &OF, + CompileUnit &Unit, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize, + bool IsLittleEndian); + + /// 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 DwarfOptObjFile &OF, 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 DwarfOptObjFile &OF, + 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 DwarfOptObjFile &OF) 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, + const DwarfOptObjFile &OF); + + /// Emit the accelerator entries for \p Unit. + void emitAcceleratorEntriesForUnit(CompileUnit &Unit); + void emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit); + void emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit); + + /// Patch the frame info for an object file and emit it. + void patchFrameInfoForObject(const DwarfOptObjFile &, 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; + /// @} + + Triple triple; + + DwarfEmitter &dwarfEmitter; + std::vector ObjectContexts; + + unsigned MaxDwarfVersion = 0; + unsigned MinDwarfVersion = std::numeric_limits::max(); + + bool AtLeastOneAppleAccelTable = false; + bool AtLeastOneDwarfAccelTable = false; + + /// 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 DebugNames; + AccelTable AppleNames; + AccelTable AppleNamespaces; + AccelTable AppleObjc; + AccelTable AppleTypes; + + /// Mapping the PCM filename to the DwoId. + StringMap ClangModules; + + DwarfOptimizerClient OptimizerClientID; + + std::function StringsTranslator = nullptr; + + /// optimization options + struct OptimizerOptions { + /// Generate processing log to the standard output. + bool Verbose = false; + + /// Skip emitting output + bool NoOutput = false; + + /// Do not unique types according to ODR + bool NoODR = false; + + /// Update + bool Update = false; + + /// Number of threads. + unsigned Threads = 1; + + /// The accelerator table kind + AccelTableKind TheAccelTableKind = AccelTableKind::Default; + + /// Prepend path for the clang modules. + std::string PrependPath; + + // warning handler + messageHandler WarningHandler = nullptr; + + // error handler + messageHandler ErrorHandler = nullptr; + + objFileLoader ObjFileLoader = nullptr; + + /// A list of all .swiftinterface files referenced by the debug + /// info, mapping Module name to path on disk. The entries need to + /// be uniqued and sorted and there are only few entries expected + /// per compile unit, which is why this is a std::map. + /// this is dsymutil specific fag. + swiftInterfacesMap *ParseableSwiftInterfaces = nullptr; + } Options; +}; + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARFOPTIMIZER_H Index: llvm/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/CMakeLists.txt +++ llvm/lib/CodeGen/CMakeLists.txt @@ -102,6 +102,7 @@ MIRPrinter.cpp MIRPrintingPass.cpp MacroFusion.cpp + NonRelocatableStringpool.cpp OptimizePHIs.cpp ParallelCG.cpp PeepholeOptimizer.cpp Index: llvm/lib/CodeGen/NonRelocatableStringpool.cpp =================================================================== --- llvm/lib/CodeGen/NonRelocatableStringpool.cpp +++ llvm/lib/CodeGen/NonRelocatableStringpool.cpp @@ -1,4 +1,4 @@ -//===- NonRelocatableStringpool.cpp - A simple stringpool ----------------===// +//===-- llvm/CodeGen/NonRelocatableStringpool.cpp - A simple stringpool --===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,10 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "NonRelocatableStringpool.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" namespace llvm { -namespace dsymutil { DwarfStringPoolEntryRef NonRelocatableStringpool::getEntry(StringRef S) { if (S.empty() && !Strings.empty()) @@ -52,5 +51,4 @@ return Result; } -} // namespace dsymutil } // namespace llvm Index: llvm/lib/DebugInfo/DWARF/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -27,6 +27,9 @@ DWARFUnitIndex.cpp DWARFUnit.cpp DWARFVerifier.cpp + DWARFOptimizer.cpp + DWARFOptCompileUnit.cpp + DWARFOptDeclContext.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/DWARF Index: llvm/lib/DebugInfo/DWARF/DWARFOptCompileUnit.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFOptCompileUnit.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFOptCompileUnit.cpp @@ -1,4 +1,4 @@ -//===- tools/dsymutil/CompileUnit.h - Dwarf compile unit ------------------===// +//===- llvm/DebugInfo/DWARF/DWARFOptCompileUnit.cpp - Dwarf compile unit --===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,11 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "CompileUnit.h" -#include "DeclContext.h" +#include "llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFOptDeclContext.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) { @@ -142,5 +141,4 @@ Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation); } -} // namespace dsymutil } // namespace llvm Index: llvm/lib/DebugInfo/DWARF/DWARFOptDeclContext.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFOptDeclContext.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFOptDeclContext.cpp @@ -1,4 +1,4 @@ -//===- tools/dsymutil/DeclContext.cpp - Declaration context ---------------===// +//===- llvm/DebugInfo/DWARF/DWARFOptDeclContext.cpp - Declaration context -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,13 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "DeclContext.h" +#include "llvm/DebugInfo/DWARF/DWARFOptDeclContext.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. @@ -206,5 +205,5 @@ return PointerIntPair(*ContextIter); } -} // namespace dsymutil + } // namespace llvm Index: llvm/lib/DebugInfo/DWARF/DWARFOptimizer.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/DWARF/DWARFOptimizer.cpp @@ -0,0 +1,2393 @@ +//=== llvm/DebugInfo/DWARF/DWARFOptimizer.cpp - Dwarf debug info optimizer ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFOptimizer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFOptDeclContext.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ThreadPool.h" +#include + +namespace llvm { + +/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our +/// CompileUnit object instead. +static CompileUnit *getUnitForOffset(const UnitListTy &Units, uint64_t Offset) { + auto CU = std::upper_bound( + Units.begin(), Units.end(), Offset, + [](uint64_t LHS, const std::unique_ptr &RHS) { + return LHS < RHS->getOrigUnit().getNextUnitOffset(); + }); + return CU != Units.end() ? CU->get() : nullptr; +} + +/// Resolve the DIE attribute reference that has been extracted in \p RefValue. +/// The resulting DIE might be in another CompileUnit which is stored into \p +/// ReferencedCU. \returns null if resolving fails for any reason. +DWARFDie DwarfOptimizer::resolveDIEReference(const DwarfOptObjFile &OF, + const UnitListTy &Units, + const DWARFFormValue &RefValue, + const DWARFDie &DIE, + CompileUnit *&RefCU) { + assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); + uint64_t RefOffset = *RefValue.getAsReference(); + if ((RefCU = getUnitForOffset(Units, RefOffset))) + if (const auto RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) { + // In a file with broken references, an attribute might point to a NULL + // DIE. + if (!RefDie.isNULL()) + return RefDie; + } + + reportWarning("could not find referenced DIE", OF, &DIE); + return DWARFDie(); +} + +/// \returns whether the passed \a Attr type might contain a DIE reference +/// suitable for ODR uniquing. +static bool isODRAttribute(uint16_t Attr) { + switch (Attr) { + default: + return false; + case dwarf::DW_AT_type: + case dwarf::DW_AT_containing_type: + case dwarf::DW_AT_specification: + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_import: + return true; + } + llvm_unreachable("Improper attribute."); +} + +static bool isTypeTag(uint16_t Tag) { + switch (Tag) { + case dwarf::DW_TAG_array_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_pointer_type: + case dwarf::DW_TAG_reference_type: + case dwarf::DW_TAG_string_type: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_ptr_to_member_type: + case dwarf::DW_TAG_set_type: + case dwarf::DW_TAG_subrange_type: + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_file_type: + case dwarf::DW_TAG_namelist: + case dwarf::DW_TAG_packed_type: + case dwarf::DW_TAG_volatile_type: + case dwarf::DW_TAG_restrict_type: + case dwarf::DW_TAG_atomic_type: + case dwarf::DW_TAG_interface_type: + case dwarf::DW_TAG_unspecified_type: + case dwarf::DW_TAG_shared_type: + return true; + default: + break; + } + return false; +} + +AddressesMap::~AddressesMap() {} + +DwarfEmitter::~DwarfEmitter() {} + +bool DwarfOptimizer::DIECloner::getDIENames(const DWARFDie &Die, + AttributesInfo &Info, + OffsetsStringPool &StringPool, + bool StripTemplate) { + // This function will be called on DIEs having low_pcs and + // ranges. As getting the name might be more expansive, filter out + // blocks directly. + if (Die.getTag() == dwarf::DW_TAG_lexical_block) + return false; + + // FIXME: a bit wasteful as the first getName might return the + // short name. + if (!Info.MangledName) + if (const char *MangledName = Die.getName(DINameKind::LinkageName)) + Info.MangledName = StringPool.getEntry(MangledName); + + if (!Info.Name) + if (const char *Name = Die.getName(DINameKind::ShortName)) + Info.Name = StringPool.getEntry(Name); + + if (StripTemplate && Info.Name && Info.MangledName != Info.Name) { + // FIXME: dsymutil compatibility. This is wrong for operator< + auto Split = Info.Name.getString().split('<'); + if (!Split.second.empty()) + Info.NameWithoutTemplate = StringPool.getEntry(Split.first); + } + + return Info.Name || Info.MangledName; +} + +/// Resolve the relative path to a build artifact referenced by DWARF by +/// applying DW_AT_comp_dir. +static void resolveRelativeObjectPath(SmallVectorImpl &Buf, DWARFDie CU) { + sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), "")); +} + +/// Collect references to parseable Swift interfaces in imported +/// DW_TAG_module blocks. +static void analyzeImportedModule( + const DWARFDie &DIE, CompileUnit &CU, + swiftInterfacesMap *ParseableSwiftInterfaces, + std::function ReportWarning) { + if (CU.getLanguage() != dwarf::DW_LANG_Swift) + return; + + if (!ParseableSwiftInterfaces) + return; + + StringRef Path = dwarf::toStringRef(DIE.find(dwarf::DW_AT_LLVM_include_path)); + if (!Path.endswith(".swiftinterface")) + return; + if (Optional Val = DIE.find(dwarf::DW_AT_name)) + if (Optional Name = Val->getAsCString()) { + auto &Entry = (*ParseableSwiftInterfaces)[*Name]; + // The prepend path is applied later when copying. + DWARFDie CUDie = CU.getOrigUnit().getUnitDIE(); + SmallString<128> ResolvedPath; + if (sys::path::is_relative(Path)) + resolveRelativeObjectPath(ResolvedPath, CUDie); + sys::path::append(ResolvedPath, Path); + if (!Entry.empty() && Entry != ResolvedPath) + ReportWarning( + Twine("Conflicting parseable interfaces for Swift Module ") + + *Name + ": " + Entry + " and " + Path, + DIE); + Entry = ResolvedPath.str(); + } +} + +/// Recursive helper to build the global DeclContext information and +/// gather the child->parent relationships in the original compile unit. +/// +/// \return true when this DIE and all of its children are only +/// forward declarations to types defined in external clang modules +/// (i.e., forward declarations that are children of a DW_TAG_module). +static bool analyzeContextInfo( + const DWARFDie &DIE, unsigned ParentIdx, CompileUnit &CU, + DeclContext *CurrentDeclContext, UniquingStringPool &StringPool, + DeclContextTree &Contexts, uint64_t ModulesEndOffset, + swiftInterfacesMap *ParseableSwiftInterfaces, + std::function ReportWarning, + bool InImportedModule = false) { + unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); + CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx); + + // Clang imposes an ODR on modules(!) regardless of the language: + // "The module-id should consist of only a single identifier, + // which provides the name of the module being defined. Each + // module shall have a single definition." + // + // This does not extend to the types inside the modules: + // "[I]n C, this implies that if two structs are defined in + // different submodules with the same name, those two types are + // distinct types (but may be compatible types if their + // definitions match)." + // + // We treat non-C++ modules like namespaces for this reason. + if (DIE.getTag() == dwarf::DW_TAG_module && ParentIdx == 0 && + dwarf::toString(DIE.find(dwarf::DW_AT_name), "") != + CU.getClangModuleName()) { + InImportedModule = true; + analyzeImportedModule(DIE, CU, ParseableSwiftInterfaces, ReportWarning); + } + + Info.ParentIdx = ParentIdx; + bool InClangModule = CU.isClangModule() || InImportedModule; + if (CU.hasODR() || InClangModule) { + if (CurrentDeclContext) { + auto PtrInvalidPair = Contexts.getChildDeclContext( + *CurrentDeclContext, DIE, CU, StringPool, InClangModule); + CurrentDeclContext = PtrInvalidPair.getPointer(); + Info.Ctxt = + PtrInvalidPair.getInt() ? nullptr : PtrInvalidPair.getPointer(); + if (Info.Ctxt) + Info.Ctxt->setDefinedInClangModule(InClangModule); + } else + Info.Ctxt = CurrentDeclContext = nullptr; + } + + Info.Prune = InImportedModule; + if (DIE.hasChildren()) + for (auto Child : DIE.children()) + Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, + StringPool, Contexts, ModulesEndOffset, + ParseableSwiftInterfaces, ReportWarning, + InImportedModule); + + // Prune this DIE if it is either a forward declaration inside a + // DW_TAG_module or a DW_TAG_module that contains nothing but + // forward declarations. + Info.Prune &= (DIE.getTag() == dwarf::DW_TAG_module) || + (isTypeTag(DIE.getTag()) && + dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0)); + + // Only prune forward declarations inside a DW_TAG_module for which a + // definition exists elsewhere. + if (ModulesEndOffset == 0) + Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); + else + Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() > 0 && + Info.Ctxt->getCanonicalDIEOffset() <= ModulesEndOffset; + + return Info.Prune; +} + +static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { + switch (Tag) { + default: + return false; + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_common_block: + case dwarf::DW_TAG_lexical_block: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_union_type: + return true; + } + llvm_unreachable("Invalid Tag"); +} + +void DwarfOptimizer::CleanupAuxiliarryData(OptContext &Context) { + Context.Clear(); + + for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) + (*I)->~DIEBlock(); + for (auto I = DIELocs.begin(), E = DIELocs.end(); I != E; ++I) + (*I)->~DIELoc(); + + DIEBlocks.clear(); + DIELocs.clear(); + DIEAlloc.Reset(); +} + +/// Get the starting and ending (exclusive) offset for the +/// attribute with index \p Idx descibed by \p Abbrev. \p Offset is +/// supposed to point to the position of the first attribute described +/// by \p Abbrev. +/// \return [StartOffset, EndOffset) as a pair. +static std::pair +getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx, + uint64_t Offset, const DWARFUnit &Unit) { + DataExtractor Data = Unit.getDebugInfoExtractor(); + + for (unsigned i = 0; i < Idx; ++i) + DWARFFormValue::skipValue(Abbrev->getFormByIndex(i), Data, &Offset, + Unit.getFormParams()); + + uint64_t End = Offset; + DWARFFormValue::skipValue(Abbrev->getFormByIndex(Idx), Data, &End, + Unit.getFormParams()); + + return std::make_pair(Offset, End); +} + +/// Check if a variable describing DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DwarfOptimizer::shouldKeepVariableDIE(AddressesMap &RelocMgr, + const DWARFDie &DIE, + CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + // Global variables with constant value can always be kept. + if (!(Flags & TF_InFunctionScope) && + Abbrev->findAttributeIndex(dwarf::DW_AT_const_value)) { + MyInfo.InDebugMap = true; + return Flags | TF_Keep; + } + + Optional LocationIdx = + Abbrev->findAttributeIndex(dwarf::DW_AT_location); + if (!LocationIdx) + return Flags; + + uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + const DWARFUnit &OrigUnit = Unit.getOrigUnit(); + uint64_t LocationOffset, LocationEndOffset; + std::tie(LocationOffset, LocationEndOffset) = + getAttributeOffsets(Abbrev, *LocationIdx, Offset, OrigUnit); + + // See if there is a relocation to a valid debug map entry inside + // this variable's location. The order is important here. We want to + // always check if the variable has a valid relocation, so that the + // DIEInfo is filled. However, we don't want a static variable in a + // function to force us to keep the enclosing function. + if (!RelocMgr.hasValidRelocationAt(LocationOffset, LocationEndOffset, + MyInfo) || + (Flags & TF_InFunctionScope)) + return Flags; + + if (Options.Verbose) { + outs() << "Keeping variable DIE:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + DIE.dump(outs(), 8 /* Indent */, DumpOpts); + } + + return Flags | TF_Keep; +} + +/// Check if a function describing DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DwarfOptimizer::shouldKeepSubprogramDIE( + AddressesMap &RelocMgr, RangesTy &Ranges, const DWARFDie &DIE, + const DwarfOptObjFile &OF, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + Flags |= TF_InFunctionScope; + + Optional LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc); + if (!LowPcIdx) + return Flags; + + uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + uint64_t LowPcOffset, LowPcEndOffset; + std::tie(LowPcOffset, LowPcEndOffset) = + getAttributeOffsets(Abbrev, *LowPcIdx, Offset, OrigUnit); + + auto LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); + assert(LowPc.hasValue() && "low_pc attribute is not an address."); + if (!LowPc || + !RelocMgr.hasValidRelocationAt(LowPcOffset, LowPcEndOffset, MyInfo)) + return Flags; + + if (Options.Verbose) { + outs() << "Keeping subprogram DIE:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + DIE.dump(outs(), 8 /* Indent */, DumpOpts); + } + + if (DIE.getTag() == dwarf::DW_TAG_label) { + if (Unit.hasLabelAt(*LowPc)) + return Flags; + // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider labels + // that don't fall into the CU's aranges. This is wrong IMO. Debug info + // generation bugs aside, this is really wrong in the case of labels, where + // a label marking the end of a function will have a PC == CU's high_pc. + if (dwarf::toAddress(OrigUnit.getUnitDIE().find(dwarf::DW_AT_high_pc)) + .getValueOr(UINT64_MAX) <= LowPc) + return Flags; + Unit.addLabelLowPc(*LowPc, MyInfo.AddrAdjust); + return Flags | TF_Keep; + } + + Flags |= TF_Keep; + + Optional HighPc = DIE.getHighPC(*LowPc); + if (!HighPc) { + reportWarning("Function without high_pc. Range will be discarded.\n", OF, + &DIE); + return Flags; + } + + // Replace the debug map range with a more accurate one. + Ranges[*LowPc] = ObjFileAddressRange(*HighPc, MyInfo.AddrAdjust); + Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); + return Flags; +} + +/// Check if a DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DwarfOptimizer::shouldKeepDIE(AddressesMap &RelocMgr, + RangesTy &Ranges, const DWARFDie &DIE, + const DwarfOptObjFile &OF, + CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { + switch (DIE.getTag()) { + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_variable: + return shouldKeepVariableDIE(RelocMgr, DIE, Unit, MyInfo, Flags); + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_label: + return shouldKeepSubprogramDIE(RelocMgr, Ranges, DIE, OF, Unit, MyInfo, + Flags); + case dwarf::DW_TAG_base_type: + // DWARF Expressions may reference basic types, but scanning them + // is expensive. Basic types are tiny, so just keep all of them. + case dwarf::DW_TAG_imported_module: + case dwarf::DW_TAG_imported_declaration: + case dwarf::DW_TAG_imported_unit: + // We always want to keep these. + return Flags | TF_Keep; + default: + break; + } + + return Flags; +} + +/// Mark the passed DIE as well as all the ones it depends on +/// as kept. +/// +/// This function is called by lookForDIEsToKeep on DIEs that are newly +/// discovered to be needed for the generation. It recursively calls +/// back to lookForDIEsToKeep while adding TF_DependencyWalk to the +/// TraversalFlags to inform it that it's not doing the primary DIE +/// tree walk. +void DwarfOptimizer::keepDIEAndDependencies( + AddressesMap &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, + const DWARFDie &Die, CompileUnit::DIEInfo &MyInfo, + const DwarfOptObjFile &OF, CompileUnit &CU, bool UseODR) { + DWARFUnit &Unit = CU.getOrigUnit(); + MyInfo.Keep = true; + + // We're looking for incomplete types. + MyInfo.Incomplete = Die.getTag() != dwarf::DW_TAG_subprogram && + Die.getTag() != dwarf::DW_TAG_member && + dwarf::toUnsigned(Die.find(dwarf::DW_AT_declaration), 0); + + // First mark all the parent chain as kept. + unsigned AncestorIdx = MyInfo.ParentIdx; + while (!CU.getInfo(AncestorIdx).Keep) { + unsigned ODRFlag = UseODR ? TF_ODR : 0; + lookForDIEsToKeep(RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), + OF, CU, + TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); + AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; + } + + // Then we need to mark all the DIEs referenced by this DIE's + // attributes as kept. + DWARFDataExtractor Data = Unit.getDebugInfoExtractor(); + const auto *Abbrev = Die.getAbbreviationDeclarationPtr(); + uint64_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode()); + + // Mark all DIEs referenced through attributes as kept. + for (const auto &AttrSpec : Abbrev->attributes()) { + DWARFFormValue Val(AttrSpec.Form); + if (!Val.isFormClass(DWARFFormValue::FC_Reference) || + AttrSpec.Attr == dwarf::DW_AT_sibling) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, + Unit.getFormParams()); + continue; + } + + Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit); + CompileUnit *ReferencedCU; + if (auto RefDie = resolveDIEReference(OF, Units, Val, Die, ReferencedCU)) { + uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie); + CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); + bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() && + Info.Ctxt->isDefinedInClangModule(); + // If the referenced DIE has a DeclContext that has already been + // emitted, then do not keep the one in this CU. We'll link to + // the canonical DIE in cloneDieReferenceAttribute. + // FIXME: compatibility with dsymutil-classic. UseODR shouldn't + // be necessary and could be advantageously replaced by + // ReferencedCU->hasODR() && CU.hasODR(). + // FIXME: compatibility with dsymutil-classic. There is no + // reason not to unique ref_addr references. + if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && (UseODR || IsModuleRef) && + Info.Ctxt && + Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt && + Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr)) + continue; + + // Keep a module forward declaration if there is no definition. + if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt && + Info.Ctxt->getCanonicalDIEOffset())) + Info.Prune = false; + + unsigned ODRFlag = UseODR ? TF_ODR : 0; + lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, OF, *ReferencedCU, + TF_Keep | TF_DependencyWalk | ODRFlag); + + // The incomplete property is propagated if the current DIE is complete + // but references an incomplete DIE. + if (Info.Incomplete && !MyInfo.Incomplete && + (Die.getTag() == dwarf::DW_TAG_typedef || + Die.getTag() == dwarf::DW_TAG_member || + Die.getTag() == dwarf::DW_TAG_reference_type || + Die.getTag() == dwarf::DW_TAG_ptr_to_member_type || + Die.getTag() == dwarf::DW_TAG_pointer_type)) + MyInfo.Incomplete = true; + } + } +} + +namespace { +/// This class represents an item in the work list. In addition to it's obvious +/// purpose of representing the state associated with a particular run of the +/// work loop, it also serves as a marker to indicate that we should run the +/// "continuation" code. +/// +/// Originally, the latter was lambda which allowed arbitrary code to be run. +/// Because we always need to run the exact same code, it made more sense to +/// use a boolean and repurpose the already existing DIE field. +struct WorklistItem { + DWARFDie Die; + unsigned Flags; + bool IsContinuation; + CompileUnit::DIEInfo *ChildInfo = nullptr; + + /// Construct a classic worklist item. + WorklistItem(DWARFDie Die, unsigned Flags) + : Die(Die), Flags(Flags), IsContinuation(false) {} + + /// Creates a continuation marker. + WorklistItem(DWARFDie Die) : Die(Die), IsContinuation(true) {} +}; +} // namespace + +// Helper that updates the completeness of the current DIE. It depends on the +// fact that the incompletness of its children is already computed. +static void updateIncompleteness(const DWARFDie &Die, + CompileUnit::DIEInfo &ChildInfo, + CompileUnit &CU) { + // Only propagate incomplete members. + if (Die.getTag() != dwarf::DW_TAG_structure_type && + Die.getTag() != dwarf::DW_TAG_class_type) + return; + + unsigned Idx = CU.getOrigUnit().getDIEIndex(Die); + CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx); + + if (MyInfo.Incomplete) + return; + + if (ChildInfo.Incomplete || ChildInfo.Prune) + MyInfo.Incomplete = true; +} + +/// Recursively walk the \p DIE tree and look for DIEs to +/// keep. Store that information in \p CU's DIEInfo. +/// +/// This function is the entry point of the DIE selection +/// algorithm. It is expected to walk the DIE tree in file order and +/// (though the mediation of its helper) call hasValidRelocationAt() on +/// each DIE that might be a 'root DIE' (See DwarfOptimizer class +/// comment). +/// While walking the dependencies of root DIEs, this function is +/// also called, but during these dependency walks the file order is +/// not respected. The TF_DependencyWalk flag tells us which kind of +/// traversal we are currently doing. +/// +/// The return value indicates whether the DIE is incomplete. +void DwarfOptimizer::lookForDIEsToKeep(AddressesMap &AddressesMap, + RangesTy &Ranges, + const UnitListTy &Units, + const DWARFDie &Die, + const DwarfOptObjFile &OF, + CompileUnit &CU, unsigned Flags) { + // LIFO work list. + SmallVector Worklist; + Worklist.emplace_back(Die, Flags); + + while (!Worklist.empty()) { + WorklistItem Current = Worklist.back(); + Worklist.pop_back(); + + if (Current.IsContinuation) { + updateIncompleteness(Current.Die, *Current.ChildInfo, CU); + continue; + } + + unsigned Idx = CU.getOrigUnit().getDIEIndex(Current.Die); + CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx); + + // At this point we are guaranteed to have a continuation marker before us + // in the worklist, except for the last DIE. + if (!Worklist.empty()) + Worklist.back().ChildInfo = &MyInfo; + + if (MyInfo.Prune) + continue; + + // If the Keep flag is set, we are marking a required DIE's dependencies. + // If our target is already marked as kept, we're all set. + bool AlreadyKept = MyInfo.Keep; + if ((Current.Flags & TF_DependencyWalk) && AlreadyKept) + continue; + + // We must not call shouldKeepDIE while called from keepDIEAndDependencies, + // because it would screw up the relocation finding logic. + if (!(Current.Flags & TF_DependencyWalk)) + Current.Flags = shouldKeepDIE(AddressesMap, Ranges, Current.Die, OF, CU, + MyInfo, Current.Flags); + + // If it is a newly kept DIE mark it as well as all its dependencies as + // kept. + if (!AlreadyKept && (Current.Flags & TF_Keep)) { + bool UseOdr = (Current.Flags & TF_DependencyWalk) + ? (Current.Flags & TF_ODR) + : CU.hasODR(); + keepDIEAndDependencies(AddressesMap, Ranges, Units, Current.Die, MyInfo, + OF, CU, UseOdr); + } + + // The TF_ParentWalk flag tells us that we are currently walking up + // the parent chain of a required DIE, and we don't want to mark all + // the children of the parents as kept (consider for example a + // DW_TAG_namespace node in the parent chain). There are however a + // set of DIE types for which we want to ignore that directive and still + // walk their children. + if (dieNeedsChildrenToBeMeaningful(Current.Die.getTag())) + Current.Flags &= ~TF_ParentWalk; + + if (!Current.Die.hasChildren() || (Current.Flags & TF_ParentWalk)) + continue; + + // Add children in reverse order to the worklist to effectively process + // them in order. + for (auto Child : reverse(Current.Die.children())) { + // Add continuation marker before every child to calculate incompleteness + // after the last child is processed. We can't store this information in + // the same item because we might have to process other continuations + // first. + Worklist.emplace_back(Current.Die); + Worklist.emplace_back(Child, Current.Flags); + } + } +} + +/// Assign an abbreviation number to \p Abbrev. +/// +/// Our DIEs get freed after every DebugMapObject has been processed, +/// thus the FoldingSet we use to unique DIEAbbrevs cannot refer to +/// the instances hold by the DIEs. When we encounter an abbreviation +/// that we don't know, we create a permanent copy of it. +void DwarfOptimizer::AssignAbbrev(DIEAbbrev &Abbrev) { + // Check the set for priors. + FoldingSetNodeID ID; + Abbrev.Profile(ID); + void *InsertToken; + DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken); + + // If it's newly added. + if (InSet) { + // Assign existing abbreviation number. + Abbrev.setNumber(InSet->getNumber()); + } else { + // Add to abbreviation list. + Abbreviations.push_back( + std::make_unique(Abbrev.getTag(), Abbrev.hasChildren())); + for (const auto &Attr : Abbrev.getData()) + Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm()); + AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken); + // Assign the unique abbreviation number. + Abbrev.setNumber(Abbreviations.size()); + Abbreviations.back()->setNumber(Abbreviations.size()); + } +} + +unsigned DwarfOptimizer::DIECloner::cloneStringAttribute( + DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, + const DWARFUnit &U, OffsetsStringPool &StringPool, AttributesInfo &Info) { + // Switch everything to out of line strings. + const char *String = *Val.getAsCString(); + auto StringEntry = StringPool.getEntry(String); + + // Update attributes info. + if (AttrSpec.Attr == dwarf::DW_AT_name) + Info.Name = StringEntry; + else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || + AttrSpec.Attr == dwarf::DW_AT_linkage_name) + Info.MangledName = StringEntry; + + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, + DIEInteger(StringEntry.getOffset())); + + return 4; +} + +unsigned DwarfOptimizer::DIECloner::cloneDieReferenceAttribute( + DIE &Die, const DWARFDie &InputDIE, AttributeSpec AttrSpec, + unsigned AttrSize, const DWARFFormValue &Val, const DwarfOptObjFile &OF, + CompileUnit &Unit) { + const DWARFUnit &U = Unit.getOrigUnit(); + uint64_t Ref = *Val.getAsReference(); + + DIE *NewRefDie = nullptr; + CompileUnit *RefUnit = nullptr; + DeclContext *Ctxt = nullptr; + + DWARFDie RefDie = + Optimizer.resolveDIEReference(OF, CompileUnits, Val, InputDIE, RefUnit); + + // If the referenced DIE is not found, drop the attribute. + if (!RefDie || AttrSpec.Attr == dwarf::DW_AT_sibling) + return 0; + + unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie); + CompileUnit::DIEInfo &RefInfo = RefUnit->getInfo(Idx); + + // If we already have emitted an equivalent DeclContext, just point + // at it. + if (isODRAttribute(AttrSpec.Attr)) { + Ctxt = RefInfo.Ctxt; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) { + DIEInteger Attr(Ctxt->getCanonicalDIEOffset()); + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, Attr); + return U.getRefAddrByteSize(); + } + } + + if (!RefInfo.Clone) { + assert(Ref > InputDIE.getOffset()); + // We haven't cloned this DIE yet. Just create an empty one and + // store it. It'll get really cloned when we process it. + RefInfo.Clone = DIE::get(DIEAlloc, dwarf::Tag(RefDie.getTag())); + } + NewRefDie = RefInfo.Clone; + + if (AttrSpec.Form == dwarf::DW_FORM_ref_addr || + (Unit.hasODR() && isODRAttribute(AttrSpec.Attr))) { + // We cannot currently rely on a DIEEntry to emit ref_addr + // references, because the implementation calls back to DwarfDebug + // to find the unit offset. (We don't have a DwarfDebug) + // FIXME: we should be able to design DIEEntry reliance on + // DwarfDebug away. + uint64_t Attr; + if (Ref < InputDIE.getOffset()) { + // We must have already cloned that DIE. + uint32_t NewRefOffset = + RefUnit->getStartOffset() + NewRefDie->getOffset(); + Attr = NewRefOffset; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, DIEInteger(Attr)); + } else { + // A forward reference. Note and fixup later. + Attr = 0xBADDEF; + Unit.noteForwardReference( + NewRefDie, RefUnit, Ctxt, + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, DIEInteger(Attr))); + } + return U.getRefAddrByteSize(); + } + + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEEntry(*NewRefDie)); + + return AttrSize; +} + +void DwarfOptimizer::DIECloner::cloneExpression( + DataExtractor &Data, DWARFExpression Expression, const DwarfOptObjFile &OF, + CompileUnit &Unit, SmallVectorImpl &OutputBuffer) { + using Encoding = DWARFExpression::Operation::Encoding; + + uint64_t OpOffset = 0; + for (auto &Op : Expression) { + auto Description = Op.getDescription(); + // DW_OP_const_type is variable-length and has 3 + // operands. DWARFExpression thus far only supports 2. + auto Op0 = Description.Op[0]; + auto Op1 = Description.Op[1]; + if ((Op0 == Encoding::BaseTypeRef && Op1 != Encoding::SizeNA) || + (Op1 == Encoding::BaseTypeRef && Op0 != Encoding::Size1)) + Optimizer.reportWarning("Unsupported DW_OP encoding.", OF); + + if ((Op0 == Encoding::BaseTypeRef && Op1 == Encoding::SizeNA) || + (Op1 == Encoding::BaseTypeRef && Op0 == Encoding::Size1)) { + // This code assumes that the other non-typeref operand fits into 1 byte. + assert(OpOffset < Op.getEndOffset()); + uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1; + assert(ULEBsize <= 16); + + // Copy over the operation. + OutputBuffer.push_back(Op.getCode()); + uint64_t RefOffset; + if (Op1 == Encoding::SizeNA) { + RefOffset = Op.getRawOperand(0); + } else { + OutputBuffer.push_back(Op.getRawOperand(0)); + RefOffset = Op.getRawOperand(1); + } + auto RefDie = Unit.getOrigUnit().getDIEForOffset(RefOffset); + uint32_t RefIdx = Unit.getOrigUnit().getDIEIndex(RefDie); + CompileUnit::DIEInfo &Info = Unit.getInfo(RefIdx); + uint32_t Offset = 0; + if (DIE *Clone = Info.Clone) + Offset = Clone->getOffset(); + else + Optimizer.reportWarning( + "base type ref doesn't point to DW_TAG_base_type.", OF); + uint8_t ULEB[16]; + unsigned RealSize = encodeULEB128(Offset, ULEB, ULEBsize); + if (RealSize > ULEBsize) { + // Emit the generic type as a fallback. + RealSize = encodeULEB128(0, ULEB, ULEBsize); + Optimizer.reportWarning("base type ref doesn't fit.", OF); + } + assert(RealSize == ULEBsize && "padding failed"); + ArrayRef ULEBbytes(ULEB, ULEBsize); + OutputBuffer.append(ULEBbytes.begin(), ULEBbytes.end()); + } else { + // Copy over everything else unmodified. + StringRef Bytes = Data.getData().slice(OpOffset, Op.getEndOffset()); + OutputBuffer.append(Bytes.begin(), Bytes.end()); + } + OpOffset = Op.getEndOffset(); + } +} + +unsigned DwarfOptimizer::DIECloner::cloneBlockAttribute( + DIE &Die, const DwarfOptObjFile &OF, CompileUnit &Unit, + AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, + bool IsLittleEndian) { + DIEValueList *Attr; + DIEValue Value; + DIELoc *Loc = nullptr; + DIEBlock *Block = nullptr; + if (AttrSpec.Form == dwarf::DW_FORM_exprloc) { + Loc = new (DIEAlloc) DIELoc; + Optimizer.DIELocs.push_back(Loc); + } else { + Block = new (DIEAlloc) DIEBlock; + Optimizer.DIEBlocks.push_back(Block); + } + Attr = Loc ? static_cast(Loc) + : static_cast(Block); + + if (Loc) + Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), Loc); + else + Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), Block); + + // If the block is a DWARF Expression, clone it into the temporary + // buffer using cloneExpression(), otherwise copy the data directly. + SmallVector Buffer; + ArrayRef Bytes = *Val.getAsBlock(); + if (DWARFAttribute::mayHaveLocationDescription(AttrSpec.Attr) && + (Val.isFormClass(DWARFFormValue::FC_Block) || + Val.isFormClass(DWARFFormValue::FC_Exprloc))) { + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + DataExtractor Data(StringRef((const char *)Bytes.data(), Bytes.size()), + IsLittleEndian, OrigUnit.getAddressByteSize()); + DWARFExpression Expr(Data, OrigUnit.getVersion(), + OrigUnit.getAddressByteSize()); + cloneExpression(Data, Expr, OF, Unit, Buffer); + Bytes = Buffer; + } + for (auto Byte : Bytes) + Attr->addValue(DIEAlloc, static_cast(0), + dwarf::DW_FORM_data1, DIEInteger(Byte)); + + // FIXME: If DIEBlock and DIELoc just reuses the Size field of + // the DIE class, this "if" could be replaced by + // Attr->setSize(Bytes.size()). + if (Loc) + Loc->setSize(Bytes.size()); + else + Block->setSize(Bytes.size()); + + Die.addValue(DIEAlloc, Value); + return AttrSize; +} + +unsigned DwarfOptimizer::DIECloner::cloneAddressAttribute( + DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, + const CompileUnit &Unit, AttributesInfo &Info) { + uint64_t Addr = *Val.getAsAddress(); + + if (LLVM_UNLIKELY(Optimizer.Options.Update)) { + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) + Info.HasLowPc = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Addr)); + return Unit.getOrigUnit().getAddressByteSize(); + } + + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) { + if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine || + Die.getTag() == dwarf::DW_TAG_lexical_block) + // The low_pc of a block or inline subroutine might get + // relocated because it happens to match the low_pc of the + // enclosing subprogram. To prevent issues with that, always use + // the low_pc from the input DIE if relocations have been applied. + Addr = (Info.OrigLowPc != std::numeric_limits::max() + ? Info.OrigLowPc + : Addr) + + Info.PCOffset; + else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { + Addr = Unit.getLowPc(); + if (Addr == std::numeric_limits::max()) + return 0; + } + Info.HasLowPc = true; + } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc) { + if (Die.getTag() == dwarf::DW_TAG_compile_unit) { + if (uint64_t HighPc = Unit.getHighPc()) + Addr = HighPc; + else + return 0; + } else + // If we have a high_pc recorded for the input DIE, use + // it. Otherwise (when no relocations where applied) just use the + // one we just decoded. + Addr = (Info.OrigHighPc ? Info.OrigHighPc : Addr) + Info.PCOffset; + } + + Die.addValue(DIEAlloc, static_cast(AttrSpec.Attr), + static_cast(AttrSpec.Form), DIEInteger(Addr)); + return Unit.getOrigUnit().getAddressByteSize(); +} + +unsigned DwarfOptimizer::DIECloner::cloneScalarAttribute( + DIE &Die, const DWARFDie &InputDIE, const DwarfOptObjFile &OF, + CompileUnit &Unit, AttributeSpec AttrSpec, const DWARFFormValue &Val, + unsigned AttrSize, AttributesInfo &Info) { + uint64_t Value; + + if (LLVM_UNLIKELY(Optimizer.Options.Update)) { + if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSectionOffset()) + Value = *OptionalValue; + else { + Optimizer.reportWarning( + "Unsupported scalar attribute form. Dropping attribute.", OF, + &InputDIE); + return 0; + } + if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + Info.IsDeclaration = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Value)); + return AttrSize; + } + + if (AttrSpec.Attr == dwarf::DW_AT_high_pc && + Die.getTag() == dwarf::DW_TAG_compile_unit) { + if (Unit.getLowPc() == -1ULL) + return 0; + // Dwarf >= 4 high_pc is an size, not an address. + Value = Unit.getHighPc() - Unit.getLowPc(); + } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) + Value = *Val.getAsSectionOffset(); + else if (AttrSpec.Form == dwarf::DW_FORM_sdata) + Value = *Val.getAsSignedConstant(); + else if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else { + Optimizer.reportWarning( + "Unsupported scalar attribute form. Dropping attribute.", OF, + &InputDIE); + return 0; + } + PatchLocation Patch = + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Value)); + if (AttrSpec.Attr == dwarf::DW_AT_ranges) { + Unit.noteRangeAttribute(Die, Patch); + Info.HasRanges = true; + } + + // A more generic way to check for location attributes would be + // nice, but it's very unlikely that any other attribute needs a + // location list. + // FIXME: use DWARFAttribute::mayHaveLocationDescription(). + else if (AttrSpec.Attr == dwarf::DW_AT_location || + AttrSpec.Attr == dwarf::DW_AT_frame_base) { + Unit.noteLocationAttribute(Patch, Info.PCOffset); + } else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + Info.IsDeclaration = true; + + return AttrSize; +} + +/// Clone \p InputDIE's attribute described by \p AttrSpec with +/// value \p Val, and add it to \p Die. +/// \returns the size of the cloned attribute. +unsigned DwarfOptimizer::DIECloner::cloneAttribute( + DIE &Die, const DWARFDie &InputDIE, const DwarfOptObjFile &OF, + CompileUnit &Unit, OffsetsStringPool &StringPool, const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info, + bool IsLittleEndian) { + const DWARFUnit &U = Unit.getOrigUnit(); + + switch (AttrSpec.Form) { + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_string: + return cloneStringAttribute(Die, AttrSpec, Val, U, StringPool, Info); + case dwarf::DW_FORM_ref_addr: + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + return cloneDieReferenceAttribute(Die, InputDIE, AttrSpec, AttrSize, Val, + OF, Unit); + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + case dwarf::DW_FORM_exprloc: + return cloneBlockAttribute(Die, OF, Unit, AttrSpec, Val, AttrSize, + IsLittleEndian); + case dwarf::DW_FORM_addr: + return cloneAddressAttribute(Die, AttrSpec, Val, Unit, Info); + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_sdata: + case dwarf::DW_FORM_sec_offset: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_flag_present: + return cloneScalarAttribute(Die, InputDIE, OF, Unit, AttrSpec, Val, + AttrSize, Info); + default: + Optimizer.reportWarning( + "Unsupported attribute form in cloneAttribute. Dropping.", OF, + &InputDIE); + } + + return 0; +} + +static bool isObjCSelector(StringRef Name) { + return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') && + (Name[1] == '['); +} + +void DwarfOptimizer::DIECloner::addObjCAccelerator( + CompileUnit &Unit, const DIE *Die, DwarfStringPoolEntryRef Name, + OffsetsStringPool &StringPool, bool SkipPubSection) { + assert(isObjCSelector(Name.getString()) && "not an objc selector"); + // Objective C method or class function. + // "- [Class(Category) selector :withArg ...]" + StringRef ClassNameStart(Name.getString().drop_front(2)); + size_t FirstSpace = ClassNameStart.find(' '); + if (FirstSpace == StringRef::npos) + return; + + StringRef SelectorStart(ClassNameStart.data() + FirstSpace + 1); + if (!SelectorStart.size()) + return; + + StringRef Selector(SelectorStart.data(), SelectorStart.size() - 1); + Unit.addNameAccelerator(Die, StringPool.getEntry(Selector), SkipPubSection); + + // Add an entry for the class name that points to this + // method/class function. + StringRef ClassName(ClassNameStart.data(), FirstSpace); + Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassName), SkipPubSection); + + if (ClassName[ClassName.size() - 1] == ')') { + size_t OpenParens = ClassName.find('('); + if (OpenParens != StringRef::npos) { + StringRef ClassNameNoCategory(ClassName.data(), OpenParens); + Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassNameNoCategory), + SkipPubSection); + + std::string MethodNameNoCategory(Name.getString().data(), OpenParens + 2); + // FIXME: The missing space here may be a bug, but + // dsymutil-classic also does it this way. + MethodNameNoCategory.append(SelectorStart); + Unit.addNameAccelerator(Die, StringPool.getEntry(MethodNameNoCategory), + SkipPubSection); + } + } +} + +static bool +shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, + uint16_t Tag, bool InDebugMap, bool SkipPC, + bool InFunctionScope) { + switch (AttrSpec.Attr) { + default: + return false; + case dwarf::DW_AT_low_pc: + case dwarf::DW_AT_high_pc: + case dwarf::DW_AT_ranges: + return SkipPC; + case dwarf::DW_AT_location: + case dwarf::DW_AT_frame_base: + // FIXME: for some reason dsymutil-classic keeps the location attributes + // when they are of block type (i.e. not location lists). This is totally + // wrong for globals where we will keep a wrong address. It is mostly + // harmless for locals, but there is no point in keeping these anyway when + // the function wasn't linked. + return (SkipPC || (!InFunctionScope && Tag == dwarf::DW_TAG_variable && + !InDebugMap)) && + !DWARFFormValue(AttrSpec.Form).isFormClass(DWARFFormValue::FC_Block); + } +} + +DIE *DwarfOptimizer::DIECloner::cloneDIE( + const DWARFDie &InputDIE, const DwarfOptObjFile &OF, CompileUnit &Unit, + OffsetsStringPool &StringPool, int64_t PCOffset, uint32_t OutOffset, + unsigned Flags, bool IsLittleEndian, DIE *Die) { + DWARFUnit &U = Unit.getOrigUnit(); + unsigned Idx = U.getDIEIndex(InputDIE); + CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); + + // Should the DIE appear in the output? + if (!Unit.getInfo(Idx).Keep) + return nullptr; + + uint64_t Offset = InputDIE.getOffset(); + assert(!(Die && Info.Clone) && "Can't supply a DIE and a cloned DIE"); + if (!Die) { + // The DIE might have been already created by a forward reference + // (see cloneDieReferenceAttribute()). + if (!Info.Clone) + Info.Clone = DIE::get(DIEAlloc, dwarf::Tag(InputDIE.getTag())); + Die = Info.Clone; + } + + assert(Die->getTag() == InputDIE.getTag()); + Die->setOffset(OutOffset); + if ((Unit.hasODR() || Unit.isClangModule()) && !Info.Incomplete && + Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt && + Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt && + !Info.Ctxt->getCanonicalDIEOffset()) { + // We are about to emit a DIE that is the root of its own valid + // DeclContext tree. Make the current offset the canonical offset + // for this context. + Info.Ctxt->setCanonicalDIEOffset(OutOffset + Unit.getStartOffset()); + } + + // Extract and clone every attribute. + DWARFDataExtractor Data = U.getDebugInfoExtractor(); + // Point to the next DIE (generally there is always at least a NULL + // entry after the current one). If this is a lone + // DW_TAG_compile_unit without any children, point to the next unit. + uint64_t NextOffset = (Idx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(Idx + 1).getOffset() + : U.getNextUnitOffset(); + AttributesInfo AttrInfo; + + // We could copy the data only if we need to apply a relocation to it. After + // testing, it seems there is no performance downside to doing the copy + // unconditionally, and it makes the code simpler. + SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset)); + Data = + DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); + + // Modify the copy with relocated addresses. + if (ObjFile.Addresses->AreRelocationsResolved() && + ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, + Data.isLittleEndian())) { + // If we applied relocations, we store the value of high_pc that was + // potentially stored in the input DIE. If high_pc is an address + // (Dwarf version == 2), then it might have been relocated to a + // totally unrelated value (because the end address in the object + // file might be start address of another function which got moved + // independently by the linker). The computation of the actual + // high_pc value is done in cloneAddressAttribute(). + AttrInfo.OrigHighPc = + dwarf::toAddress(InputDIE.find(dwarf::DW_AT_high_pc), 0); + // Also store the low_pc. It might get relocated in an + // inline_subprogram that happens at the beginning of its + // inlining function. + AttrInfo.OrigLowPc = dwarf::toAddress(InputDIE.find(dwarf::DW_AT_low_pc), + std::numeric_limits::max()); + } + + // Reset the Offset to 0 as we will be working on the local copy of + // the data. + Offset = 0; + + const auto *Abbrev = InputDIE.getAbbreviationDeclarationPtr(); + Offset += getULEB128Size(Abbrev->getCode()); + + // We are entering a subprogram. Get and propagate the PCOffset. + if (Die->getTag() == dwarf::DW_TAG_subprogram) + PCOffset = Info.AddrAdjust; + AttrInfo.PCOffset = PCOffset; + + if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) { + Flags |= TF_InFunctionScope; + if (!Info.InDebugMap && LLVM_LIKELY(!Update)) + Flags |= TF_SkipPC; + } + + bool Copied = false; + for (const auto &AttrSpec : Abbrev->attributes()) { + if (LLVM_LIKELY(!Update) && + shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, + Flags & TF_SkipPC, Flags & TF_InFunctionScope)) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, + U.getFormParams()); + // FIXME: dsymutil-classic keeps the old abbreviation around + // even if it's not used. We can remove this (and the copyAbbrev + // helper) as soon as bit-for-bit compatibility is not a goal anymore. + if (!Copied) { + copyAbbrev(*InputDIE.getAbbreviationDeclarationPtr(), Unit.hasODR()); + Copied = true; + } + continue; + } + + DWARFFormValue Val(AttrSpec.Form); + uint64_t AttrSize = Offset; + Val.extractValue(Data, &Offset, U.getFormParams(), &U); + AttrSize = Offset - AttrSize; + + OutOffset += cloneAttribute(*Die, InputDIE, OF, Unit, StringPool, Val, + AttrSpec, AttrSize, AttrInfo, IsLittleEndian); + } + + // Look for accelerator entries. + uint16_t Tag = InputDIE.getTag(); + // FIXME: This is slightly wrong. An inline_subroutine without a + // low_pc, but with AT_ranges might be interesting to get into the + // accelerator tables too. For now stick with dsymutil's behavior. + if ((Info.InDebugMap || AttrInfo.HasLowPc || AttrInfo.HasRanges) && + Tag != dwarf::DW_TAG_compile_unit && + getDIENames(InputDIE, AttrInfo, StringPool, + Tag != dwarf::DW_TAG_inlined_subroutine)) { + if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name) + Unit.addNameAccelerator(Die, AttrInfo.MangledName, + Tag == dwarf::DW_TAG_inlined_subroutine); + if (AttrInfo.Name) { + if (AttrInfo.NameWithoutTemplate) + Unit.addNameAccelerator(Die, AttrInfo.NameWithoutTemplate, + /* SkipPubSection */ true); + Unit.addNameAccelerator(Die, AttrInfo.Name, + Tag == dwarf::DW_TAG_inlined_subroutine); + } + if (AttrInfo.Name && isObjCSelector(AttrInfo.Name.getString())) + addObjCAccelerator(Unit, Die, AttrInfo.Name, StringPool, + /* SkipPubSection =*/true); + + } else if (Tag == dwarf::DW_TAG_namespace) { + if (!AttrInfo.Name) + AttrInfo.Name = StringPool.getEntry("(anonymous namespace)"); + Unit.addNamespaceAccelerator(Die, AttrInfo.Name); + } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && + getDIENames(InputDIE, AttrInfo, StringPool) && AttrInfo.Name && + AttrInfo.Name.getString()[0]) { + uint32_t Hash = hashFullyQualifiedName(InputDIE, Unit, OF); + uint64_t RuntimeLang = + dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class)) + .getValueOr(0); + bool ObjCClassIsImplementation = + (RuntimeLang == dwarf::DW_LANG_ObjC || + RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) && + dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type)) + .getValueOr(0); + Unit.addTypeAccelerator(Die, AttrInfo.Name, ObjCClassIsImplementation, + Hash); + } + + // Determine whether there are any children that we want to keep. + bool HasChildren = false; + for (auto Child : InputDIE.children()) { + unsigned Idx = U.getDIEIndex(Child); + if (Unit.getInfo(Idx).Keep) { + HasChildren = true; + break; + } + } + + DIEAbbrev NewAbbrev = Die->generateAbbrev(); + if (HasChildren) + NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); + // Assign a permanent abbrev number + Optimizer.AssignAbbrev(NewAbbrev); + Die->setAbbrevNumber(NewAbbrev.getNumber()); + + // Add the size of the abbreviation number to the output offset. + OutOffset += getULEB128Size(Die->getAbbrevNumber()); + + if (!HasChildren) { + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; + } + + // Recursively clone children. + for (auto Child : InputDIE.children()) { + if (DIE *Clone = cloneDIE(Child, OF, Unit, StringPool, PCOffset, OutOffset, + Flags, IsLittleEndian)) { + Die->addChild(Clone); + OutOffset = Clone->getOffset() + Clone->getSize(); + } + } + + // Account for the end of children marker. + OutOffset += sizeof(int8_t); + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; +} + +/// Patch the input object file relevant debug_ranges entries +/// and emit them in the output file. Update the relevant attributes +/// to point at the new entries. +void DwarfOptimizer::patchRangesForUnit(const CompileUnit &Unit, + DWARFContext &OrigDwarf, + const DwarfOptObjFile &OF) const { + DWARFDebugRangeList RangeList; + const auto &FunctionRanges = Unit.getFunctionRanges(); + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + DWARFDataExtractor RangeExtractor(OrigDwarf.getDWARFObj(), + OrigDwarf.getDWARFObj().getRangesSection(), + OrigDwarf.isLittleEndian(), AddressSize); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + auto OrigUnitDie = OrigUnit.getUnitDIE(false); + uint64_t OrigLowPc = + dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), -1ULL); + // Ranges addresses are based on the unit's low_pc. Compute the + // offset we need to apply to adapt to the new unit's low_pc. + int64_t UnitPcOffset = 0; + if (OrigLowPc != -1ULL) + UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); + + for (const auto &RangeAttribute : Unit.getRangesAttributes()) { + uint64_t Offset = RangeAttribute.get(); + RangeAttribute.set(dwarfEmitter.getRangesSectionSize()); + if (Error E = RangeList.extract(RangeExtractor, &Offset)) { + llvm::consumeError(std::move(E)); + reportWarning("invalid range list ignored.", OF); + RangeList.clear(); + } + const auto &Entries = RangeList.getEntries(); + if (!Entries.empty()) { + const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); + + if (CurrRange == InvalidRange || + First.StartAddress + OrigLowPc < CurrRange.start() || + First.StartAddress + OrigLowPc >= CurrRange.stop()) { + CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); + if (CurrRange == InvalidRange || + CurrRange.start() > First.StartAddress + OrigLowPc) { + reportWarning("no mapping for range.", OF); + continue; + } + } + } + + dwarfEmitter.emitRangesEntries(UnitPcOffset, OrigLowPc, CurrRange, Entries, + AddressSize); + } +} + +/// Generate the debug_aranges entries for \p Unit and if the +/// unit has a DW_AT_ranges attribute, also emit the debug_ranges +/// contribution for this attribute. +/// FIXME: this could actually be done right in patchRangesForUnit, +/// but for the sake of initial bit-for-bit compatibility with legacy +/// dsymutil, we have to do it in a delayed pass. +void DwarfOptimizer::generateUnitRanges(CompileUnit &Unit) const { + auto Attr = Unit.getUnitRangesAttribute(); + if (Attr) + Attr->set(dwarfEmitter.getRangesSectionSize()); + dwarfEmitter.emitUnitRangesEntries(Unit, static_cast(Attr)); +} + +/// Insert the new line info sequence \p Seq into the current +/// set of already linked line info \p Rows. +static void insertLineSequence(std::vector &Seq, + std::vector &Rows) { + if (Seq.empty()) + return; + + if (!Rows.empty() && Rows.back().Address < Seq.front().Address) { + Rows.insert(Rows.end(), Seq.begin(), Seq.end()); + Seq.clear(); + return; + } + + object::SectionedAddress Front = Seq.front().Address; + auto InsertPoint = partition_point( + Rows, [=](const DWARFDebugLine::Row &O) { return O.Address < Front; }); + + // FIXME: this only removes the unneeded end_sequence if the + // sequences have been inserted in order. Using a global sort like + // described in patchLineTableForUnit() and delaying the end_sequene + // elimination to emitLineTableForUnit() we can get rid of all of them. + if (InsertPoint != Rows.end() && InsertPoint->Address == Front && + InsertPoint->EndSequence) { + *InsertPoint = Seq.front(); + Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end()); + } else { + Rows.insert(InsertPoint, Seq.begin(), Seq.end()); + } + + Seq.clear(); +} + +static void patchStmtList(DIE &Die, DIEInteger Offset) { + for (auto &V : Die.values()) + if (V.getAttribute() == dwarf::DW_AT_stmt_list) { + V = DIEValue(V.getAttribute(), V.getForm(), Offset); + return; + } + + llvm_unreachable("Didn't find DW_AT_stmt_list in cloned DIE!"); +} + +/// Extract the line table for \p Unit from \p OrigDwarf, and +/// recreate a relocated version of these for the address ranges that +/// are present in the binary. +void DwarfOptimizer::patchLineTableForUnit(CompileUnit &Unit, + DWARFContext &OrigDwarf, + const DwarfOptObjFile &OF) { + DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); + auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); + if (!StmtList) + return; + + // Update the cloned DW_AT_stmt_list with the correct debug_line offset. + if (auto *OutputDIE = Unit.getOutputUnitDIE()) + patchStmtList(*OutputDIE, DIEInteger(dwarfEmitter.getLineSectionSize())); + + RangesTy &Ranges = OF.Addresses->getValidAddressRanges(); + + // Parse the original line info for the unit. + DWARFDebugLine::LineTable LineTable; + uint64_t StmtOffset = *StmtList; + DWARFDataExtractor LineExtractor( + OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(), + OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize()); + if (NeedToTranslateStrings()) + return dwarfEmitter.translateLineTable(LineExtractor, StmtOffset); + + Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, + &Unit.getOrigUnit(), DWARFContext::dumpWarning); + DWARFContext::dumpWarning(std::move(Err)); + + // This vector is the output line table. + std::vector NewRows; + NewRows.reserve(LineTable.Rows.size()); + + // Current sequence of rows being extracted, before being inserted + // in NewRows. + std::vector Seq; + const auto &FunctionRanges = Unit.getFunctionRanges(); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + + // FIXME: This logic is meant to generate exactly the same output as + // Darwin's classic dsymutil. There is a nicer way to implement this + // by simply putting all the relocated line info in NewRows and simply + // sorting NewRows before passing it to emitLineTableForUnit. This + // should be correct as sequences for a function should stay + // together in the sorted output. There are a few corner cases that + // look suspicious though, and that required to implement the logic + // this way. Revisit that once initial validation is finished. + + // Iterate over the object file line info and extract the sequences + // that correspond to linked functions. + for (auto &Row : LineTable.Rows) { + // Check whether we stepped out of the range. The range is + // half-open, but consider accept the end address of the range if + // it is marked as end_sequence in the input (because in that + // case, the relocation offset is accurate and that entry won't + // serve as the start of another function). + if (CurrRange == InvalidRange || Row.Address.Address < CurrRange.start() || + Row.Address.Address > CurrRange.stop() || + (Row.Address.Address == CurrRange.stop() && !Row.EndSequence)) { + // We just stepped out of a known range. Insert a end_sequence + // corresponding to the end of the range. + uint64_t StopAddress = CurrRange != InvalidRange + ? CurrRange.stop() + CurrRange.value() + : -1ULL; + CurrRange = FunctionRanges.find(Row.Address.Address); + bool CurrRangeValid = + CurrRange != InvalidRange && CurrRange.start() <= Row.Address.Address; + if (!CurrRangeValid) { + CurrRange = InvalidRange; + if (StopAddress != -1ULL) { + // Try harder by looking in the Address ranges map. + // There are corner cases where this finds a + // valid entry. It's unclear if this is right or wrong, but + // for now do as dsymutil. + // FIXME: Understand exactly what cases this addresses and + // potentially remove it along with the Ranges map. + auto Range = Ranges.lower_bound(Row.Address.Address); + if (Range != Ranges.begin() && Range != Ranges.end()) + --Range; + + if (Range != Ranges.end() && Range->first <= Row.Address.Address && + Range->second.HighPC >= Row.Address.Address) { + StopAddress = Row.Address.Address + Range->second.Offset; + } + } + } + if (StopAddress != -1ULL && !Seq.empty()) { + // Insert end sequence row with the computed end address, but + // the same line as the previous one. + auto NextLine = Seq.back(); + NextLine.Address.Address = StopAddress; + NextLine.EndSequence = 1; + NextLine.PrologueEnd = 0; + NextLine.BasicBlock = 0; + NextLine.EpilogueBegin = 0; + Seq.push_back(NextLine); + insertLineSequence(Seq, NewRows); + } + + if (!CurrRangeValid) + continue; + } + + // Ignore empty sequences. + if (Row.EndSequence && Seq.empty()) + continue; + + // Relocate row address and add it to the current sequence. + Row.Address.Address += CurrRange.value(); + Seq.emplace_back(Row); + + if (Row.EndSequence) + insertLineSequence(Seq, NewRows); + } + + // Finished extracting, now emit the line tables. + // FIXME: LLVM hard-codes its prologue values. We just copy the + // prologue over and that works because we act as both producer and + // consumer. It would be nicer to have a real configurable line + // table emitter. + if (LineTable.Prologue.getVersion() < 2 || + LineTable.Prologue.getVersion() > 5 || + LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || + LineTable.Prologue.OpcodeBase > 13) + reportWarning("line table parameters mismatch. Cannot emit.", OF); + else { + uint32_t PrologueEnd = *StmtList + 10 + LineTable.Prologue.PrologueLength; + // DWARF v5 has an extra 2 bytes of information before the header_length + // field. + if (LineTable.Prologue.getVersion() == 5) + PrologueEnd += 2; + StringRef LineData = OrigDwarf.getDWARFObj().getLineSection().Data; + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; + Params.DWARF2LineBase = LineTable.Prologue.LineBase; + Params.DWARF2LineRange = LineTable.Prologue.LineRange; + dwarfEmitter.emitLineTableForUnit( + Params, LineData.slice(*StmtList + 4, PrologueEnd), + LineTable.Prologue.MinInstLength, NewRows, + Unit.getOrigUnit().getAddressByteSize()); + } +} + +void DwarfOptimizer::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { + switch (Options.TheAccelTableKind) { + case AccelTableKind::Apple: + emitAppleAcceleratorEntriesForUnit(Unit); + break; + case AccelTableKind::Dwarf: + emitDwarfAcceleratorEntriesForUnit(Unit); + break; + case AccelTableKind::Default: + llvm_unreachable("The default must be updated to a concrete value."); + break; + } +} + +void DwarfOptimizer::emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit) { + // Add namespaces. + for (const auto &Namespace : Unit.getNamespaces()) + AppleNamespaces.addName(Namespace.Name, + Namespace.Die->getOffset() + Unit.getStartOffset()); + + /// Add names. + dwarfEmitter.emitPubNamesForUnit(Unit); + for (const auto &Pubname : Unit.getPubnames()) + AppleNames.addName(Pubname.Name, + Pubname.Die->getOffset() + Unit.getStartOffset()); + + /// Add types. + dwarfEmitter.emitPubTypesForUnit(Unit); + for (const auto &Pubtype : Unit.getPubtypes()) + AppleTypes.addName( + Pubtype.Name, Pubtype.Die->getOffset() + Unit.getStartOffset(), + Pubtype.Die->getTag(), + Pubtype.ObjcClassImplementation ? dwarf::DW_FLAG_type_implementation + : 0, + Pubtype.QualifiedNameHash); + + /// Add ObjC names. + for (const auto &ObjC : Unit.getObjC()) + AppleObjc.addName(ObjC.Name, ObjC.Die->getOffset() + Unit.getStartOffset()); +} + +void DwarfOptimizer::emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit) { + for (const auto &Namespace : Unit.getNamespaces()) + DebugNames.addName(Namespace.Name, Namespace.Die->getOffset(), + Namespace.Die->getTag(), Unit.getUniqueID()); + for (const auto &Pubname : Unit.getPubnames()) + DebugNames.addName(Pubname.Name, Pubname.Die->getOffset(), + Pubname.Die->getTag(), Unit.getUniqueID()); + for (const auto &Pubtype : Unit.getPubtypes()) + DebugNames.addName(Pubtype.Name, Pubtype.Die->getOffset(), + Pubtype.Die->getTag(), Unit.getUniqueID()); +} + +/// Read the frame info stored in the object, and emit the +/// patched frame descriptions for the resulting file. +/// +/// This is actually pretty easy as the data of the CIEs and FDEs can +/// be considered as black boxes and moved as is. The only thing to do +/// is to patch the addresses in the headers. +void DwarfOptimizer::patchFrameInfoForObject(const DwarfOptObjFile &OF, + RangesTy &Ranges, + DWARFContext &OrigDwarf, + unsigned AddrSize) { + StringRef FrameData = OrigDwarf.getDWARFObj().getFrameSection().Data; + if (FrameData.empty()) + return; + + DataExtractor Data(FrameData, OrigDwarf.isLittleEndian(), 0); + uint64_t InputOffset = 0; + + // Store the data of the CIEs defined in this object, keyed by their + // offsets. + DenseMap LocalCIES; + + while (Data.isValidOffset(InputOffset)) { + uint64_t EntryOffset = InputOffset; + uint32_t InitialLength = Data.getU32(&InputOffset); + if (InitialLength == 0xFFFFFFFF) + return reportWarning("Dwarf64 bits no supported", OF); + + uint32_t CIEId = Data.getU32(&InputOffset); + if (CIEId == 0xFFFFFFFF) { + // This is a CIE, store it. + StringRef CIEData = FrameData.substr(EntryOffset, InitialLength + 4); + LocalCIES[EntryOffset] = CIEData; + // The -4 is to account for the CIEId we just read. + InputOffset += InitialLength - 4; + continue; + } + + uint32_t Loc = Data.getUnsigned(&InputOffset, AddrSize); + + // Some compilers seem to emit frame info that doesn't start at + // the function entry point, thus we can't just lookup the address + // in the debug map. Use the AddressInfo's range map to see if the FDE + // describes something that we can relocate. + auto Range = Ranges.upper_bound(Loc); + if (Range != Ranges.begin()) + --Range; + if (Range == Ranges.end() || Range->first > Loc || + Range->second.HighPC <= Loc) { + // The +4 is to account for the size of the InitialLength field itself. + InputOffset = EntryOffset + InitialLength + 4; + continue; + } + + // This is an FDE, and we have a mapping. + // Have we already emitted a corresponding CIE? + StringRef CIEData = LocalCIES[CIEId]; + if (CIEData.empty()) + return reportWarning("Inconsistent debug_frame content. Dropping.", OF); + + // Look if we already emitted a CIE that corresponds to the + // referenced one (the CIE data is the key of that lookup). + auto IteratorInserted = EmittedCIEs.insert( + std::make_pair(CIEData, dwarfEmitter.getFrameSectionSize())); + // If there is no CIE yet for this ID, emit it. + if (IteratorInserted.second || + // FIXME: dsymutil-classic only caches the last used CIE for + // reuse. Mimic that behavior for now. Just removing that + // second half of the condition and the LastCIEOffset variable + // makes the code DTRT. + LastCIEOffset != IteratorInserted.first->getValue()) { + LastCIEOffset = dwarfEmitter.getFrameSectionSize(); + IteratorInserted.first->getValue() = LastCIEOffset; + dwarfEmitter.emitCIE(CIEData); + } + + // Emit the FDE with updated address and CIE pointer. + // (4 + AddrSize) is the size of the CIEId + initial_location + // fields that will get reconstructed by emitFDE(). + unsigned FDERemainingBytes = InitialLength - (4 + AddrSize); + dwarfEmitter.emitFDE(IteratorInserted.first->getValue(), AddrSize, + Loc + Range->second.Offset, + FrameData.substr(InputOffset, FDERemainingBytes)); + InputOffset += FDERemainingBytes; + } +} + +void DwarfOptimizer::DIECloner::copyAbbrev( + const DWARFAbbreviationDeclaration &Abbrev, bool hasODR) { + DIEAbbrev Copy(dwarf::Tag(Abbrev.getTag()), + dwarf::Form(Abbrev.hasChildren())); + + for (const auto &Attr : Abbrev.attributes()) { + uint16_t Form = Attr.Form; + if (hasODR && isODRAttribute(Attr.Attr)) + Form = dwarf::DW_FORM_ref_addr; + Copy.AddAttribute(dwarf::Attribute(Attr.Attr), dwarf::Form(Form)); + } + + Optimizer.AssignAbbrev(Copy); +} + +uint32_t +DwarfOptimizer::DIECloner::hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, + const DwarfOptObjFile &OF, + int ChildRecurseDepth) { + const char *Name = nullptr; + DWARFUnit *OrigUnit = &U.getOrigUnit(); + CompileUnit *CU = &U; + Optional Ref; + + while (1) { + if (const char *CurrentName = DIE.getName(DINameKind::ShortName)) + Name = CurrentName; + + if (!(Ref = DIE.find(dwarf::DW_AT_specification)) && + !(Ref = DIE.find(dwarf::DW_AT_abstract_origin))) + break; + + if (!Ref->isFormClass(DWARFFormValue::FC_Reference)) + break; + + CompileUnit *RefCU; + if (auto RefDIE = + Optimizer.resolveDIEReference(OF, CompileUnits, *Ref, DIE, RefCU)) { + CU = RefCU; + OrigUnit = &RefCU->getOrigUnit(); + DIE = RefDIE; + } + } + + unsigned Idx = OrigUnit->getDIEIndex(DIE); + if (!Name && DIE.getTag() == dwarf::DW_TAG_namespace) + Name = "(anonymous namespace)"; + + if (CU->getInfo(Idx).ParentIdx == 0 || + // FIXME: dsymutil-classic compatibility. Ignore modules. + CU->getOrigUnit().getDIEAtIndex(CU->getInfo(Idx).ParentIdx).getTag() == + dwarf::DW_TAG_module) + return djbHash(Name ? Name : "", djbHash(ChildRecurseDepth ? "" : "::")); + + DWARFDie Die = OrigUnit->getDIEAtIndex(CU->getInfo(Idx).ParentIdx); + return djbHash( + (Name ? Name : ""), + djbHash((Name ? "::" : ""), + hashFullyQualifiedName(Die, *CU, OF, ++ChildRecurseDepth))); +} + +static uint64_t getDwoId(const DWARFDie &CUDie, const DWARFUnit &Unit) { + auto DwoId = dwarf::toUnsigned( + CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id})); + if (DwoId) + return *DwoId; + return 0; +} + +bool DwarfOptimizer::registerModuleReference( + DWARFDie CUDie, const DWARFUnit &Unit, const DwarfOptObjFile &OF, + OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, unsigned &UnitID, + bool IsLittleEndian, unsigned Indent, bool Quiet) { + std::string PCMfile = dwarf::toString( + CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); + if (PCMfile.empty()) + return false; + + // Clang module DWARF skeleton CUs abuse this for the path to the module. + uint64_t DwoId = getDwoId(CUDie, Unit); + + std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); + if (Name.empty()) { + if (!Quiet) + reportWarning("Anonymous module skeleton CU for " + PCMfile, OF); + return true; + } + + if (!Quiet && Options.Verbose) { + outs().indent(Indent); + outs() << "Found clang module reference " << PCMfile; + } + + auto Cached = ClangModules.find(PCMfile); + if (Cached != ClangModules.end()) { + // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is + // fixed in clang, only warn about DWO_id mismatches in verbose mode. + // ASTFileSignatures will change randomly when a module is rebuilt. + if (!Quiet && Options.Verbose && (Cached->second != DwoId)) + reportWarning(Twine("hash mismatch: this object file was built against a " + "different version of the module ") + + PCMfile, + OF); + if (!Quiet && Options.Verbose) + outs() << " [cached].\n"; + return true; + } + if (!Quiet && Options.Verbose) + outs() << " ...\n"; + + // Cyclic dependencies are disallowed by Clang, but we still + // shouldn't run into an infinite loop, so mark it as processed now. + ClangModules.insert({PCMfile, DwoId}); + + if (Error E = + loadClangModule(CUDie, PCMfile, Name, DwoId, OF, StringPool, + UniquingStringPool, ODRContexts, ModulesEndOffset, + UnitID, IsLittleEndian, Indent + 2, Quiet)) { + consumeError(std::move(E)); + return false; + } + return true; +} + +Error DwarfOptimizer::loadClangModule( + DWARFDie CUDie, StringRef Filename, StringRef ModuleName, uint64_t DwoId, + const DwarfOptObjFile &OF, OffsetsStringPool &StringPool, + UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, + uint64_t ModulesEndOffset, unsigned &UnitID, bool IsLittleEndian, + unsigned Indent, bool Quiet) { + /// Using a SmallString<0> because loadClangModule() is recursive. + SmallString<0> Path(Options.PrependPath); + if (sys::path::is_relative(Filename)) + resolveRelativeObjectPath(Path, CUDie); + sys::path::append(Path, Filename); + // Don't use the cached binary holder because we have no thread-safety + // guarantee and the lifetime is limited. + + if (Options.ObjFileLoader == nullptr) + return Error::success(); + + auto ErrOrObj = Options.ObjFileLoader(OF.FileName, Path); + if (!ErrOrObj) + return Error::success(); + + std::unique_ptr Unit; + + // Setup access to the debug info. + auto DwarfContext = DWARFContext::create(*ErrOrObj->ObjFile); + + for (const auto &CU : DwarfContext->compile_units()) { + updateDwarfVersion(CU->getVersion()); + // Recursively get all modules imported by this one. + auto CUDie = CU->getUnitDIE(false); + if (!CUDie) + continue; + if (!registerModuleReference(CUDie, *CU, OF, StringPool, UniquingStringPool, + ODRContexts, ModulesEndOffset, UnitID, + IsLittleEndian, Indent, Quiet)) { + if (Unit) { + std::string Err = + (Filename + + ": Clang modules are expected to have exactly 1 compile unit.\n") + .str(); + reportError(Err, OF); + return make_error(Err, inconvertibleErrorCode()); + } + // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is + // fixed in clang, only warn about DWO_id mismatches in verbose mode. + // ASTFileSignatures will change randomly when a module is rebuilt. + uint64_t PCMDwoId = getDwoId(CUDie, *CU); + if (PCMDwoId != DwoId) { + if (!Quiet && Options.Verbose) + reportWarning( + Twine("hash mismatch: this object file was built against a " + "different version of the module ") + + Filename, + OF); + // Update the cache entry with the DwoId of the module loaded from disk. + ClangModules[Filename] = PCMDwoId; + } + + // Add this module. + Unit = std::make_unique(*CU, UnitID++, !Options.NoODR, + ModuleName); + Unit->setHasInterestingContent(); + analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), + UniquingStringPool, ODRContexts, ModulesEndOffset, + Options.ParseableSwiftInterfaces, + [&](const Twine &Warning, const DWARFDie &DIE) { + reportWarning(Warning, OF, &DIE); + }); + // Keep everything. + Unit->markEverythingAsKept(); + } + } + if (!Unit->getOrigUnit().getUnitDIE().hasChildren()) + return Error::success(); + if (!Quiet && Options.Verbose) { + outs().indent(Indent); + outs() << "cloning .debug_info from " << Filename << "\n"; + } + + UnitListTy CompileUnits; + CompileUnits.push_back(std::move(Unit)); + DIECloner(*this, dwarfEmitter, *ErrOrObj, DIEAlloc, CompileUnits, + Options.Update) + .cloneAllCompileUnits(*DwarfContext, OF, StringPool, IsLittleEndian); + return Error::success(); +} + +void DwarfOptimizer::DIECloner::cloneAllCompileUnits( + DWARFContext &DwarfContext, const DwarfOptObjFile &OF, + OffsetsStringPool &StringPool, bool IsLittleEndian) { + uint64_t OutputDebugInfoSize = Emitter.getDebugInfoSectionSize(); + for (auto &CurrentUnit : CompileUnits) { + auto InputDIE = CurrentUnit->getOrigUnit().getUnitDIE(); + CurrentUnit->setStartOffset(OutputDebugInfoSize); + if (!InputDIE) { + OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); + continue; + } + if (CurrentUnit->getInfo(0).Keep) { + // Clone the InputDIE into your Unit DIE in our compile unit since it + // already has a DIE inside of it. + CurrentUnit->createOutputDIE(); + cloneDIE(InputDIE, OF, *CurrentUnit, StringPool, 0 /* PC offset */, + 11 /* Unit Header size */, 0, IsLittleEndian, + CurrentUnit->getOutputUnitDIE()); + } + + OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); + + if (!Optimizer.Options.NoOutput) { + // FIXME: for compatibility with the classic dsymutil, we emit + // an empty line table for the unit, even if the unit doesn't + // actually exist in the DIE tree. + if (LLVM_LIKELY(!Optimizer.Options.Update) || + Optimizer.NeedToTranslateStrings()) + Optimizer.patchLineTableForUnit(*CurrentUnit, DwarfContext, OF); + + Optimizer.emitAcceleratorEntriesForUnit(*CurrentUnit); + + if (LLVM_UNLIKELY(Optimizer.Options.Update)) + continue; + + Optimizer.patchRangesForUnit(*CurrentUnit, DwarfContext, OF); + auto ProcessExpr = [&](StringRef Bytes, + SmallVectorImpl &Buffer) { + DWARFUnit &OrigUnit = CurrentUnit->getOrigUnit(); + DataExtractor Data(Bytes, IsLittleEndian, + OrigUnit.getAddressByteSize()); + cloneExpression(Data, + DWARFExpression(Data, OrigUnit.getVersion(), + OrigUnit.getAddressByteSize()), + OF, *CurrentUnit, Buffer); + }; + Emitter.emitLocationsForUnit(*CurrentUnit, DwarfContext, ProcessExpr); + } + } + + if (!Optimizer.Options.NoOutput) { + // Emit all the compile unit's debug information. + for (auto &CurrentUnit : CompileUnits) { + if (LLVM_LIKELY(!Optimizer.Options.Update)) + Optimizer.generateUnitRanges(*CurrentUnit); + + CurrentUnit->fixupForwardReferences(); + + if (!CurrentUnit->getOutputUnitDIE()) + continue; + + assert(Emitter.getDebugInfoSectionSize() == + CurrentUnit->getStartOffset()); + Emitter.emitCompileUnitHeader(*CurrentUnit); + Emitter.emitDIE(*CurrentUnit->getOutputUnitDIE()); + assert(Emitter.getDebugInfoSectionSize() == + CurrentUnit->computeNextUnitOffset()); + } + } +} + +void DwarfOptimizer::updateAccelKind(DWARFContext &Dwarf) { + if (Options.TheAccelTableKind != AccelTableKind::Default) + return; + + auto &DwarfObj = Dwarf.getDWARFObj(); + + if (!AtLeastOneDwarfAccelTable && + (!DwarfObj.getAppleNamesSection().Data.empty() || + !DwarfObj.getAppleTypesSection().Data.empty() || + !DwarfObj.getAppleNamespacesSection().Data.empty() || + !DwarfObj.getAppleObjCSection().Data.empty())) { + AtLeastOneAppleAccelTable = true; + } + + if (!AtLeastOneDwarfAccelTable && !DwarfObj.getNamesSection().Data.empty()) { + AtLeastOneDwarfAccelTable = true; + } +} + +bool DwarfOptimizer::emitPaperTrailWarnings(const DwarfOptObjFile &OF, + OffsetsStringPool &StringPool) { + + if (OF.Warnings.empty() || (OF.ObjFile && (OF.ObjFile->symbols().begin() != + OF.ObjFile->symbols().end()))) + return false; + + DIE *CUDie = DIE::get(DIEAlloc, dwarf::DW_TAG_compile_unit); + CUDie->setOffset(11); + StringRef Producer; + StringRef WarningHeader; + + switch (OptimizerClientID) { + case DwarfOptimizerClient::Dsymutil: + Producer = StringPool.internString("dsymutil"); + WarningHeader = "dsymutil_warning"; + break; + + default: + Producer = StringPool.internString("dwarfopt"); + WarningHeader = "dwarfopt_warning"; + break; + } + + StringRef File = StringPool.internString(OF.FileName); + CUDie->addValue(DIEAlloc, dwarf::DW_AT_producer, dwarf::DW_FORM_strp, + DIEInteger(StringPool.getStringOffset(Producer))); + DIEBlock *String = new (DIEAlloc) DIEBlock(); + DIEBlocks.push_back(String); + for (auto &C : File) + String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, + DIEInteger(C)); + String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, + DIEInteger(0)); + + CUDie->addValue(DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_string, String); + for (const auto &Warning : OF.Warnings) { + DIE &ConstDie = CUDie->addChild(DIE::get(DIEAlloc, dwarf::DW_TAG_constant)); + ConstDie.addValue(DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, + DIEInteger(StringPool.getStringOffset(WarningHeader))); + ConstDie.addValue(DIEAlloc, dwarf::DW_AT_artificial, dwarf::DW_FORM_flag, + DIEInteger(1)); + ConstDie.addValue(DIEAlloc, dwarf::DW_AT_const_value, dwarf::DW_FORM_strp, + DIEInteger(StringPool.getStringOffset(Warning))); + } + unsigned Size = 4 /* FORM_strp */ + File.size() + 1 + + OF.Warnings.size() * (4 + 1 + 4) + 1 /* End of children */; + DIEAbbrev Abbrev = CUDie->generateAbbrev(); + AssignAbbrev(Abbrev); + CUDie->setAbbrevNumber(Abbrev.getNumber()); + Size += getULEB128Size(Abbrev.getNumber()); + // Abbreviation ordering needed for classic compatibility. + for (auto &Child : CUDie->children()) { + Abbrev = Child.generateAbbrev(); + AssignAbbrev(Abbrev); + Child.setAbbrevNumber(Abbrev.getNumber()); + Size += getULEB128Size(Abbrev.getNumber()); + } + CUDie->setSize(Size); + + dwarfEmitter.emitPaperTrailWarnings(triple, *CUDie); + + return true; +} + +void DwarfOptimizer::copyInvariantDebugSection(const object::ObjectFile &Obj) { + if (!NeedToTranslateStrings()) { + dwarfEmitter.emitSectionContents(Obj, "debug_line"); + } + dwarfEmitter.emitSectionContents(Obj, "debug_loc"); + dwarfEmitter.emitSectionContents(Obj, "debug_ranges"); + dwarfEmitter.emitSectionContents(Obj, "debug_frame"); + dwarfEmitter.emitSectionContents(Obj, "debug_aranges"); +} + +void DwarfOptimizer::addObjectFile(DwarfOptObjFile &objFile) { + ObjectContexts.emplace_back(OptContext(objFile)); + + if (ObjectContexts.back().dwarfContext) + updateAccelKind(*ObjectContexts.back().dwarfContext); +} + +bool DwarfOptimizer::optimize() { + // A unique ID that identifies each compile unit. + unsigned UnitID = 0; + + // First populate the data structure we need for each iteration of the + // parallel loop. + unsigned NumObjects = ObjectContexts.size(); + + // This Dwarf string pool which is only used for uniquing. This one should + // never be used for offsets as its not thread-safe or predictable. + UniquingStringPool UniquingStringPool; + + // This Dwarf string pool which is used for emission. It must be used + // serially as the order of calling getStringOffset matters for + // reproducibility. + OffsetsStringPool OffsetsStringPool(StringsTranslator); + + // ODR Contexts for the optimize. + DeclContextTree ODRContexts; + + // If we haven't decided on an accelerator table kind yet, we base ourselves + // on the DWARF we have seen so far. At this point we haven't pulled in debug + // information from modules yet, so it is technically possible that they + // would affect the decision. However, as they're built with the same + // compiler and flags, it is safe to assume that they will follow the + // decision made here. + if (Options.TheAccelTableKind == AccelTableKind::Default) { + if (AtLeastOneDwarfAccelTable && !AtLeastOneAppleAccelTable) + Options.TheAccelTableKind = AccelTableKind::Dwarf; + else + Options.TheAccelTableKind = AccelTableKind::Apple; + } + + for (OptContext &OptContext : ObjectContexts) { + if (Options.Verbose) { + if (OptimizerClientID == DwarfOptimizerClient::Dsymutil) + outs() << "DEBUG MAP OBJECT: " << OptContext.objectFile.FileName + << "\n"; + else + outs() << "OBJECT FILE: " << OptContext.objectFile.FileName << "\n"; + } + + if (emitPaperTrailWarnings(OptContext.objectFile, OffsetsStringPool)) + continue; + + if (!OptContext.objectFile.ObjFile) + continue; + // Look for relocations that correspond to address map entries. + + // there was findvalidrelocations previously ... probably we need to gather + // info here + if (LLVM_LIKELY(!Options.Update) && + !OptContext.objectFile.Addresses->hasValidRelocs()) { + if (Options.Verbose) + outs() << "No valid relocations found. Skipping.\n"; + + // Set "skip" flag as a signal to other loops that we should not + // process this iteration. + OptContext.skip = true; + continue; + } + + // Setup access to the debug info. + if (!OptContext.dwarfContext) + continue; + + // In a first phase, just read in the debug info and load all clang modules. + OptContext.compileUnits.reserve( + OptContext.dwarfContext->getNumCompileUnits()); + + for (const auto &CU : OptContext.dwarfContext->compile_units()) { + updateDwarfVersion(CU->getVersion()); + auto CUDie = CU->getUnitDIE(false); + if (Options.Verbose) { + outs() << "Input compilation unit:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + CUDie.dump(outs(), 0, DumpOpts); + } + if (CUDie && !LLVM_UNLIKELY(Options.Update)) + registerModuleReference(CUDie, *CU, OptContext.objectFile, + OffsetsStringPool, UniquingStringPool, + ODRContexts, 0, UnitID, + OptContext.dwarfContext->isLittleEndian()); + } + } + + // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway. + if (MaxDwarfVersion == 0) + MaxDwarfVersion = 3; + + // At this point we know how much data we have emitted. We use this value to + // compare canonical DIE offsets in analyzeContextInfo to see if a definition + // is already emitted, without being affected by canonical die offsets set + // later. This prevents undeterminism when analyze and clone execute + // concurrently, as clone set the canonical DIE offset and analyze reads it. + const uint64_t ModulesEndOffset = dwarfEmitter.getDebugInfoSectionSize(); + + // These variables manage the list of processed object files. + // The mutex and condition variable are to ensure that this is thread safe. + std::mutex ProcessedFilesMutex; + std::condition_variable ProcessedFilesConditionVariable; + BitVector ProcessedFiles(NumObjects, false); + + // Analyzing the context info is particularly expensive so it is executed in + // parallel with emitting the previous compile unit. + auto AnalyzeLambda = [&](size_t i) { + auto &Context = ObjectContexts[i]; + + if (Context.skip || !Context.dwarfContext) + return; + + for (const auto &CU : Context.dwarfContext->compile_units()) { + updateDwarfVersion(CU->getVersion()); + // The !registerModuleReference() condition effectively skips + // over fully resolved skeleton units. This second pass of + // registerModuleReferences doesn't do any new work, but it + // will collect top-level errors, which are suppressed. Module + // warnings were already displayed in the first iteration. + bool Quiet = true; + auto CUDie = CU->getUnitDIE(false); + if (!CUDie || LLVM_UNLIKELY(Options.Update) || + !registerModuleReference(CUDie, *CU, Context.objectFile, + OffsetsStringPool, UniquingStringPool, + ODRContexts, ModulesEndOffset, UnitID, + Quiet)) { + Context.compileUnits.push_back(std::make_unique( + *CU, UnitID++, !Options.NoODR && !Options.Update, "")); + } + } + + // Now build the DIE parent links that we will use during the next phase. + for (auto &CurrentUnit : Context.compileUnits) { + auto CUDie = CurrentUnit->getOrigUnit().getUnitDIE(); + if (!CUDie) + continue; + analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, + *CurrentUnit, &ODRContexts.getRoot(), + UniquingStringPool, ODRContexts, ModulesEndOffset, + Options.ParseableSwiftInterfaces, + [&](const Twine &Warning, const DWARFDie &DIE) { + reportWarning(Warning, Context.objectFile, &DIE); + }); + } + }; + + // And then the remaining work in serial again. + // Note, although this loop runs in serial, it can run in parallel with + // the analyzeContextInfo loop so long as we process files with indices >= + // than those processed by analyzeContextInfo. + auto CloneLambda = [&](size_t i) { + auto &OptContext = ObjectContexts[i]; + if (OptContext.skip || !OptContext.objectFile.ObjFile) + return; + + // Then mark all the DIEs that need to be present in the generated output + // and collect some information about them. + // Note that this loop can not be merged with the previous one because + // cross-cu references require the ParentIdx to be setup for every CU in + // the object file before calling this. + if (LLVM_UNLIKELY(Options.Update)) { + for (auto &CurrentUnit : OptContext.compileUnits) + CurrentUnit->markEverythingAsKept(); + copyInvariantDebugSection(*OptContext.objectFile.ObjFile); + } else { + for (auto &CurrentUnit : OptContext.compileUnits) + lookForDIEsToKeep( + *OptContext.objectFile.Addresses, + OptContext.objectFile.Addresses->getValidAddressRanges(), + OptContext.compileUnits, CurrentUnit->getOrigUnit().getUnitDIE(), + OptContext.objectFile, *CurrentUnit, 0); + } + + // The calls to applyValidRelocs inside cloneDIE will walk the reloc + // array again (in the same way findValidRelocsInDebugInfo() did). We + // need to reset the NextValidReloc index to the beginning. + if (OptContext.objectFile.Addresses->hasValidRelocs() || + LLVM_UNLIKELY(Options.Update)) { + DIECloner(*this, dwarfEmitter, OptContext.objectFile, DIEAlloc, + OptContext.compileUnits, Options.Update) + .cloneAllCompileUnits(*OptContext.dwarfContext, OptContext.objectFile, + OffsetsStringPool, + OptContext.dwarfContext->isLittleEndian()); + } + if (!Options.NoOutput && !OptContext.compileUnits.empty() && + LLVM_LIKELY(!Options.Update)) + patchFrameInfoForObject( + OptContext.objectFile, + OptContext.objectFile.Addresses->getValidAddressRanges(), + *OptContext.dwarfContext, + OptContext.compileUnits[0]->getOrigUnit().getAddressByteSize()); + + // Clean-up before starting working on the next object. + CleanupAuxiliarryData(OptContext); + }; + + auto EmitLambda = [&]() { + // Emit everything that's global. + if (!Options.NoOutput) { + dwarfEmitter.emitAbbrevs(Abbreviations, MaxDwarfVersion); + dwarfEmitter.emitStrings(OffsetsStringPool); + switch (Options.TheAccelTableKind) { + case AccelTableKind::Apple: + dwarfEmitter.emitAppleNames(AppleNames); + dwarfEmitter.emitAppleNamespaces(AppleNamespaces); + dwarfEmitter.emitAppleTypes(AppleTypes); + dwarfEmitter.emitAppleObjc(AppleObjc); + break; + case AccelTableKind::Dwarf: + dwarfEmitter.emitDebugNames(DebugNames); + break; + case AccelTableKind::Default: + llvm_unreachable("Default should have already been resolved."); + break; + } + } + }; + + auto AnalyzeAll = [&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + AnalyzeLambda(i); + + std::unique_lock LockGuard(ProcessedFilesMutex); + ProcessedFiles.set(i); + ProcessedFilesConditionVariable.notify_one(); + } + }; + + auto CloneAll = [&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + { + std::unique_lock LockGuard(ProcessedFilesMutex); + if (!ProcessedFiles[i]) { + ProcessedFilesConditionVariable.wait( + LockGuard, [&]() { return ProcessedFiles[i]; }); + } + } + + CloneLambda(i); + } + EmitLambda(); + }; + + // To limit memory usage in the single threaded case, analyze and clone are + // run sequentially so the OptContext is freed after processing each object + // in endDebugObject. + if (Options.Threads == 1) { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + AnalyzeLambda(i); + CloneLambda(i); + } + EmitLambda(); + + } else { + ThreadPool pool(2); + pool.async(AnalyzeAll); + pool.async(CloneAll); + pool.wait(); + } + + return true; +} + +} // namespace llvm Index: llvm/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/tools/dsymutil/CMakeLists.txt +++ llvm/tools/dsymutil/CMakeLists.txt @@ -21,14 +21,11 @@ dsymutil.cpp BinaryHolder.cpp CFBundle.cpp - CompileUnit.cpp DebugMap.cpp - DeclContext.cpp DwarfLinker.cpp DwarfStreamer.cpp MachODebugMapParser.cpp MachOUtils.cpp - NonRelocatableStringpool.cpp SymbolMap.cpp DEPENDS Index: llvm/tools/dsymutil/DwarfLinker.h =================================================================== --- llvm/tools/dsymutil/DwarfLinker.h +++ llvm/tools/dsymutil/DwarfLinker.h @@ -10,50 +10,26 @@ #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" +#include "llvm/DebugInfo/DWARF/DWARFOptimizer.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Remarks/RemarkLinker.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 { +/// driven by DWARFOptimizer. DwarfLinker reads DebugMap objects +/// and pass information to the DWARFOptimizer. DWARFOptimizer +/// optimizes DWARF taking into account valid relocations. +/// Finally, optimized DWARF is passed to DwarfLinker through +/// DWARFEmitter interface. +class DwarfLinker : public DwarfEmitter { public: DwarfLinker(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, LinkOptions Options) @@ -62,25 +38,100 @@ /// Link the contents of the DebugMap. bool link(const DebugMap &); - void reportWarning(const Twine &Warning, const DebugMapObject &DMO, + void reportWarning(const Twine &Warning, StringRef Context, const DWARFDie *DIE = nullptr) const; -private: - /// Remembers the oldest and newest DWARF version we've seen in a unit. - void updateDwarfVersion(unsigned Version) { - MaxDwarfVersion = std::max(MaxDwarfVersion, Version); - MinDwarfVersion = std::min(MinDwarfVersion, Version); - } + bool emitPaperTrailWarnings(const DwarfOptObjFile &OF, + OffsetsStringPool &StringPool); - /// Remembers the kinds of accelerator tables we've seen in a unit. - void updateAccelKind(DWARFContext &Dwarf); + void emitPaperTrailWarnings(const Triple &triple, DIE &die) override; + void emitSectionContents(const object::ObjectFile &Obj, + StringRef SecName) override; + /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. + void emitAbbrevs(const std::vector> &Abbrevs, + unsigned DwarfVersion) override; + /// Emit the string table described by \p Pool. + void emitStrings(const NonRelocatableStringpool &Pool) override; + + /// Emit DWARF debug names. + void emitDebugNames(AccelTable &Table) override; + + /// Emit Apple namespaces accelerator table. + void emitAppleNamespaces( + AccelTable &Table) override; + + /// Emit Apple names accelerator table. + void + emitAppleNames(AccelTable &Table) override; + + /// Emit Apple Objective-C accelerator table. + void + emitAppleObjc(AccelTable &Table) override; + + /// Emit Apple type accelerator table. + void + emitAppleTypes(AccelTable &Table) override; + + void emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector &Entries, + unsigned AddressSize) override; + + /// Emit debug_aranges entries for \p Unit and if \p DoRangesSection is true, + /// also emit the debug_ranges entries for the DW_TAG_compile_unit's + /// DW_AT_ranges attribute. + void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection) override; + + void translateLineTable(DataExtractor LineData, uint64_t Offset) override; + + /// Emit the line table described in \p Rows into the debug_line section. + void emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, unsigned MinInstLength, + std::vector &Rows, + unsigned AdddressSize) override; + + /// Emit the .debug_pubnames contribution for \p Unit. + void emitPubNamesForUnit(const CompileUnit &Unit) override; + + /// Emit the .debug_pubtypes contribution for \p Unit. + void emitPubTypesForUnit(const CompileUnit &Unit) override; + + /// Emit a CIE. + void emitCIE(StringRef CIEBytes) override; + + /// Emit an FDE with data \p Bytes. + void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes) override; + + /// Emit 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, + std::function &)> ProcessExpr) + override; + + /// Emit the compilation unit header for \p Unit in the + /// debug_info section. + /// + /// As a side effect, this also switches the current Dwarf version + /// of the MC layer to the one of U.getOrigUnit(). + void emitCompileUnitHeader(CompileUnit &Unit) override; - /// Emit warnings as Dwarf compile units to leave a trail after linking. - bool emitPaperTrailWarnings(const DebugMapObject &DMO, const DebugMap &Map, - OffsetsStringPool &StringPool); + /// Recursively emit the DIE tree rooted at \p Die. + void emitDIE(DIE &Die) override; + + uint64_t getLineSectionSize() const override; + + uint64_t getFrameSectionSize() const override; + + uint64_t getRangesSectionSize() const override; + + uint64_t getDebugInfoSectionSize() const override; /// Keeps track of relocations. - class RelocationManager { + class AddressManager : public AddressesMap { struct ValidReloc { uint64_t Offset; uint32_t Size; @@ -108,13 +159,47 @@ /// cheap lookup during the root DIE selection and during DIE cloning. unsigned NextValidReloc = 0; + RangesTy AddressRanges; + public: - RelocationManager(DwarfLinker &Linker) : Linker(Linker) {} + AddressManager(DwarfLinker &Linker, const object::ObjectFile &Obj, + const DebugMapObject &DMO) + : Linker(Linker) { + findValidRelocsInDebugInfo(Obj, DMO); + + // Iterate over the debug map entries and put all the ones that are + // functions (because they have a size) into the Ranges map. This map is + // very similar to the FunctionRanges that are stored in each unit, with 2 + // notable differences: + // + // 1. Obviously this one is global, while the other ones are per-unit. + // + // 2. This one contains not only the functions described in the DIE + // tree, but also the ones that are only in the debug map. + // + // The latter information is required to reproduce dsymutil's logic while + // linking line tables. The cases where this information matters look like + // bugs that need to be investigated, but for now we need to reproduce + // dsymutil's behavior. + // FIXME: Once we understood exactly if that information is needed, + // maybe totally remove this (or try to use it to do a real + // -gline-tables-only on Darwin. + for (const auto &Entry : DMO.symbols()) { + const auto &Mapping = Entry.getValue(); + if (Mapping.Size && Mapping.ObjectAddress) + AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( + *Mapping.ObjectAddress + Mapping.Size, + int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + } + } - bool hasValidRelocs() const { return !ValidRelocs.empty(); } + virtual bool AreRelocationsResolved() const override { return true; } - /// Reset the NextValidReloc counter. - void resetValidRelocs() { NextValidReloc = 0; } + bool hasValidRelocs(bool resetRelocsPtr = true) override { + if (resetRelocsPtr) + NextValidReloc = 0; + return !ValidRelocs.empty(); + } /// \defgroup FindValidRelocations Translate debug map into a list /// of relevant relocations @@ -132,327 +217,22 @@ const DebugMapObject &DMO); /// @} - bool hasValidRelocation(uint64_t StartOffset, uint64_t EndOffset, - CompileUnit::DIEInfo &Info); + bool hasValidRelocationAt(uint64_t StartOffset, uint64_t EndOffset, + CompileUnit::DIEInfo &Info) override; bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, - bool IsLittleEndian); - }; + bool IsLittleEndian) override; - /// Keeps track of data associated with one object during linking. - struct LinkContext { - DebugMapObject &DMO; - const object::ObjectFile *ObjectFile; - RelocationManager RelocMgr; - std::unique_ptr DwarfContext; - RangesTy Ranges; - UnitListTy CompileUnits; - - LinkContext(const DebugMap &Map, DwarfLinker &Linker, DebugMapObject &DMO) - : DMO(DMO), RelocMgr(Linker) { - // Swift ASTs are not object files. - if (DMO.getType() == MachO::N_AST) { - ObjectFile = nullptr; - return; - } - auto ErrOrObj = Linker.loadObject(DMO, Map); - ObjectFile = ErrOrObj ? &*ErrOrObj : nullptr; - DwarfContext = ObjectFile ? DWARFContext::create(*ObjectFile) : nullptr; - } + RangesTy &getValidAddressRanges() override { return AddressRanges; } - /// Clear part of the context that's no longer needed when we're done with - /// the debug object. - void Clear() { - DwarfContext.reset(nullptr); - CompileUnits.clear(); - Ranges.clear(); + void clear() override { + AddressRanges.clear(); + ValidRelocs.clear(); + NextValidReloc = 0; } }; - /// 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. - void lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, - const 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(DWARFDie CUDie, const DWARFUnit &Unit, - DebugMap &ModuleMap, const DebugMapObject &DMO, - RangesTy &Ranges, - OffsetsStringPool &OffsetsStringPool, - UniquingStringPool &UniquingStringPoolStringPool, - DeclContextTree &ODRContexts, - uint64_t ModulesEndOffset, unsigned &UnitID, - bool IsLittleEndian, unsigned Indent = 0, - bool Quiet = false); - - /// Recursively add the debug info in this clang module .pcm - /// file (and all the modules imported by it in a bottom-up fashion) - /// to Units. - Error loadClangModule(DWARFDie CUDie, StringRef FilePath, - StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, const DebugMapObject &DMO, - RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, - UniquingStringPool &UniquingStringPool, - DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, - unsigned &UnitID, bool IsLittleEndian, - unsigned Indent = 0, bool Quiet = false); - - /// 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, - const 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); - - /// Check if a variable describing DIE should be kept. - /// \returns updated TraversalFlags. - 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, - bool IsLittleEndian, DIE *Die = nullptr); - - /// Construct the output DIE tree by cloning the DIEs we - /// chose to keep above. If there are no valid relocs, then there's - /// nothing to clone/emit. - void cloneAllCompileUnits(DWARFContext &DwarfContext, - const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool, - bool IsLittleEndian); - - private: - using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; - - /// Information gathered and exchanged between the various - /// clone*Attributes helpers about the attributes of a particular DIE. - struct AttributesInfo { - /// Names. - DwarfStringPoolEntryRef Name, MangledName, NameWithoutTemplate; - - /// Offsets in the string pool. - uint32_t NameOffset = 0; - uint32_t MangledNameOffset = 0; - - /// Value of AT_low_pc in the input DIE - uint64_t OrigLowPc = std::numeric_limits::max(); - - /// Value of AT_high_pc in the input DIE - uint64_t OrigHighPc = 0; - - /// 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, bool IsLittleEndian); - - /// 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 a DWARF expression that may be referencing another DIE. - void cloneExpression(DataExtractor &Data, DWARFExpression Expression, - const DebugMapObject &DMO, CompileUnit &Unit, - SmallVectorImpl &OutputBuffer); - - /// Clone an attribute referencing another DIE and add - /// it to \p Die. - /// \returns the size of the new attribute. - unsigned cloneBlockAttribute(DIE &Die, const DebugMapObject &DMO, - CompileUnit &Unit, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize, - bool IsLittleEndian); - - /// 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); - void emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit); - void emitAppleAcceleratorEntriesForUnit(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; - /// @} - +private: /// \defgroup Helpers Various helper methods. /// /// @{ @@ -460,39 +240,16 @@ /// Attempt to load a debug object from disk. ErrorOr loadObject(const DebugMapObject &Obj, - const DebugMap &Map); - /// @} + const Triple &triple); + ErrorOr loadObjectForOptimizer(const DebugMapObject &Obj, + const DebugMap &DebugMap, + remarks::RemarkLinker &RL); raw_fd_ostream &OutFile; BinaryHolder &BinHolder; LinkOptions Options; std::unique_ptr Streamer; - uint64_t OutputDebugInfoSize; - - unsigned MaxDwarfVersion = 0; - unsigned MinDwarfVersion = std::numeric_limits::max(); - - bool AtLeastOneAppleAccelTable = false; - bool AtLeastOneDwarfAccelTable = false; - - /// 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 DebugNames; - AccelTable AppleNames; - AccelTable AppleNamespaces; - AccelTable AppleObjc; - AccelTable AppleTypes; - - /// Mapping the PCM filename to the DwoId. - StringMap ClangModules; + std::vector> OptimizedObjects; /// A list of all .swiftinterface files referenced by the debug /// info, mapping Module name to path on disk. The entries need to Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -9,10 +9,8 @@ #include "DwarfLinker.h" #include "BinaryHolder.h" #include "DebugMap.h" -#include "DeclContext.h" #include "DwarfStreamer.h" #include "MachOUtils.h" -#include "NonRelocatableStringpool.h" #include "dsymutil.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" @@ -63,8 +61,6 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" -#include "llvm/Remarks/RemarkFormat.h" -#include "llvm/Remarks/RemarkLinker.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" @@ -105,124 +101,47 @@ namespace llvm { namespace dsymutil { -/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our -/// CompileUnit object instead. -static CompileUnit *getUnitForOffset(const UnitListTy &Units, uint64_t Offset) { - auto CU = std::upper_bound( - Units.begin(), Units.end(), Offset, - [](uint64_t LHS, const std::unique_ptr &RHS) { - return LHS < RHS->getOrigUnit().getNextUnitOffset(); - }); - return CU != Units.end() ? CU->get() : nullptr; -} +static Error copySwiftInterfaces( + const std::map &ParseableSwiftInterfaces, + StringRef Architecture, const LinkOptions &Options) { + std::error_code EC; + SmallString<128> InputPath; + SmallString<128> Path; + sys::path::append(Path, *Options.ResourceDir, "Swift", Architecture); + if ((EC = sys::fs::create_directories(Path.str(), true, + sys::fs::perms::all_all))) + return make_error( + "cannot create directory: " + toString(errorCodeToError(EC)), EC); + unsigned BaseLength = Path.size(); -/// Resolve the DIE attribute reference that has been extracted in \p RefValue. -/// The resulting DIE might be in another CompileUnit which is stored into \p -/// ReferencedCU. \returns null if resolving fails for any reason. -static DWARFDie resolveDIEReference(const DwarfLinker &Linker, - const DebugMapObject &DMO, - const UnitListTy &Units, - const DWARFFormValue &RefValue, - const DWARFDie &DIE, CompileUnit *&RefCU) { - assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); - uint64_t RefOffset = *RefValue.getAsReference(); - if ((RefCU = getUnitForOffset(Units, RefOffset))) - if (const auto RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) { - // In a file with broken references, an attribute might point to a NULL - // DIE. - if (!RefDie.isNULL()) - return RefDie; + for (auto &I : ParseableSwiftInterfaces) { + StringRef ModuleName = I.first; + StringRef InterfaceFile = I.second; + if (!Options.PrependPath.empty()) { + InputPath.clear(); + sys::path::append(InputPath, Options.PrependPath, InterfaceFile); + InterfaceFile = InputPath; } + sys::path::append(Path, ModuleName); + Path.append(".swiftinterface"); + if (Options.Verbose) + outs() << "copy parseable Swift interface " << InterfaceFile << " -> " + << Path.str() << '\n'; - Linker.reportWarning("could not find referenced DIE", DMO, &DIE); - return DWARFDie(); -} - -/// \returns whether the passed \a Attr type might contain a DIE reference -/// suitable for ODR uniquing. -static bool isODRAttribute(uint16_t Attr) { - switch (Attr) { - default: - return false; - case dwarf::DW_AT_type: - case dwarf::DW_AT_containing_type: - case dwarf::DW_AT_specification: - case dwarf::DW_AT_abstract_origin: - case dwarf::DW_AT_import: - return true; - } - llvm_unreachable("Improper attribute."); -} - -static bool isTypeTag(uint16_t Tag) { - switch (Tag) { - case dwarf::DW_TAG_array_type: - case dwarf::DW_TAG_class_type: - case dwarf::DW_TAG_enumeration_type: - case dwarf::DW_TAG_pointer_type: - case dwarf::DW_TAG_reference_type: - case dwarf::DW_TAG_string_type: - case dwarf::DW_TAG_structure_type: - case dwarf::DW_TAG_subroutine_type: - case dwarf::DW_TAG_typedef: - case dwarf::DW_TAG_union_type: - case dwarf::DW_TAG_ptr_to_member_type: - case dwarf::DW_TAG_set_type: - case dwarf::DW_TAG_subrange_type: - case dwarf::DW_TAG_base_type: - case dwarf::DW_TAG_const_type: - case dwarf::DW_TAG_constant: - case dwarf::DW_TAG_file_type: - case dwarf::DW_TAG_namelist: - case dwarf::DW_TAG_packed_type: - case dwarf::DW_TAG_volatile_type: - case dwarf::DW_TAG_restrict_type: - case dwarf::DW_TAG_atomic_type: - case dwarf::DW_TAG_interface_type: - case dwarf::DW_TAG_unspecified_type: - case dwarf::DW_TAG_shared_type: - return true; - default: - break; - } - return false; -} - -bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die, - AttributesInfo &Info, - OffsetsStringPool &StringPool, - bool StripTemplate) { - // This function will be called on DIEs having low_pcs and - // ranges. As getting the name might be more expansive, filter out - // blocks directly. - if (Die.getTag() == dwarf::DW_TAG_lexical_block) - return false; - - // FIXME: a bit wasteful as the first getName might return the - // short name. - if (!Info.MangledName) - if (const char *MangledName = Die.getName(DINameKind::LinkageName)) - Info.MangledName = StringPool.getEntry(MangledName); - - if (!Info.Name) - if (const char *Name = Die.getName(DINameKind::ShortName)) - Info.Name = StringPool.getEntry(Name); - - if (StripTemplate && Info.Name && Info.MangledName != Info.Name) { - // FIXME: dsymutil compatibility. This is wrong for operator< - auto Split = Info.Name.getString().split('<'); - if (!Split.second.empty()) - Info.NameWithoutTemplate = StringPool.getEntry(Split.first); + // copy_file attempts an APFS clone first, so this should be cheap. + if ((EC = sys::fs::copy_file(InterfaceFile, Path.str()))) + warn(Twine("cannot copy parseable Swift interface ") + InterfaceFile + + ": " + toString(errorCodeToError(EC))); + Path.resize(BaseLength); } - - return Info.Name || Info.MangledName; + return Error::success(); } /// Report a warning to the user, optionally including information about a /// specific \p DIE related to the warning. -void DwarfLinker::reportWarning(const Twine &Warning, const DebugMapObject &DMO, +void DwarfLinker::reportWarning(const Twine &Warning, StringRef Context, const DWARFDie *DIE) const { - StringRef Context = DMO.getObjectFilename(); + warn(Warning, Context); if (!Options.Verbose || !DIE) @@ -245,174 +164,386 @@ return Streamer->init(TheTriple); } -/// Resolve the relative path to a build artifact referenced by DWARF by -/// applying DW_AT_comp_dir. -static void resolveRelativeObjectPath(SmallVectorImpl &Buf, DWARFDie CU) { - sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), "")); -} +ErrorOr +DwarfLinker::loadObject(const DebugMapObject &Obj, const Triple &triple) { + auto ObjectEntry = + BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp()); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + reportWarning(Twine(Obj.getObjectFilename()) + ": " + + toString(std::move(Err)), + Obj.getObjectFilename()); + return errorToErrorCode(std::move(Err)); + } -/// Collect references to parseable Swift interfaces in imported -/// DW_TAG_module blocks. -static void analyzeImportedModule( - const DWARFDie &DIE, CompileUnit &CU, - std::map &ParseableSwiftInterfaces, - std::function ReportWarning) { - if (CU.getLanguage() != dwarf::DW_LANG_Swift) - return; + auto Object = ObjectEntry->getObject(triple); + if (!Object) { + auto Err = Object.takeError(); + reportWarning(Twine(Obj.getObjectFilename()) + ": " + + toString(std::move(Err)), + Obj.getObjectFilename()); + return errorToErrorCode(std::move(Err)); + } - StringRef Path = dwarf::toStringRef(DIE.find(dwarf::DW_AT_LLVM_include_path)); - if (!Path.endswith(".swiftinterface")) - return; - if (Optional Val = DIE.find(dwarf::DW_AT_name)) - if (Optional Name = Val->getAsCString()) { - auto &Entry = ParseableSwiftInterfaces[*Name]; - // The prepend path is applied later when copying. - DWARFDie CUDie = CU.getOrigUnit().getUnitDIE(); - SmallString<128> ResolvedPath; - if (sys::path::is_relative(Path)) - resolveRelativeObjectPath(ResolvedPath, CUDie); - sys::path::append(ResolvedPath, Path); - if (!Entry.empty() && Entry != ResolvedPath) - ReportWarning( - Twine("Conflicting parseable interfaces for Swift Module ") + - *Name + ": " + Entry + " and " + Path, - DIE); - Entry = ResolvedPath.str(); - } + return *Object; } -/// Recursive helper to build the global DeclContext information and -/// gather the child->parent relationships in the original compile unit. -/// -/// \return true when this DIE and all of its children are only -/// forward declarations to types defined in external clang modules -/// (i.e., forward declarations that are children of a DW_TAG_module). -static bool analyzeContextInfo( - const DWARFDie &DIE, unsigned ParentIdx, CompileUnit &CU, - DeclContext *CurrentDeclContext, UniquingStringPool &StringPool, - DeclContextTree &Contexts, uint64_t ModulesEndOffset, - std::map &ParseableSwiftInterfaces, - std::function ReportWarning, - bool InImportedModule = false) { - unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); - CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx); - - // Clang imposes an ODR on modules(!) regardless of the language: - // "The module-id should consist of only a single identifier, - // which provides the name of the module being defined. Each - // module shall have a single definition." - // - // This does not extend to the types inside the modules: - // "[I]n C, this implies that if two structs are defined in - // different submodules with the same name, those two types are - // distinct types (but may be compatible types if their - // definitions match)." - // - // We treat non-C++ modules like namespaces for this reason. - if (DIE.getTag() == dwarf::DW_TAG_module && ParentIdx == 0 && - dwarf::toString(DIE.find(dwarf::DW_AT_name), "") != - CU.getClangModuleName()) { - InImportedModule = true; - analyzeImportedModule(DIE, CU, ParseableSwiftInterfaces, ReportWarning); +ErrorOr +DwarfLinker::loadObjectForOptimizer(const DebugMapObject &Obj, + const DebugMap &DebugMap, + remarks::RemarkLinker &RL) { + auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple()); + + if (ErrorOrObj) { + + OptimizedObjects.push_back( + std::unique_ptr(new DwarfOptObjFile( + Obj.getObjectFilename(), &*ErrorOrObj, Obj.getWarnings()))); + + OptimizedObjects.back()->Addresses.reset( + new AddressManager(*this, *ErrorOrObj, Obj)); + + // TODO: make remarks generation to be done in parallel with + // optimizer.optimize(); + if (Error E = RL.link(*OptimizedObjects.back()->ObjFile)) + return errorToErrorCode(std::move(E)); + + return *OptimizedObjects.back(); } - Info.ParentIdx = ParentIdx; - bool InClangModule = CU.isClangModule() || InImportedModule; - if (CU.hasODR() || InClangModule) { - if (CurrentDeclContext) { - auto PtrInvalidPair = Contexts.getChildDeclContext( - *CurrentDeclContext, DIE, CU, StringPool, InClangModule); - CurrentDeclContext = PtrInvalidPair.getPointer(); - Info.Ctxt = - PtrInvalidPair.getInt() ? nullptr : PtrInvalidPair.getPointer(); - if (Info.Ctxt) - Info.Ctxt->setDefinedInClangModule(InClangModule); - } else - Info.Ctxt = CurrentDeclContext = nullptr; + return ErrorOrObj.getError(); +} + +static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath, + StringRef ArchName, const remarks::RemarkLinker &RL) { + // Make sure we don't create the directories and the file if there is nothing + // to serialize. + if (RL.empty()) + return Error::success(); + + SmallString<128> InputPath; + SmallString<128> Path; + // Create the "Remarks" directory in the "Resources" directory. + sys::path::append(Path, *Options.ResourceDir, "Remarks"); + if (std::error_code EC = sys::fs::create_directories(Path.str(), true, + sys::fs::perms::all_all)) + return errorCodeToError(EC); + + // Append the file name. + // For fat binaries, also append a dash and the architecture name. + sys::path::append(Path, sys::path::filename(BinaryPath)); + if (Options.NumDebugMaps > 1) { + // More than one debug map means we have a fat binary. + Path += '-'; + Path += ArchName; } - Info.Prune = InImportedModule; - if (DIE.hasChildren()) - for (auto Child : DIE.children()) - Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, - StringPool, Contexts, ModulesEndOffset, - ParseableSwiftInterfaces, ReportWarning, - InImportedModule); - - // Prune this DIE if it is either a forward declaration inside a - // DW_TAG_module or a DW_TAG_module that contains nothing but - // forward declarations. - Info.Prune &= (DIE.getTag() == dwarf::DW_TAG_module) || - (isTypeTag(DIE.getTag()) && - dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0)); - - // Only prune forward declarations inside a DW_TAG_module for which a - // definition exists elsewhere. - if (ModulesEndOffset == 0) - Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); - else - Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() > 0 && - Info.Ctxt->getCanonicalDIEOffset() <= ModulesEndOffset; + std::error_code EC; + raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, sys::fs::OF_None); + if (EC) + return errorCodeToError(EC); - return Info.Prune; -} // namespace dsymutil + if (Error E = RL.serialize(OS, Options.RemarksFormat)) + return E; -static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { - switch (Tag) { - default: + return Error::success(); +} + +bool DwarfLinker::link(const DebugMap &Map) { + if (!createStreamer(Map.getTriple(), OutFile)) return false; - case dwarf::DW_TAG_class_type: - case dwarf::DW_TAG_common_block: - case dwarf::DW_TAG_lexical_block: - case dwarf::DW_TAG_structure_type: - case dwarf::DW_TAG_subprogram: - case dwarf::DW_TAG_subroutine_type: - case dwarf::DW_TAG_union_type: + + OptimizedObjects.clear(); + + DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); + + DwarfOptimizer optimizer(Map.getTriple(), *this, + DwarfOptimizerClient::Dsymutil); + + remarks::RemarkLinker RL; + if (!Options.RemarksPrependPath.empty()) + RL.setExternalFilePrependPath(Options.RemarksPrependPath); + + std::function TranslationLambda = [&](StringRef Input) { + assert(Options.Translator); + return Options.Translator(Input); + }; + + optimizer.setVerbosity(Options.Verbose); + optimizer.setNoOutput(Options.NoOutput); + optimizer.setNoODR(Options.NoODR); + optimizer.setUpdate(Options.Update); + optimizer.setNumThreads(Options.Threads); + optimizer.setAccelTableKind(Options.TheAccelTableKind); + optimizer.setPrependPath(Options.PrependPath); + if (Options.Translator) + optimizer.setStringsTranslator(TranslationLambda); + optimizer.setWarningHandler( + [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { + reportWarning(Warning, Context, DIE); + }); + optimizer.setErrorHandler( + [&](const Twine &Error, StringRef Context, const DWARFDie *DIE) { + error(Error, Context); + }); + optimizer.setObjFileLoader( + [&DebugMap, &RL, this](StringRef ContainerName, + StringRef Path) -> ErrorOr { + auto &Obj = DebugMap.addDebugMapObject( + Path, sys::TimePoint(), MachO::N_OSO); + + if (auto ErrorOrObj = loadObjectForOptimizer(Obj, DebugMap, RL)) { + return *ErrorOrObj; + } else { + // Try and emit more helpful warnings by applying some heuristics. + StringRef ObjFile = ContainerName; + bool isClangModule = sys::path::extension(Path).equals(".pcm"); + bool isArchive = ObjFile.endswith(")"); + + if (isClangModule) { + StringRef ModuleCacheDir = sys::path::parent_path(Path); + if (sys::fs::exists(ModuleCacheDir)) { + // If the module's parent directory exists, we assume that the + // module cache has expired and was pruned by clang. A more + // adventurous dsymutil would invoke clang to rebuild the module + // now. + if (!ModuleCacheHintDisplayed) { + WithColor::note() + << "The clang module cache may have expired since " + "this object file was built. Rebuilding the " + "object file will rebuild the module cache.\n"; + ModuleCacheHintDisplayed = true; + } + } else if (isArchive) { + // If the module cache directory doesn't exist at all and the + // object file is inside a static library, we assume that the + // static library was built on a different machine. We don't want + // to discourage module debugging for convenience libraries within + // a project though. + if (!ArchiveHintDisplayed) { + WithColor::note() + << "Linking a static library that was built with " + "-gmodules, but the module cache was not found. " + "Redistributable static libraries should never be " + "built with module debugging enabled. The debug " + "experience will be degraded due to incomplete " + "debug information.\n"; + ArchiveHintDisplayed = true; + } + } + } + + return ErrorOrObj.getError(); + } + + llvm_unreachable("Unhandled DebugMap object"); + }); + optimizer.setSwiftInterfacesMap(&ParseableSwiftInterfaces); + + // First populate the data structure we need for each iteration of the + // parallel loop. + for (const auto &Obj : Map.objects()) { + + // N_AST objects (swiftmodule files) should get dumped directly into the + // appropriate DWARF section. + if (Obj->getType() == MachO::N_AST) { + if (Options.Verbose) + outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; + + StringRef File = Obj->getObjectFilename(); + auto ErrorOrMem = MemoryBuffer::getFile(File); + if (!ErrorOrMem) { + warn("Could not open '" + File + "'\n"); + continue; + } + sys::fs::file_status Stat; + if (auto Err = sys::fs::status(File, Stat)) { + warn(Err.message()); + continue; + } + if (!Options.NoTimestamp) { + // The modification can have sub-second precision so we need to cast + // away the extra precision that's not present in the debug map. + auto ModificationTime = + std::chrono::time_point_cast( + Stat.getLastModificationTime()); + if (ModificationTime != Obj->getTimestamp()) { + // Not using the helper here as we can easily stream TimePoint<>. + WithColor::warning() << "Timestamp mismatch for " << File << ": " + << Stat.getLastModificationTime() << " and " + << sys::TimePoint<>(Obj->getTimestamp()) << "\n"; + continue; + } + } + + // Copy the module into the .swift_ast section. + if (!Options.NoOutput) + Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer()); + + continue; + } + + if (auto ErrorOrObj = loadObjectForOptimizer(*Obj, Map, RL)) + optimizer.addObjectFile(*ErrorOrObj); + else { + OptimizedObjects.push_back( + std::unique_ptr(new DwarfOptObjFile( + Obj->getObjectFilename(), nullptr, Obj->getWarnings()))); + optimizer.addObjectFile(*OptimizedObjects.back()); + } + } + + optimizer.optimize(); + + StringRef ArchName = Map.getTriple().getArchName(); + if (Error E = emitRemarks(Options, Map.getBinaryPath(), ArchName, RL)) + return error(toString(std::move(E))); + + if (Options.NoOutput) return true; + + if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { + StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); + if (auto E = + copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options)) + return error(toString(std::move(E))); } - llvm_unreachable("Invalid Tag"); + + return Streamer->finish(Map, Options.Translator); } -void DwarfLinker::startDebugObject(LinkContext &Context) { - // Iterate over the debug map entries and put all the ones that are - // functions (because they have a size) into the Ranges map. This map is - // very similar to the FunctionRanges that are stored in each unit, with 2 - // notable differences: - // - // 1. Obviously this one is global, while the other ones are per-unit. - // - // 2. This one contains not only the functions described in the DIE - // tree, but also the ones that are only in the debug map. - // - // The latter information is required to reproduce dsymutil's logic while - // linking line tables. The cases where this information matters look like - // bugs that need to be investigated, but for now we need to reproduce - // dsymutil's behavior. - // FIXME: Once we understood exactly if that information is needed, - // maybe totally remove this (or try to use it to do a real - // -gline-tables-only on Darwin. - for (const auto &Entry : Context.DMO.symbols()) { - const auto &Mapping = Entry.getValue(); - if (Mapping.Size && Mapping.ObjectAddress) - Context.Ranges[*Mapping.ObjectAddress] = DebugMapObjectRange( - *Mapping.ObjectAddress + Mapping.Size, - int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); - } +void DwarfLinker::emitPaperTrailWarnings(const Triple &triple, DIE &die) { + Streamer->emitPaperTrailWarnings(triple, die); +} + +void DwarfLinker::emitSectionContents(const object::ObjectFile &Obj, + StringRef SecName) { + Streamer->emitInvariantSection(Obj, SecName); +} + +/// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. +void DwarfLinker::emitAbbrevs( + const std::vector> &Abbrevs, + unsigned DwarfVersion) { + Streamer->emitAbbrevs(Abbrevs, DwarfVersion); +} + +/// Emit the string table described by \p Pool. +void DwarfLinker::emitStrings(const NonRelocatableStringpool &Pool) { + Streamer->emitStrings(Pool); +} + +/// Emit DWARF debug names. +void DwarfLinker::emitDebugNames( + AccelTable &Table) { + Streamer->emitDebugNames(Table); +} + +/// Emit Apple namespaces accelerator table. +void DwarfLinker::emitAppleNamespaces( + AccelTable &Table) { + Streamer->emitAppleNamespaces(Table); +} + +/// Emit Apple names accelerator table. +void DwarfLinker::emitAppleNames( + AccelTable &Table) { + Streamer->emitAppleNames(Table); +} + +/// Emit Apple Objective-C accelerator table. +void DwarfLinker::emitAppleObjc( + AccelTable &Table) { + Streamer->emitAppleObjc(Table); } -void DwarfLinker::endDebugObject(LinkContext &Context) { - Context.Clear(); +/// Emit Apple type accelerator table. +void DwarfLinker::emitAppleTypes( + AccelTable &Table) { + Streamer->emitAppleTypes(Table); +} + +void DwarfLinker::emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector &Entries, + unsigned AddressSize) { + Streamer->emitRangesEntries(UnitPcOffset, OrigLowPc, FuncRange, Entries, + AddressSize); +} + +uint64_t DwarfLinker::getLineSectionSize() const { + return (Streamer == nullptr) ? 0 : Streamer->getLineSectionSize(); +} + +uint64_t DwarfLinker::getFrameSectionSize() const { + return (Streamer == nullptr) ? 0 : Streamer->getFrameSectionSize(); +} + +uint64_t DwarfLinker::getRangesSectionSize() const { + return (Streamer == nullptr) ? 0 : Streamer->getRangesSectionSize(); +} + +uint64_t DwarfLinker::getDebugInfoSectionSize() const { + return (Streamer == nullptr) ? 0 : Streamer->getDebugInfoSectionSize(); +} + +void DwarfLinker::emitUnitRangesEntries(CompileUnit &Unit, + bool DoRangesSection) { + Streamer->emitUnitRangesEntries(Unit, DoRangesSection); +} + +void DwarfLinker::translateLineTable(DataExtractor LineData, uint64_t Offset) { + Streamer->translateLineTable(LineData, Offset); +} + +void DwarfLinker::emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, + unsigned MinInstLength, + std::vector &Rows, + unsigned AdddressSize) { + Streamer->emitLineTableForUnit(Params, PrologueBytes, MinInstLength, Rows, + AdddressSize); +} + +/// Emit the .debug_pubnames contribution for \p Unit. +void DwarfLinker::emitPubNamesForUnit(const CompileUnit &Unit) { + if (Options.Minimize) + return; + + Streamer->emitPubNamesForUnit(Unit); +} + +/// Emit the .debug_pubtypes contribution for \p Unit. +void DwarfLinker::emitPubTypesForUnit(const CompileUnit &Unit) { + if (Options.Minimize) + return; + + Streamer->emitPubTypesForUnit(Unit); +} + +/// Emit a CIE. +void DwarfLinker::emitCIE(StringRef CIEBytes) { Streamer->emitCIE(CIEBytes); } + +/// Emit an FDE with data \p Bytes. +void DwarfLinker::emitFDE(uint32_t CIEOffset, uint32_t AddreSize, + uint32_t Address, StringRef Bytes) { + Streamer->emitFDE(CIEOffset, AddreSize, Address, Bytes); +} - for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) - (*I)->~DIEBlock(); - for (auto I = DIELocs.begin(), E = DIELocs.end(); I != E; ++I) - (*I)->~DIELoc(); +void DwarfLinker::emitLocationsForUnit( + const CompileUnit &Unit, DWARFContext &Dwarf, + std::function &)> ProcessExpr) { + Streamer->emitLocationsForUnit(Unit, Dwarf, ProcessExpr); +} - DIEBlocks.clear(); - DIELocs.clear(); - DIEAlloc.Reset(); +void DwarfLinker::emitCompileUnitHeader(CompileUnit &Unit) { + Streamer->emitCompileUnitHeader(Unit); } +/// Recursively emit the DIE tree rooted at \p Die. +void DwarfLinker::emitDIE(DIE &Die) { Streamer->emitDIE(Die); } + static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) { switch (Arch) { case Triple::x86: @@ -436,13 +567,13 @@ /// Iterate over the relocations of the given \p Section and /// store the ones that correspond to debug map entries into the /// ValidRelocs array. -void DwarfLinker::RelocationManager::findValidRelocsMachO( +void DwarfLinker::AddressManager::findValidRelocsMachO( const object::SectionRef &Section, const object::MachOObjectFile &Obj, const DebugMapObject &DMO) { Expected ContentsOrErr = Section.getContents(); if (!ContentsOrErr) { consumeError(ContentsOrErr.takeError()); - Linker.reportWarning("error reading section", DMO); + Linker.reportWarning("error reading section", DMO.getObjectFilename()); return; } DataExtractor Data(*ContentsOrErr, Obj.isLittleEndian(), 0); @@ -461,7 +592,7 @@ Obj.getArch())) { SkipNext = true; Linker.reportWarning("unsupported relocation in debug_info section.", - DMO); + DMO.getObjectFilename()); continue; } @@ -469,7 +600,7 @@ uint64_t Offset64 = Reloc.getOffset(); if ((RelocSize != 4 && RelocSize != 8)) { Linker.reportWarning("unsupported relocation in debug_info section.", - DMO); + DMO.getObjectFilename()); continue; } uint64_t OffsetCopy = Offset64; @@ -494,7 +625,8 @@ Expected SymbolName = Sym->getName(); if (!SymbolName) { consumeError(SymbolName.takeError()); - Linker.reportWarning("error getting relocation symbol name.", DMO); + Linker.reportWarning("error getting relocation symbol name.", + DMO.getObjectFilename()); continue; } if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) @@ -510,16 +642,16 @@ /// Dispatch the valid relocation finding logic to the /// appropriate handler depending on the object file format. -bool DwarfLinker::RelocationManager::findValidRelocs( +bool DwarfLinker::AddressManager::findValidRelocs( const object::SectionRef &Section, const object::ObjectFile &Obj, const DebugMapObject &DMO) { // Dispatch to the right handler depending on the file type. if (auto *MachOObj = dyn_cast(&Obj)) findValidRelocsMachO(Section, *MachOObj, DMO); else - Linker.reportWarning( - Twine("unsupported object file type: ") + Obj.getFileName(), DMO); - + Linker.reportWarning(Twine("unsupported object file type: ") + + Obj.getFileName(), + DMO.getObjectFilename()); if (ValidRelocs.empty()) return false; @@ -536,7 +668,7 @@ /// link by indicating which DIEs refer to symbols present in the /// linked binary. /// \returns whether there are any valid relocations in the debug info. -bool DwarfLinker::RelocationManager::findValidRelocsInDebugInfo( +bool DwarfLinker::AddressManager::findValidRelocsInDebugInfo( const object::ObjectFile &Obj, const DebugMapObject &DMO) { // Find the debug_info section. for (const object::SectionRef &Section : Obj.sections()) { @@ -560,7 +692,7 @@ /// This function must be called with offsets in strictly ascending /// order because it never looks back at relocations it already 'went past'. /// \returns true and sets Info.InDebugMap if it is the case. -bool DwarfLinker::RelocationManager::hasValidRelocation( +bool DwarfLinker::AddressManager::hasValidRelocationAt( uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info) { assert(NextValidReloc == 0 || StartOffset > ValidRelocs[NextValidReloc - 1].Offset); @@ -598,814 +730,28 @@ return true; } -/// Get the starting and ending (exclusive) offset for the -/// attribute with index \p Idx descibed by \p Abbrev. \p Offset is -/// supposed to point to the position of the first attribute described -/// by \p Abbrev. -/// \return [StartOffset, EndOffset) as a pair. -static std::pair -getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx, - uint64_t Offset, const DWARFUnit &Unit) { - DataExtractor Data = Unit.getDebugInfoExtractor(); - - for (unsigned i = 0; i < Idx; ++i) - DWARFFormValue::skipValue(Abbrev->getFormByIndex(i), Data, &Offset, - Unit.getFormParams()); - - uint64_t End = Offset; - DWARFFormValue::skipValue(Abbrev->getFormByIndex(Idx), Data, &End, - Unit.getFormParams()); - - return std::make_pair(Offset, End); -} - -/// Check if a variable describing DIE should be kept. -/// \returns updated TraversalFlags. -unsigned DwarfLinker::shouldKeepVariableDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, - CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, - unsigned Flags) { - const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); - - // Global variables with constant value can always be kept. - if (!(Flags & TF_InFunctionScope) && - Abbrev->findAttributeIndex(dwarf::DW_AT_const_value)) { - MyInfo.InDebugMap = true; - return Flags | TF_Keep; - } - - Optional LocationIdx = - Abbrev->findAttributeIndex(dwarf::DW_AT_location); - if (!LocationIdx) - return Flags; - - uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); - const DWARFUnit &OrigUnit = Unit.getOrigUnit(); - uint64_t LocationOffset, LocationEndOffset; - std::tie(LocationOffset, LocationEndOffset) = - getAttributeOffsets(Abbrev, *LocationIdx, Offset, OrigUnit); - - // See if there is a relocation to a valid debug map entry inside - // this variable's location. The order is important here. We want to - // always check if the variable has a valid relocation, so that the - // DIEInfo is filled. However, we don't want a static variable in a - // function to force us to keep the enclosing function. - if (!RelocMgr.hasValidRelocation(LocationOffset, LocationEndOffset, MyInfo) || - (Flags & TF_InFunctionScope)) - return Flags; - - if (Options.Verbose) { - outs() << "Keeping variable DIE:"; - DIDumpOptions DumpOpts; - DumpOpts.ChildRecurseDepth = 0; - DumpOpts.Verbose = Options.Verbose; - DIE.dump(outs(), 8 /* Indent */, DumpOpts); - } - - return Flags | TF_Keep; -} - -/// Check if a function describing DIE should be kept. -/// \returns updated TraversalFlags. -unsigned DwarfLinker::shouldKeepSubprogramDIE( - RelocationManager &RelocMgr, RangesTy &Ranges, const DWARFDie &DIE, - const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, - unsigned Flags) { - const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); - - Flags |= TF_InFunctionScope; - - Optional LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc); - if (!LowPcIdx) - return Flags; - - uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); - DWARFUnit &OrigUnit = Unit.getOrigUnit(); - uint64_t LowPcOffset, LowPcEndOffset; - std::tie(LowPcOffset, LowPcEndOffset) = - getAttributeOffsets(Abbrev, *LowPcIdx, Offset, OrigUnit); - - auto LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); - assert(LowPc.hasValue() && "low_pc attribute is not an address."); - if (!LowPc || - !RelocMgr.hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo)) - return Flags; - - if (Options.Verbose) { - outs() << "Keeping subprogram DIE:"; - DIDumpOptions DumpOpts; - DumpOpts.ChildRecurseDepth = 0; - DumpOpts.Verbose = Options.Verbose; - DIE.dump(outs(), 8 /* Indent */, DumpOpts); - } - - if (DIE.getTag() == dwarf::DW_TAG_label) { - if (Unit.hasLabelAt(*LowPc)) - return Flags; - // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider labels - // that don't fall into the CU's aranges. This is wrong IMO. Debug info - // generation bugs aside, this is really wrong in the case of labels, where - // a label marking the end of a function will have a PC == CU's high_pc. - if (dwarf::toAddress(OrigUnit.getUnitDIE().find(dwarf::DW_AT_high_pc)) - .getValueOr(UINT64_MAX) <= LowPc) - return Flags; - Unit.addLabelLowPc(*LowPc, MyInfo.AddrAdjust); - return Flags | TF_Keep; - } - - Flags |= TF_Keep; - - Optional HighPc = DIE.getHighPC(*LowPc); - if (!HighPc) { - reportWarning("Function without high_pc. Range will be discarded.\n", DMO, - &DIE); - return Flags; - } +/// Apply the valid relocations found by findValidRelocs() to +/// the buffer \p Data, taking into account that Data is at \p BaseOffset +/// in the debug_info section. +/// +/// Like for findValidRelocs(), this function must be called with +/// monotonic \p BaseOffset values. +/// +/// \returns whether any reloc has been applied. +bool DwarfLinker::AddressManager::applyValidRelocs(MutableArrayRef Data, + uint64_t BaseOffset, + bool IsLittleEndian) { + assert(AreRelocationsResolved()); + assert((NextValidReloc == 0 || + BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && + "BaseOffset should only be increasing."); + if (NextValidReloc >= ValidRelocs.size()) + return false; - // Replace the debug map range with a more accurate one. - Ranges[*LowPc] = DebugMapObjectRange(*HighPc, MyInfo.AddrAdjust); - Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); - return Flags; -} - -/// Check if a DIE should be kept. -/// \returns updated TraversalFlags. -unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr, - RangesTy &Ranges, const DWARFDie &DIE, - const DebugMapObject &DMO, - CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, - unsigned Flags) { - switch (DIE.getTag()) { - case dwarf::DW_TAG_constant: - case dwarf::DW_TAG_variable: - return shouldKeepVariableDIE(RelocMgr, DIE, Unit, MyInfo, Flags); - case dwarf::DW_TAG_subprogram: - case dwarf::DW_TAG_label: - return shouldKeepSubprogramDIE(RelocMgr, Ranges, DIE, DMO, Unit, MyInfo, - Flags); - case dwarf::DW_TAG_base_type: - // DWARF Expressions may reference basic types, but scanning them - // is expensive. Basic types are tiny, so just keep all of them. - case dwarf::DW_TAG_imported_module: - case dwarf::DW_TAG_imported_declaration: - case dwarf::DW_TAG_imported_unit: - // We always want to keep these. - return Flags | TF_Keep; - default: - break; - } - - return Flags; -} - -/// Mark the passed DIE as well as all the ones it depends on -/// as kept. -/// -/// This function is called by lookForDIEsToKeep on DIEs that are -/// newly discovered to be needed in the link. It recursively calls -/// back to lookForDIEsToKeep while adding TF_DependencyWalk to the -/// TraversalFlags to inform it that it's not doing the primary DIE -/// tree walk. -void DwarfLinker::keepDIEAndDependencies( - RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, - const DWARFDie &Die, CompileUnit::DIEInfo &MyInfo, - const DebugMapObject &DMO, CompileUnit &CU, bool UseODR) { - DWARFUnit &Unit = CU.getOrigUnit(); - MyInfo.Keep = true; - - // We're looking for incomplete types. - MyInfo.Incomplete = Die.getTag() != dwarf::DW_TAG_subprogram && - Die.getTag() != dwarf::DW_TAG_member && - dwarf::toUnsigned(Die.find(dwarf::DW_AT_declaration), 0); - - // First mark all the parent chain as kept. - unsigned AncestorIdx = MyInfo.ParentIdx; - while (!CU.getInfo(AncestorIdx).Keep) { - unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), - DMO, CU, - TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); - AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; - } - - // Then we need to mark all the DIEs referenced by this DIE's - // attributes as kept. - DWARFDataExtractor Data = Unit.getDebugInfoExtractor(); - const auto *Abbrev = Die.getAbbreviationDeclarationPtr(); - uint64_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode()); - - // Mark all DIEs referenced through attributes as kept. - for (const auto &AttrSpec : Abbrev->attributes()) { - DWARFFormValue Val(AttrSpec.Form); - if (!Val.isFormClass(DWARFFormValue::FC_Reference) || - AttrSpec.Attr == dwarf::DW_AT_sibling) { - DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, - Unit.getFormParams()); - continue; - } - - Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit); - CompileUnit *ReferencedCU; - if (auto RefDie = - resolveDIEReference(*this, DMO, Units, Val, Die, ReferencedCU)) { - uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie); - CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); - bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() && - Info.Ctxt->isDefinedInClangModule(); - // If the referenced DIE has a DeclContext that has already been - // emitted, then do not keep the one in this CU. We'll link to - // the canonical DIE in cloneDieReferenceAttribute. - // FIXME: compatibility with dsymutil-classic. UseODR shouldn't - // be necessary and could be advantageously replaced by - // ReferencedCU->hasODR() && CU.hasODR(). - // FIXME: compatibility with dsymutil-classic. There is no - // reason not to unique ref_addr references. - if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && (UseODR || IsModuleRef) && - Info.Ctxt && - Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt && - Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr)) - continue; - - // Keep a module forward declaration if there is no definition. - if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt && - Info.Ctxt->getCanonicalDIEOffset())) - Info.Prune = false; - - unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, DMO, *ReferencedCU, - TF_Keep | TF_DependencyWalk | ODRFlag); - - // The incomplete property is propagated if the current DIE is complete - // but references an incomplete DIE. - if (Info.Incomplete && !MyInfo.Incomplete && - (Die.getTag() == dwarf::DW_TAG_typedef || - Die.getTag() == dwarf::DW_TAG_member || - Die.getTag() == dwarf::DW_TAG_reference_type || - Die.getTag() == dwarf::DW_TAG_ptr_to_member_type || - Die.getTag() == dwarf::DW_TAG_pointer_type)) - MyInfo.Incomplete = true; - } - } -} - -namespace { -/// This class represents an item in the work list. In addition to it's obvious -/// purpose of representing the state associated with a particular run of the -/// work loop, it also serves as a marker to indicate that we should run the -/// "continuation" code. -/// -/// Originally, the latter was lambda which allowed arbitrary code to be run. -/// Because we always need to run the exact same code, it made more sense to -/// use a boolean and repurpose the already existing DIE field. -struct WorklistItem { - DWARFDie Die; - unsigned Flags; - bool IsContinuation; - CompileUnit::DIEInfo *ChildInfo = nullptr; - - /// Construct a classic worklist item. - WorklistItem(DWARFDie Die, unsigned Flags) - : Die(Die), Flags(Flags), IsContinuation(false){}; - - /// Creates a continuation marker. - WorklistItem(DWARFDie Die) : Die(Die), IsContinuation(true){}; -}; -} // namespace - -// Helper that updates the completeness of the current DIE. It depends on the -// fact that the incompletness of its children is already computed. -static void updateIncompleteness(const DWARFDie &Die, - CompileUnit::DIEInfo &ChildInfo, - CompileUnit &CU) { - // Only propagate incomplete members. - if (Die.getTag() != dwarf::DW_TAG_structure_type && - Die.getTag() != dwarf::DW_TAG_class_type) - return; - - unsigned Idx = CU.getOrigUnit().getDIEIndex(Die); - CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx); - - if (MyInfo.Incomplete) - return; - - if (ChildInfo.Incomplete || ChildInfo.Prune) - MyInfo.Incomplete = true; -} - -/// Recursively walk the \p DIE tree and look for DIEs to -/// keep. Store that information in \p CU's DIEInfo. -/// -/// This function is the entry point of the DIE selection -/// algorithm. It is expected to walk the DIE tree in file order and -/// (though the mediation of its helper) call hasValidRelocation() on -/// each DIE that might be a 'root DIE' (See DwarfLinker class -/// comment). -/// While walking the dependencies of root DIEs, this function is -/// also called, but during these dependency walks the file order is -/// not respected. The TF_DependencyWalk flag tells us which kind of -/// traversal we are currently doing. -/// -/// The return value indicates whether the DIE is incomplete. -void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, - RangesTy &Ranges, const UnitListTy &Units, - const DWARFDie &Die, - const DebugMapObject &DMO, CompileUnit &CU, - unsigned Flags) { - // LIFO work list. - SmallVector Worklist; - Worklist.emplace_back(Die, Flags); - - while (!Worklist.empty()) { - WorklistItem Current = Worklist.back(); - Worklist.pop_back(); - - if (Current.IsContinuation) { - updateIncompleteness(Current.Die, *Current.ChildInfo, CU); - continue; - } - - unsigned Idx = CU.getOrigUnit().getDIEIndex(Current.Die); - CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx); - - // At this point we are guaranteed to have a continuation marker before us - // in the worklist, except for the last DIE. - if (!Worklist.empty()) - Worklist.back().ChildInfo = &MyInfo; - - if (MyInfo.Prune) - continue; - - // If the Keep flag is set, we are marking a required DIE's dependencies. - // If our target is already marked as kept, we're all set. - bool AlreadyKept = MyInfo.Keep; - if ((Current.Flags & TF_DependencyWalk) && AlreadyKept) - continue; - - // We must not call shouldKeepDIE while called from keepDIEAndDependencies, - // because it would screw up the relocation finding logic. - if (!(Current.Flags & TF_DependencyWalk)) - Current.Flags = shouldKeepDIE(RelocMgr, Ranges, Current.Die, DMO, CU, - MyInfo, Current.Flags); - - // If it is a newly kept DIE mark it as well as all its dependencies as - // kept. - if (!AlreadyKept && (Current.Flags & TF_Keep)) { - bool UseOdr = (Current.Flags & TF_DependencyWalk) - ? (Current.Flags & TF_ODR) - : CU.hasODR(); - keepDIEAndDependencies(RelocMgr, Ranges, Units, Current.Die, MyInfo, DMO, - CU, UseOdr); - } - - // The TF_ParentWalk flag tells us that we are currently walking up - // the parent chain of a required DIE, and we don't want to mark all - // the children of the parents as kept (consider for example a - // DW_TAG_namespace node in the parent chain). There are however a - // set of DIE types for which we want to ignore that directive and still - // walk their children. - if (dieNeedsChildrenToBeMeaningful(Current.Die.getTag())) - Current.Flags &= ~TF_ParentWalk; - - if (!Current.Die.hasChildren() || (Current.Flags & TF_ParentWalk)) - continue; - - // Add children in reverse order to the worklist to effectively process - // them in order. - for (auto Child : reverse(Current.Die.children())) { - // Add continuation marker before every child to calculate incompleteness - // after the last child is processed. We can't store this information in - // the same item because we might have to process other continuations - // first. - Worklist.emplace_back(Current.Die); - Worklist.emplace_back(Child, Current.Flags); - } - } -} - -/// Assign an abbreviation number to \p Abbrev. -/// -/// Our DIEs get freed after every DebugMapObject has been processed, -/// thus the FoldingSet we use to unique DIEAbbrevs cannot refer to -/// the instances hold by the DIEs. When we encounter an abbreviation -/// that we don't know, we create a permanent copy of it. -void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) { - // Check the set for priors. - FoldingSetNodeID ID; - Abbrev.Profile(ID); - void *InsertToken; - DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken); - - // If it's newly added. - if (InSet) { - // Assign existing abbreviation number. - Abbrev.setNumber(InSet->getNumber()); - } else { - // Add to abbreviation list. - Abbreviations.push_back( - std::make_unique(Abbrev.getTag(), Abbrev.hasChildren())); - for (const auto &Attr : Abbrev.getData()) - Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm()); - AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken); - // Assign the unique abbreviation number. - Abbrev.setNumber(Abbreviations.size()); - Abbreviations.back()->setNumber(Abbreviations.size()); - } -} - -unsigned DwarfLinker::DIECloner::cloneStringAttribute( - DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, - const DWARFUnit &U, OffsetsStringPool &StringPool, AttributesInfo &Info) { - // Switch everything to out of line strings. - const char *String = *Val.getAsCString(); - auto StringEntry = StringPool.getEntry(String); - - // Update attributes info. - if (AttrSpec.Attr == dwarf::DW_AT_name) - Info.Name = StringEntry; - else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || - AttrSpec.Attr == dwarf::DW_AT_linkage_name) - Info.MangledName = StringEntry; - - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, - DIEInteger(StringEntry.getOffset())); - - return 4; -} - -unsigned DwarfLinker::DIECloner::cloneDieReferenceAttribute( - DIE &Die, const DWARFDie &InputDIE, AttributeSpec AttrSpec, - unsigned AttrSize, const DWARFFormValue &Val, const DebugMapObject &DMO, - CompileUnit &Unit) { - const DWARFUnit &U = Unit.getOrigUnit(); - uint64_t Ref = *Val.getAsReference(); - DIE *NewRefDie = nullptr; - CompileUnit *RefUnit = nullptr; - DeclContext *Ctxt = nullptr; - - DWARFDie RefDie = - resolveDIEReference(Linker, DMO, CompileUnits, Val, InputDIE, RefUnit); - - // If the referenced DIE is not found, drop the attribute. - if (!RefDie || AttrSpec.Attr == dwarf::DW_AT_sibling) - return 0; - - unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie); - CompileUnit::DIEInfo &RefInfo = RefUnit->getInfo(Idx); - - // If we already have emitted an equivalent DeclContext, just point - // at it. - if (isODRAttribute(AttrSpec.Attr)) { - Ctxt = RefInfo.Ctxt; - if (Ctxt && Ctxt->getCanonicalDIEOffset()) { - DIEInteger Attr(Ctxt->getCanonicalDIEOffset()); - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::DW_FORM_ref_addr, Attr); - return U.getRefAddrByteSize(); - } - } - - if (!RefInfo.Clone) { - assert(Ref > InputDIE.getOffset()); - // We haven't cloned this DIE yet. Just create an empty one and - // store it. It'll get really cloned when we process it. - RefInfo.Clone = DIE::get(DIEAlloc, dwarf::Tag(RefDie.getTag())); - } - NewRefDie = RefInfo.Clone; - - if (AttrSpec.Form == dwarf::DW_FORM_ref_addr || - (Unit.hasODR() && isODRAttribute(AttrSpec.Attr))) { - // We cannot currently rely on a DIEEntry to emit ref_addr - // references, because the implementation calls back to DwarfDebug - // to find the unit offset. (We don't have a DwarfDebug) - // FIXME: we should be able to design DIEEntry reliance on - // DwarfDebug away. - uint64_t Attr; - if (Ref < InputDIE.getOffset()) { - // We must have already cloned that DIE. - uint32_t NewRefOffset = - RefUnit->getStartOffset() + NewRefDie->getOffset(); - Attr = NewRefOffset; - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::DW_FORM_ref_addr, DIEInteger(Attr)); - } else { - // A forward reference. Note and fixup later. - Attr = 0xBADDEF; - Unit.noteForwardReference( - NewRefDie, RefUnit, Ctxt, - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::DW_FORM_ref_addr, DIEInteger(Attr))); - } - return U.getRefAddrByteSize(); - } - - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::Form(AttrSpec.Form), DIEEntry(*NewRefDie)); - return AttrSize; -} - -void DwarfLinker::DIECloner::cloneExpression( - DataExtractor &Data, DWARFExpression Expression, const DebugMapObject &DMO, - CompileUnit &Unit, SmallVectorImpl &OutputBuffer) { - using Encoding = DWARFExpression::Operation::Encoding; - - uint64_t OpOffset = 0; - for (auto &Op : Expression) { - auto Description = Op.getDescription(); - // DW_OP_const_type is variable-length and has 3 - // operands. DWARFExpression thus far only supports 2. - auto Op0 = Description.Op[0]; - auto Op1 = Description.Op[1]; - if ((Op0 == Encoding::BaseTypeRef && Op1 != Encoding::SizeNA) || - (Op1 == Encoding::BaseTypeRef && Op0 != Encoding::Size1)) - Linker.reportWarning("Unsupported DW_OP encoding.", DMO); - - if ((Op0 == Encoding::BaseTypeRef && Op1 == Encoding::SizeNA) || - (Op1 == Encoding::BaseTypeRef && Op0 == Encoding::Size1)) { - // This code assumes that the other non-typeref operand fits into 1 byte. - assert(OpOffset < Op.getEndOffset()); - uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1; - assert(ULEBsize <= 16); - - // Copy over the operation. - OutputBuffer.push_back(Op.getCode()); - uint64_t RefOffset; - if (Op1 == Encoding::SizeNA) { - RefOffset = Op.getRawOperand(0); - } else { - OutputBuffer.push_back(Op.getRawOperand(0)); - RefOffset = Op.getRawOperand(1); - } - auto RefDie = Unit.getOrigUnit().getDIEForOffset(RefOffset); - uint32_t RefIdx = Unit.getOrigUnit().getDIEIndex(RefDie); - CompileUnit::DIEInfo &Info = Unit.getInfo(RefIdx); - uint32_t Offset = 0; - if (DIE *Clone = Info.Clone) - Offset = Clone->getOffset(); - else - Linker.reportWarning("base type ref doesn't point to DW_TAG_base_type.", - DMO); - uint8_t ULEB[16]; - unsigned RealSize = encodeULEB128(Offset, ULEB, ULEBsize); - if (RealSize > ULEBsize) { - // Emit the generic type as a fallback. - RealSize = encodeULEB128(0, ULEB, ULEBsize); - Linker.reportWarning("base type ref doesn't fit.", DMO); - } - assert(RealSize == ULEBsize && "padding failed"); - ArrayRef ULEBbytes(ULEB, ULEBsize); - OutputBuffer.append(ULEBbytes.begin(), ULEBbytes.end()); - } else { - // Copy over everything else unmodified. - StringRef Bytes = Data.getData().slice(OpOffset, Op.getEndOffset()); - OutputBuffer.append(Bytes.begin(), Bytes.end()); - } - OpOffset = Op.getEndOffset(); - } -} - -unsigned DwarfLinker::DIECloner::cloneBlockAttribute( - DIE &Die, const DebugMapObject &DMO, CompileUnit &Unit, - AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, - bool IsLittleEndian) { - DIEValueList *Attr; - DIEValue Value; - DIELoc *Loc = nullptr; - DIEBlock *Block = nullptr; - if (AttrSpec.Form == dwarf::DW_FORM_exprloc) { - Loc = new (DIEAlloc) DIELoc; - Linker.DIELocs.push_back(Loc); - } else { - Block = new (DIEAlloc) DIEBlock; - Linker.DIEBlocks.push_back(Block); - } - Attr = Loc ? static_cast(Loc) - : static_cast(Block); - - if (Loc) - Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), - dwarf::Form(AttrSpec.Form), Loc); - else - Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), - dwarf::Form(AttrSpec.Form), Block); - - // If the block is a DWARF Expression, clone it into the temporary - // buffer using cloneExpression(), otherwise copy the data directly. - SmallVector Buffer; - ArrayRef Bytes = *Val.getAsBlock(); - if (DWARFAttribute::mayHaveLocationDescription(AttrSpec.Attr) && - (Val.isFormClass(DWARFFormValue::FC_Block) || - Val.isFormClass(DWARFFormValue::FC_Exprloc))) { - DWARFUnit &OrigUnit = Unit.getOrigUnit(); - DataExtractor Data(StringRef((const char *)Bytes.data(), Bytes.size()), - IsLittleEndian, OrigUnit.getAddressByteSize()); - DWARFExpression Expr(Data, OrigUnit.getVersion(), - OrigUnit.getAddressByteSize()); - cloneExpression(Data, Expr, DMO, Unit, Buffer); - Bytes = Buffer; - } - for (auto Byte : Bytes) - Attr->addValue(DIEAlloc, static_cast(0), - dwarf::DW_FORM_data1, DIEInteger(Byte)); - - // FIXME: If DIEBlock and DIELoc just reuses the Size field of - // the DIE class, this if could be replaced by - // Attr->setSize(Bytes.size()). - if (Linker.Streamer) { - auto *AsmPrinter = &Linker.Streamer->getAsmPrinter(); - if (Loc) - Loc->ComputeSize(AsmPrinter); - else - Block->ComputeSize(AsmPrinter); - } - Die.addValue(DIEAlloc, Value); - return AttrSize; -} - -unsigned DwarfLinker::DIECloner::cloneAddressAttribute( - DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, - const CompileUnit &Unit, AttributesInfo &Info) { - uint64_t Addr = *Val.getAsAddress(); - - if (LLVM_UNLIKELY(Linker.Options.Update)) { - if (AttrSpec.Attr == dwarf::DW_AT_low_pc) - Info.HasLowPc = true; - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::Form(AttrSpec.Form), DIEInteger(Addr)); - return Unit.getOrigUnit().getAddressByteSize(); - } - - if (AttrSpec.Attr == dwarf::DW_AT_low_pc) { - if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine || - Die.getTag() == dwarf::DW_TAG_lexical_block) - // The low_pc of a block or inline subroutine might get - // relocated because it happens to match the low_pc of the - // enclosing subprogram. To prevent issues with that, always use - // the low_pc from the input DIE if relocations have been applied. - Addr = (Info.OrigLowPc != std::numeric_limits::max() - ? Info.OrigLowPc - : Addr) + - Info.PCOffset; - else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { - Addr = Unit.getLowPc(); - if (Addr == std::numeric_limits::max()) - return 0; - } - Info.HasLowPc = true; - } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc) { - if (Die.getTag() == dwarf::DW_TAG_compile_unit) { - if (uint64_t HighPc = Unit.getHighPc()) - Addr = HighPc; - else - return 0; - } else - // If we have a high_pc recorded for the input DIE, use - // it. Otherwise (when no relocations where applied) just use the - // one we just decoded. - Addr = (Info.OrigHighPc ? Info.OrigHighPc : Addr) + Info.PCOffset; - } - - Die.addValue(DIEAlloc, static_cast(AttrSpec.Attr), - static_cast(AttrSpec.Form), DIEInteger(Addr)); - return Unit.getOrigUnit().getAddressByteSize(); -} - -unsigned DwarfLinker::DIECloner::cloneScalarAttribute( - DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, - CompileUnit &Unit, AttributeSpec AttrSpec, const DWARFFormValue &Val, - unsigned AttrSize, AttributesInfo &Info) { - uint64_t Value; - - if (LLVM_UNLIKELY(Linker.Options.Update)) { - if (auto OptionalValue = Val.getAsUnsignedConstant()) - Value = *OptionalValue; - else if (auto OptionalValue = Val.getAsSignedConstant()) - Value = *OptionalValue; - else if (auto OptionalValue = Val.getAsSectionOffset()) - Value = *OptionalValue; - else { - Linker.reportWarning( - "Unsupported scalar attribute form. Dropping attribute.", DMO, - &InputDIE); - return 0; - } - if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) - Info.IsDeclaration = true; - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::Form(AttrSpec.Form), DIEInteger(Value)); - return AttrSize; - } - - if (AttrSpec.Attr == dwarf::DW_AT_high_pc && - Die.getTag() == dwarf::DW_TAG_compile_unit) { - if (Unit.getLowPc() == -1ULL) - return 0; - // Dwarf >= 4 high_pc is an size, not an address. - Value = Unit.getHighPc() - Unit.getLowPc(); - } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) - Value = *Val.getAsSectionOffset(); - else if (AttrSpec.Form == dwarf::DW_FORM_sdata) - Value = *Val.getAsSignedConstant(); - else if (auto OptionalValue = Val.getAsUnsignedConstant()) - Value = *OptionalValue; - else { - Linker.reportWarning( - "Unsupported scalar attribute form. Dropping attribute.", DMO, - &InputDIE); - return 0; - } - PatchLocation Patch = - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::Form(AttrSpec.Form), DIEInteger(Value)); - if (AttrSpec.Attr == dwarf::DW_AT_ranges) { - Unit.noteRangeAttribute(Die, Patch); - Info.HasRanges = true; - } - - // A more generic way to check for location attributes would be - // nice, but it's very unlikely that any other attribute needs a - // location list. - // FIXME: use DWARFAttribute::mayHaveLocationDescription(). - else if (AttrSpec.Attr == dwarf::DW_AT_location || - AttrSpec.Attr == dwarf::DW_AT_frame_base) - Unit.noteLocationAttribute(Patch, Info.PCOffset); - else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) - Info.IsDeclaration = true; - - return AttrSize; -} - -/// Clone \p InputDIE's attribute described by \p AttrSpec with -/// value \p Val, and add it to \p Die. -/// \returns the size of the cloned attribute. -unsigned DwarfLinker::DIECloner::cloneAttribute( - DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, - CompileUnit &Unit, OffsetsStringPool &StringPool, const DWARFFormValue &Val, - const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info, - bool IsLittleEndian) { - const DWARFUnit &U = Unit.getOrigUnit(); - - switch (AttrSpec.Form) { - case dwarf::DW_FORM_strp: - case dwarf::DW_FORM_string: - return cloneStringAttribute(Die, AttrSpec, Val, U, StringPool, Info); - case dwarf::DW_FORM_ref_addr: - case dwarf::DW_FORM_ref1: - case dwarf::DW_FORM_ref2: - case dwarf::DW_FORM_ref4: - case dwarf::DW_FORM_ref8: - return cloneDieReferenceAttribute(Die, InputDIE, AttrSpec, AttrSize, Val, - DMO, Unit); - case dwarf::DW_FORM_block: - case dwarf::DW_FORM_block1: - case dwarf::DW_FORM_block2: - case dwarf::DW_FORM_block4: - case dwarf::DW_FORM_exprloc: - return cloneBlockAttribute(Die, DMO, Unit, AttrSpec, Val, AttrSize, - IsLittleEndian); - case dwarf::DW_FORM_addr: - return cloneAddressAttribute(Die, AttrSpec, Val, Unit, Info); - case dwarf::DW_FORM_data1: - case dwarf::DW_FORM_data2: - case dwarf::DW_FORM_data4: - case dwarf::DW_FORM_data8: - case dwarf::DW_FORM_udata: - case dwarf::DW_FORM_sdata: - case dwarf::DW_FORM_sec_offset: - case dwarf::DW_FORM_flag: - case dwarf::DW_FORM_flag_present: - return cloneScalarAttribute(Die, InputDIE, DMO, Unit, AttrSpec, Val, - AttrSize, Info); - default: - Linker.reportWarning( - "Unsupported attribute form in cloneAttribute. Dropping.", DMO, - &InputDIE); - } - - return 0; -} - -/// Apply the valid relocations found by findValidRelocs() to -/// the buffer \p Data, taking into account that Data is at \p BaseOffset -/// in the debug_info section. -/// -/// Like for findValidRelocs(), this function must be called with -/// monotonic \p BaseOffset values. -/// -/// \returns whether any reloc has been applied. -bool DwarfLinker::RelocationManager::applyValidRelocs( - MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) { - assert((NextValidReloc == 0 || - BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && - "BaseOffset should only be increasing."); - if (NextValidReloc >= ValidRelocs.size()) - return false; - - // Skip relocs that haven't been applied. - while (NextValidReloc < ValidRelocs.size() && - ValidRelocs[NextValidReloc].Offset < BaseOffset) - ++NextValidReloc; + // Skip relocs that haven't been applied. + while (NextValidReloc < ValidRelocs.size() && + ValidRelocs[NextValidReloc].Offset < BaseOffset) + ++NextValidReloc; bool Applied = false; uint64_t EndOffset = BaseOffset + Data.size(); @@ -1430,1519 +776,6 @@ return Applied; } -static bool isObjCSelector(StringRef Name) { - return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') && - (Name[1] == '['); -} - -void DwarfLinker::DIECloner::addObjCAccelerator(CompileUnit &Unit, - const DIE *Die, - DwarfStringPoolEntryRef Name, - OffsetsStringPool &StringPool, - bool SkipPubSection) { - assert(isObjCSelector(Name.getString()) && "not an objc selector"); - // Objective C method or class function. - // "- [Class(Category) selector :withArg ...]" - StringRef ClassNameStart(Name.getString().drop_front(2)); - size_t FirstSpace = ClassNameStart.find(' '); - if (FirstSpace == StringRef::npos) - return; - - StringRef SelectorStart(ClassNameStart.data() + FirstSpace + 1); - if (!SelectorStart.size()) - return; - - StringRef Selector(SelectorStart.data(), SelectorStart.size() - 1); - Unit.addNameAccelerator(Die, StringPool.getEntry(Selector), SkipPubSection); - - // Add an entry for the class name that points to this - // method/class function. - StringRef ClassName(ClassNameStart.data(), FirstSpace); - Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassName), SkipPubSection); - - if (ClassName[ClassName.size() - 1] == ')') { - size_t OpenParens = ClassName.find('('); - if (OpenParens != StringRef::npos) { - StringRef ClassNameNoCategory(ClassName.data(), OpenParens); - Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassNameNoCategory), - SkipPubSection); - - std::string MethodNameNoCategory(Name.getString().data(), OpenParens + 2); - // FIXME: The missing space here may be a bug, but - // dsymutil-classic also does it this way. - MethodNameNoCategory.append(SelectorStart); - Unit.addNameAccelerator(Die, StringPool.getEntry(MethodNameNoCategory), - SkipPubSection); - } - } -} - -static bool -shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, - uint16_t Tag, bool InDebugMap, bool SkipPC, - bool InFunctionScope) { - switch (AttrSpec.Attr) { - default: - return false; - case dwarf::DW_AT_low_pc: - case dwarf::DW_AT_high_pc: - case dwarf::DW_AT_ranges: - return SkipPC; - case dwarf::DW_AT_location: - case dwarf::DW_AT_frame_base: - // FIXME: for some reason dsymutil-classic keeps the location attributes - // when they are of block type (i.e. not location lists). This is totally - // wrong for globals where we will keep a wrong address. It is mostly - // harmless for locals, but there is no point in keeping these anyway when - // the function wasn't linked. - return (SkipPC || (!InFunctionScope && Tag == dwarf::DW_TAG_variable && - !InDebugMap)) && - !DWARFFormValue(AttrSpec.Form).isFormClass(DWARFFormValue::FC_Block); - } -} - -DIE *DwarfLinker::DIECloner::cloneDIE( - const DWARFDie &InputDIE, const DebugMapObject &DMO, CompileUnit &Unit, - OffsetsStringPool &StringPool, int64_t PCOffset, uint32_t OutOffset, - unsigned Flags, bool IsLittleEndian, DIE *Die) { - DWARFUnit &U = Unit.getOrigUnit(); - unsigned Idx = U.getDIEIndex(InputDIE); - CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); - - // Should the DIE appear in the output? - if (!Unit.getInfo(Idx).Keep) - return nullptr; - - uint64_t Offset = InputDIE.getOffset(); - assert(!(Die && Info.Clone) && "Can't supply a DIE and a cloned DIE"); - if (!Die) { - // The DIE might have been already created by a forward reference - // (see cloneDieReferenceAttribute()). - if (!Info.Clone) - Info.Clone = DIE::get(DIEAlloc, dwarf::Tag(InputDIE.getTag())); - Die = Info.Clone; - } - - assert(Die->getTag() == InputDIE.getTag()); - Die->setOffset(OutOffset); - if ((Unit.hasODR() || Unit.isClangModule()) && !Info.Incomplete && - Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt && - Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt && - !Info.Ctxt->getCanonicalDIEOffset()) { - // We are about to emit a DIE that is the root of its own valid - // DeclContext tree. Make the current offset the canonical offset - // for this context. - Info.Ctxt->setCanonicalDIEOffset(OutOffset + Unit.getStartOffset()); - } - - // Extract and clone every attribute. - DWARFDataExtractor Data = U.getDebugInfoExtractor(); - // Point to the next DIE (generally there is always at least a NULL - // entry after the current one). If this is a lone - // DW_TAG_compile_unit without any children, point to the next unit. - uint64_t NextOffset = (Idx + 1 < U.getNumDIEs()) - ? U.getDIEAtIndex(Idx + 1).getOffset() - : U.getNextUnitOffset(); - AttributesInfo AttrInfo; - - // We could copy the data only if we need to apply a relocation to it. After - // testing, it seems there is no performance downside to doing the copy - // unconditionally, and it makes the code simpler. - SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset)); - Data = - DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); - // Modify the copy with relocated addresses. - if (RelocMgr.applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) { - // If we applied relocations, we store the value of high_pc that was - // potentially stored in the input DIE. If high_pc is an address - // (Dwarf version == 2), then it might have been relocated to a - // totally unrelated value (because the end address in the object - // file might be start address of another function which got moved - // independently by the linker). The computation of the actual - // high_pc value is done in cloneAddressAttribute(). - AttrInfo.OrigHighPc = - dwarf::toAddress(InputDIE.find(dwarf::DW_AT_high_pc), 0); - // Also store the low_pc. It might get relocated in an - // inline_subprogram that happens at the beginning of its - // inlining function. - AttrInfo.OrigLowPc = dwarf::toAddress(InputDIE.find(dwarf::DW_AT_low_pc), - std::numeric_limits::max()); - } - - // Reset the Offset to 0 as we will be working on the local copy of - // the data. - Offset = 0; - - const auto *Abbrev = InputDIE.getAbbreviationDeclarationPtr(); - Offset += getULEB128Size(Abbrev->getCode()); - - // We are entering a subprogram. Get and propagate the PCOffset. - if (Die->getTag() == dwarf::DW_TAG_subprogram) - PCOffset = Info.AddrAdjust; - AttrInfo.PCOffset = PCOffset; - - if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) { - Flags |= TF_InFunctionScope; - if (!Info.InDebugMap && LLVM_LIKELY(!Options.Update)) - Flags |= TF_SkipPC; - } - - bool Copied = false; - for (const auto &AttrSpec : Abbrev->attributes()) { - if (LLVM_LIKELY(!Options.Update) && - shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, - Flags & TF_SkipPC, Flags & TF_InFunctionScope)) { - DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, - U.getFormParams()); - // FIXME: dsymutil-classic keeps the old abbreviation around - // even if it's not used. We can remove this (and the copyAbbrev - // helper) as soon as bit-for-bit compatibility is not a goal anymore. - if (!Copied) { - copyAbbrev(*InputDIE.getAbbreviationDeclarationPtr(), Unit.hasODR()); - Copied = true; - } - continue; - } - - DWARFFormValue Val(AttrSpec.Form); - uint64_t AttrSize = Offset; - Val.extractValue(Data, &Offset, U.getFormParams(), &U); - AttrSize = Offset - AttrSize; - - OutOffset += cloneAttribute(*Die, InputDIE, DMO, Unit, StringPool, Val, - AttrSpec, AttrSize, AttrInfo, IsLittleEndian); - } - - // Look for accelerator entries. - uint16_t Tag = InputDIE.getTag(); - // FIXME: This is slightly wrong. An inline_subroutine without a - // low_pc, but with AT_ranges might be interesting to get into the - // accelerator tables too. For now stick with dsymutil's behavior. - if ((Info.InDebugMap || AttrInfo.HasLowPc || AttrInfo.HasRanges) && - Tag != dwarf::DW_TAG_compile_unit && - getDIENames(InputDIE, AttrInfo, StringPool, - Tag != dwarf::DW_TAG_inlined_subroutine)) { - if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name) - Unit.addNameAccelerator(Die, AttrInfo.MangledName, - Tag == dwarf::DW_TAG_inlined_subroutine); - if (AttrInfo.Name) { - if (AttrInfo.NameWithoutTemplate) - Unit.addNameAccelerator(Die, AttrInfo.NameWithoutTemplate, - /* SkipPubSection */ true); - Unit.addNameAccelerator(Die, AttrInfo.Name, - Tag == dwarf::DW_TAG_inlined_subroutine); - } - if (AttrInfo.Name && isObjCSelector(AttrInfo.Name.getString())) - addObjCAccelerator(Unit, Die, AttrInfo.Name, StringPool, - /* SkipPubSection =*/true); - - } else if (Tag == dwarf::DW_TAG_namespace) { - if (!AttrInfo.Name) - AttrInfo.Name = StringPool.getEntry("(anonymous namespace)"); - Unit.addNamespaceAccelerator(Die, AttrInfo.Name); - } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && - getDIENames(InputDIE, AttrInfo, StringPool) && AttrInfo.Name && - AttrInfo.Name.getString()[0]) { - uint32_t Hash = hashFullyQualifiedName(InputDIE, Unit, DMO); - uint64_t RuntimeLang = - dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class)) - .getValueOr(0); - bool ObjCClassIsImplementation = - (RuntimeLang == dwarf::DW_LANG_ObjC || - RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) && - dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type)) - .getValueOr(0); - Unit.addTypeAccelerator(Die, AttrInfo.Name, ObjCClassIsImplementation, - Hash); - } - - // Determine whether there are any children that we want to keep. - bool HasChildren = false; - for (auto Child : InputDIE.children()) { - unsigned Idx = U.getDIEIndex(Child); - if (Unit.getInfo(Idx).Keep) { - HasChildren = true; - break; - } - } - - DIEAbbrev NewAbbrev = Die->generateAbbrev(); - if (HasChildren) - NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); - // Assign a permanent abbrev number - Linker.AssignAbbrev(NewAbbrev); - Die->setAbbrevNumber(NewAbbrev.getNumber()); - - // Add the size of the abbreviation number to the output offset. - OutOffset += getULEB128Size(Die->getAbbrevNumber()); - - if (!HasChildren) { - // Update our size. - Die->setSize(OutOffset - Die->getOffset()); - return Die; - } - - // Recursively clone children. - for (auto Child : InputDIE.children()) { - if (DIE *Clone = cloneDIE(Child, DMO, Unit, StringPool, PCOffset, OutOffset, - Flags, IsLittleEndian)) { - Die->addChild(Clone); - OutOffset = Clone->getOffset() + Clone->getSize(); - } - } - - // Account for the end of children marker. - OutOffset += sizeof(int8_t); - // Update our size. - Die->setSize(OutOffset - Die->getOffset()); - return Die; -} - -/// Patch the input object file relevant debug_ranges entries -/// and emit them in the output file. Update the relevant attributes -/// to point at the new entries. -void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, - DWARFContext &OrigDwarf, - const DebugMapObject &DMO) const { - DWARFDebugRangeList RangeList; - const auto &FunctionRanges = Unit.getFunctionRanges(); - unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); - DWARFDataExtractor RangeExtractor(OrigDwarf.getDWARFObj(), - OrigDwarf.getDWARFObj().getRangesSection(), - OrigDwarf.isLittleEndian(), AddressSize); - auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; - DWARFUnit &OrigUnit = Unit.getOrigUnit(); - auto OrigUnitDie = OrigUnit.getUnitDIE(false); - uint64_t OrigLowPc = - dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), -1ULL); - // Ranges addresses are based on the unit's low_pc. Compute the - // offset we need to apply to adapt to the new unit's low_pc. - int64_t UnitPcOffset = 0; - if (OrigLowPc != -1ULL) - UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); - - for (const auto &RangeAttribute : Unit.getRangesAttributes()) { - uint64_t Offset = RangeAttribute.get(); - RangeAttribute.set(Streamer->getRangesSectionSize()); - if (Error E = RangeList.extract(RangeExtractor, &Offset)) { - llvm::consumeError(std::move(E)); - reportWarning("invalid range list ignored.", DMO); - RangeList.clear(); - } - const auto &Entries = RangeList.getEntries(); - if (!Entries.empty()) { - const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); - - if (CurrRange == InvalidRange || - First.StartAddress + OrigLowPc < CurrRange.start() || - First.StartAddress + OrigLowPc >= CurrRange.stop()) { - CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); - if (CurrRange == InvalidRange || - CurrRange.start() > First.StartAddress + OrigLowPc) { - reportWarning("no mapping for range.", DMO); - continue; - } - } - } - - Streamer->emitRangesEntries(UnitPcOffset, OrigLowPc, CurrRange, Entries, - AddressSize); - } -} - -/// Generate the debug_aranges entries for \p Unit and if the -/// unit has a DW_AT_ranges attribute, also emit the debug_ranges -/// contribution for this attribute. -/// FIXME: this could actually be done right in patchRangesForUnit, -/// but for the sake of initial bit-for-bit compatibility with legacy -/// dsymutil, we have to do it in a delayed pass. -void DwarfLinker::generateUnitRanges(CompileUnit &Unit) const { - auto Attr = Unit.getUnitRangesAttribute(); - if (Attr) - Attr->set(Streamer->getRangesSectionSize()); - Streamer->emitUnitRangesEntries(Unit, static_cast(Attr)); -} - -/// Insert the new line info sequence \p Seq into the current -/// set of already linked line info \p Rows. -static void insertLineSequence(std::vector &Seq, - std::vector &Rows) { - if (Seq.empty()) - return; - - if (!Rows.empty() && Rows.back().Address < Seq.front().Address) { - Rows.insert(Rows.end(), Seq.begin(), Seq.end()); - Seq.clear(); - return; - } - - object::SectionedAddress Front = Seq.front().Address; - auto InsertPoint = partition_point( - Rows, [=](const DWARFDebugLine::Row &O) { return O.Address < Front; }); - - // FIXME: this only removes the unneeded end_sequence if the - // sequences have been inserted in order. Using a global sort like - // described in patchLineTableForUnit() and delaying the end_sequene - // elimination to emitLineTableForUnit() we can get rid of all of them. - if (InsertPoint != Rows.end() && InsertPoint->Address == Front && - InsertPoint->EndSequence) { - *InsertPoint = Seq.front(); - Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end()); - } else { - Rows.insert(InsertPoint, Seq.begin(), Seq.end()); - } - - Seq.clear(); -} - -static void patchStmtList(DIE &Die, DIEInteger Offset) { - for (auto &V : Die.values()) - if (V.getAttribute() == dwarf::DW_AT_stmt_list) { - V = DIEValue(V.getAttribute(), V.getForm(), Offset); - return; - } - - llvm_unreachable("Didn't find DW_AT_stmt_list in cloned DIE!"); -} - -/// Extract the line table for \p Unit from \p OrigDwarf, and -/// recreate a relocated version of these for the address ranges that -/// are present in the binary. -void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, - DWARFContext &OrigDwarf, - RangesTy &Ranges, - const DebugMapObject &DMO) { - DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); - auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); - if (!StmtList) - return; - - // Update the cloned DW_AT_stmt_list with the correct debug_line offset. - if (auto *OutputDIE = Unit.getOutputUnitDIE()) - patchStmtList(*OutputDIE, DIEInteger(Streamer->getLineSectionSize())); - - // Parse the original line info for the unit. - DWARFDebugLine::LineTable LineTable; - uint64_t StmtOffset = *StmtList; - DWARFDataExtractor LineExtractor( - OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(), - OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize()); - if (Options.Translator) - return Streamer->translateLineTable(LineExtractor, StmtOffset); - - Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, - &Unit.getOrigUnit(), DWARFContext::dumpWarning); - DWARFContext::dumpWarning(std::move(Err)); - - // This vector is the output line table. - std::vector NewRows; - NewRows.reserve(LineTable.Rows.size()); - - // Current sequence of rows being extracted, before being inserted - // in NewRows. - std::vector Seq; - const auto &FunctionRanges = Unit.getFunctionRanges(); - auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; - - // FIXME: This logic is meant to generate exactly the same output as - // Darwin's classic dsymutil. There is a nicer way to implement this - // by simply putting all the relocated line info in NewRows and simply - // sorting NewRows before passing it to emitLineTableForUnit. This - // should be correct as sequences for a function should stay - // together in the sorted output. There are a few corner cases that - // look suspicious though, and that required to implement the logic - // this way. Revisit that once initial validation is finished. - - // Iterate over the object file line info and extract the sequences - // that correspond to linked functions. - for (auto &Row : LineTable.Rows) { - // Check whether we stepped out of the range. The range is - // half-open, but consider accept the end address of the range if - // it is marked as end_sequence in the input (because in that - // case, the relocation offset is accurate and that entry won't - // serve as the start of another function). - if (CurrRange == InvalidRange || Row.Address.Address < CurrRange.start() || - Row.Address.Address > CurrRange.stop() || - (Row.Address.Address == CurrRange.stop() && !Row.EndSequence)) { - // We just stepped out of a known range. Insert a end_sequence - // corresponding to the end of the range. - uint64_t StopAddress = CurrRange != InvalidRange - ? CurrRange.stop() + CurrRange.value() - : -1ULL; - CurrRange = FunctionRanges.find(Row.Address.Address); - bool CurrRangeValid = - CurrRange != InvalidRange && CurrRange.start() <= Row.Address.Address; - if (!CurrRangeValid) { - CurrRange = InvalidRange; - if (StopAddress != -1ULL) { - // Try harder by looking in the DebugMapObject function - // ranges map. There are corner cases where this finds a - // valid entry. It's unclear if this is right or wrong, but - // for now do as dsymutil. - // FIXME: Understand exactly what cases this addresses and - // potentially remove it along with the Ranges map. - auto Range = Ranges.lower_bound(Row.Address.Address); - if (Range != Ranges.begin() && Range != Ranges.end()) - --Range; - - if (Range != Ranges.end() && Range->first <= Row.Address.Address && - Range->second.HighPC >= Row.Address.Address) { - StopAddress = Row.Address.Address + Range->second.Offset; - } - } - } - if (StopAddress != -1ULL && !Seq.empty()) { - // Insert end sequence row with the computed end address, but - // the same line as the previous one. - auto NextLine = Seq.back(); - NextLine.Address.Address = StopAddress; - NextLine.EndSequence = 1; - NextLine.PrologueEnd = 0; - NextLine.BasicBlock = 0; - NextLine.EpilogueBegin = 0; - Seq.push_back(NextLine); - insertLineSequence(Seq, NewRows); - } - - if (!CurrRangeValid) - continue; - } - - // Ignore empty sequences. - if (Row.EndSequence && Seq.empty()) - continue; - - // Relocate row address and add it to the current sequence. - Row.Address.Address += CurrRange.value(); - Seq.emplace_back(Row); - - if (Row.EndSequence) - insertLineSequence(Seq, NewRows); - } - - // Finished extracting, now emit the line tables. - // FIXME: LLVM hard-codes its prologue values. We just copy the - // prologue over and that works because we act as both producer and - // consumer. It would be nicer to have a real configurable line - // table emitter. - if (LineTable.Prologue.getVersion() < 2 || - LineTable.Prologue.getVersion() > 5 || - LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || - LineTable.Prologue.OpcodeBase > 13) - reportWarning("line table parameters mismatch. Cannot emit.", DMO); - else { - uint32_t PrologueEnd = *StmtList + 10 + LineTable.Prologue.PrologueLength; - // DWARF v5 has an extra 2 bytes of information before the header_length - // field. - if (LineTable.Prologue.getVersion() == 5) - PrologueEnd += 2; - StringRef LineData = OrigDwarf.getDWARFObj().getLineSection().Data; - MCDwarfLineTableParams Params; - Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; - Params.DWARF2LineBase = LineTable.Prologue.LineBase; - Params.DWARF2LineRange = LineTable.Prologue.LineRange; - Streamer->emitLineTableForUnit(Params, - LineData.slice(*StmtList + 4, PrologueEnd), - LineTable.Prologue.MinInstLength, NewRows, - Unit.getOrigUnit().getAddressByteSize()); - } -} - -void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { - switch (Options.TheAccelTableKind) { - case AccelTableKind::Apple: - emitAppleAcceleratorEntriesForUnit(Unit); - break; - case AccelTableKind::Dwarf: - emitDwarfAcceleratorEntriesForUnit(Unit); - break; - case AccelTableKind::Default: - llvm_unreachable("The default must be updated to a concrete value."); - break; - } -} - -void DwarfLinker::emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit) { - // Add namespaces. - for (const auto &Namespace : Unit.getNamespaces()) - AppleNamespaces.addName(Namespace.Name, - Namespace.Die->getOffset() + Unit.getStartOffset()); - - /// Add names. - if (!Options.Minimize) - Streamer->emitPubNamesForUnit(Unit); - for (const auto &Pubname : Unit.getPubnames()) - AppleNames.addName(Pubname.Name, - Pubname.Die->getOffset() + Unit.getStartOffset()); - - /// Add types. - if (!Options.Minimize) - Streamer->emitPubTypesForUnit(Unit); - for (const auto &Pubtype : Unit.getPubtypes()) - AppleTypes.addName( - Pubtype.Name, Pubtype.Die->getOffset() + Unit.getStartOffset(), - Pubtype.Die->getTag(), - Pubtype.ObjcClassImplementation ? dwarf::DW_FLAG_type_implementation - : 0, - Pubtype.QualifiedNameHash); - - /// Add ObjC names. - for (const auto &ObjC : Unit.getObjC()) - AppleObjc.addName(ObjC.Name, ObjC.Die->getOffset() + Unit.getStartOffset()); -} - -void DwarfLinker::emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit) { - for (const auto &Namespace : Unit.getNamespaces()) - DebugNames.addName(Namespace.Name, Namespace.Die->getOffset(), - Namespace.Die->getTag(), Unit.getUniqueID()); - for (const auto &Pubname : Unit.getPubnames()) - DebugNames.addName(Pubname.Name, Pubname.Die->getOffset(), - Pubname.Die->getTag(), Unit.getUniqueID()); - for (const auto &Pubtype : Unit.getPubtypes()) - DebugNames.addName(Pubtype.Name, Pubtype.Die->getOffset(), - Pubtype.Die->getTag(), Unit.getUniqueID()); -} - -/// Read the frame info stored in the object, and emit the -/// patched frame descriptions for the linked binary. -/// -/// This is actually pretty easy as the data of the CIEs and FDEs can -/// be considered as black boxes and moved as is. The only thing to do -/// is to patch the addresses in the headers. -void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, - RangesTy &Ranges, - DWARFContext &OrigDwarf, - unsigned AddrSize) { - StringRef FrameData = OrigDwarf.getDWARFObj().getFrameSection().Data; - if (FrameData.empty()) - return; - - DataExtractor Data(FrameData, OrigDwarf.isLittleEndian(), 0); - uint64_t InputOffset = 0; - - // Store the data of the CIEs defined in this object, keyed by their - // offsets. - DenseMap LocalCIES; - - while (Data.isValidOffset(InputOffset)) { - uint64_t EntryOffset = InputOffset; - uint32_t InitialLength = Data.getU32(&InputOffset); - if (InitialLength == 0xFFFFFFFF) - return reportWarning("Dwarf64 bits no supported", DMO); - - uint32_t CIEId = Data.getU32(&InputOffset); - if (CIEId == 0xFFFFFFFF) { - // This is a CIE, store it. - StringRef CIEData = FrameData.substr(EntryOffset, InitialLength + 4); - LocalCIES[EntryOffset] = CIEData; - // The -4 is to account for the CIEId we just read. - InputOffset += InitialLength - 4; - continue; - } - - uint32_t Loc = Data.getUnsigned(&InputOffset, AddrSize); - - // Some compilers seem to emit frame info that doesn't start at - // the function entry point, thus we can't just lookup the address - // in the debug map. Use the linker's range map to see if the FDE - // describes something that we can relocate. - auto Range = Ranges.upper_bound(Loc); - if (Range != Ranges.begin()) - --Range; - if (Range == Ranges.end() || Range->first > Loc || - Range->second.HighPC <= Loc) { - // The +4 is to account for the size of the InitialLength field itself. - InputOffset = EntryOffset + InitialLength + 4; - continue; - } - - // This is an FDE, and we have a mapping. - // Have we already emitted a corresponding CIE? - StringRef CIEData = LocalCIES[CIEId]; - if (CIEData.empty()) - return reportWarning("Inconsistent debug_frame content. Dropping.", DMO); - - // Look if we already emitted a CIE that corresponds to the - // referenced one (the CIE data is the key of that lookup). - auto IteratorInserted = EmittedCIEs.insert( - std::make_pair(CIEData, Streamer->getFrameSectionSize())); - // If there is no CIE yet for this ID, emit it. - if (IteratorInserted.second || - // FIXME: dsymutil-classic only caches the last used CIE for - // reuse. Mimic that behavior for now. Just removing that - // second half of the condition and the LastCIEOffset variable - // makes the code DTRT. - LastCIEOffset != IteratorInserted.first->getValue()) { - LastCIEOffset = Streamer->getFrameSectionSize(); - IteratorInserted.first->getValue() = LastCIEOffset; - Streamer->emitCIE(CIEData); - } - - // Emit the FDE with updated address and CIE pointer. - // (4 + AddrSize) is the size of the CIEId + initial_location - // fields that will get reconstructed by emitFDE(). - unsigned FDERemainingBytes = InitialLength - (4 + AddrSize); - Streamer->emitFDE(IteratorInserted.first->getValue(), AddrSize, - Loc + Range->second.Offset, - FrameData.substr(InputOffset, FDERemainingBytes)); - InputOffset += FDERemainingBytes; - } -} - -void DwarfLinker::DIECloner::copyAbbrev( - const DWARFAbbreviationDeclaration &Abbrev, bool hasODR) { - DIEAbbrev Copy(dwarf::Tag(Abbrev.getTag()), - dwarf::Form(Abbrev.hasChildren())); - - for (const auto &Attr : Abbrev.attributes()) { - uint16_t Form = Attr.Form; - if (hasODR && isODRAttribute(Attr.Attr)) - Form = dwarf::DW_FORM_ref_addr; - Copy.AddAttribute(dwarf::Attribute(Attr.Attr), dwarf::Form(Form)); - } - - Linker.AssignAbbrev(Copy); -} - -uint32_t -DwarfLinker::DIECloner::hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, - const DebugMapObject &DMO, - int ChildRecurseDepth) { - const char *Name = nullptr; - DWARFUnit *OrigUnit = &U.getOrigUnit(); - CompileUnit *CU = &U; - Optional Ref; - - while (1) { - if (const char *CurrentName = DIE.getName(DINameKind::ShortName)) - Name = CurrentName; - - if (!(Ref = DIE.find(dwarf::DW_AT_specification)) && - !(Ref = DIE.find(dwarf::DW_AT_abstract_origin))) - break; - - if (!Ref->isFormClass(DWARFFormValue::FC_Reference)) - break; - - CompileUnit *RefCU; - if (auto RefDIE = - resolveDIEReference(Linker, DMO, CompileUnits, *Ref, DIE, RefCU)) { - CU = RefCU; - OrigUnit = &RefCU->getOrigUnit(); - DIE = RefDIE; - } - } - - unsigned Idx = OrigUnit->getDIEIndex(DIE); - if (!Name && DIE.getTag() == dwarf::DW_TAG_namespace) - Name = "(anonymous namespace)"; - - if (CU->getInfo(Idx).ParentIdx == 0 || - // FIXME: dsymutil-classic compatibility. Ignore modules. - CU->getOrigUnit().getDIEAtIndex(CU->getInfo(Idx).ParentIdx).getTag() == - dwarf::DW_TAG_module) - return djbHash(Name ? Name : "", djbHash(ChildRecurseDepth ? "" : "::")); - - DWARFDie Die = OrigUnit->getDIEAtIndex(CU->getInfo(Idx).ParentIdx); - return djbHash( - (Name ? Name : ""), - djbHash((Name ? "::" : ""), - hashFullyQualifiedName(Die, *CU, DMO, ++ChildRecurseDepth))); -} - -static uint64_t getDwoId(const DWARFDie &CUDie, const DWARFUnit &Unit) { - auto DwoId = dwarf::toUnsigned( - CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id})); - if (DwoId) - return *DwoId; - return 0; -} - -bool DwarfLinker::registerModuleReference( - DWARFDie CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, - const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, - UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, - uint64_t ModulesEndOffset, unsigned &UnitID, bool IsLittleEndian, - unsigned Indent, bool Quiet) { - std::string PCMfile = dwarf::toString( - CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); - if (PCMfile.empty()) - return false; - - // Clang module DWARF skeleton CUs abuse this for the path to the module. - uint64_t DwoId = getDwoId(CUDie, Unit); - - std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); - if (Name.empty()) { - if (!Quiet) - reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO); - return true; - } - - if (!Quiet && Options.Verbose) { - outs().indent(Indent); - outs() << "Found clang module reference " << PCMfile; - } - - auto Cached = ClangModules.find(PCMfile); - if (Cached != ClangModules.end()) { - // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is - // fixed in clang, only warn about DWO_id mismatches in verbose mode. - // ASTFileSignatures will change randomly when a module is rebuilt. - if (!Quiet && Options.Verbose && (Cached->second != DwoId)) - reportWarning(Twine("hash mismatch: this object file was built against a " - "different version of the module ") + - PCMfile, - DMO); - if (!Quiet && Options.Verbose) - outs() << " [cached].\n"; - return true; - } - if (!Quiet && Options.Verbose) - outs() << " ...\n"; - - // Cyclic dependencies are disallowed by Clang, but we still - // shouldn't run into an infinite loop, so mark it as processed now. - ClangModules.insert({PCMfile, DwoId}); - - if (Error E = loadClangModule(CUDie, PCMfile, Name, DwoId, ModuleMap, DMO, - Ranges, StringPool, UniquingStringPool, - ODRContexts, ModulesEndOffset, UnitID, - IsLittleEndian, Indent + 2, Quiet)) { - consumeError(std::move(E)); - return false; - } - return true; -} - -ErrorOr -DwarfLinker::loadObject(const DebugMapObject &Obj, const DebugMap &Map) { - auto ObjectEntry = - BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp()); - if (!ObjectEntry) { - auto Err = ObjectEntry.takeError(); - reportWarning( - Twine(Obj.getObjectFilename()) + ": " + toString(std::move(Err)), Obj); - return errorToErrorCode(std::move(Err)); - } - - auto Object = ObjectEntry->getObject(Map.getTriple()); - if (!Object) { - auto Err = Object.takeError(); - reportWarning( - Twine(Obj.getObjectFilename()) + ": " + toString(std::move(Err)), Obj); - return errorToErrorCode(std::move(Err)); - } - - return *Object; -} - -Error DwarfLinker::loadClangModule( - DWARFDie CUDie, StringRef Filename, StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, - DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, unsigned &UnitID, - bool IsLittleEndian, unsigned Indent, bool Quiet) { - /// Using a SmallString<0> because loadClangModule() is recursive. - SmallString<0> Path(Options.PrependPath); - if (sys::path::is_relative(Filename)) - resolveRelativeObjectPath(Path, CUDie); - sys::path::append(Path, Filename); - // Don't use the cached binary holder because we have no thread-safety - // guarantee and the lifetime is limited. - auto &Obj = ModuleMap.addDebugMapObject( - Path, sys::TimePoint(), MachO::N_OSO); - auto ErrOrObj = loadObject(Obj, ModuleMap); - if (!ErrOrObj) { - // Try and emit more helpful warnings by applying some heuristics. - StringRef ObjFile = DMO.getObjectFilename(); - bool isClangModule = sys::path::extension(Filename).equals(".pcm"); - bool isArchive = ObjFile.endswith(")"); - if (isClangModule) { - StringRef ModuleCacheDir = sys::path::parent_path(Path); - if (sys::fs::exists(ModuleCacheDir)) { - // If the module's parent directory exists, we assume that the module - // cache has expired and was pruned by clang. A more adventurous - // dsymutil would invoke clang to rebuild the module now. - if (!ModuleCacheHintDisplayed) { - WithColor::note() << "The clang module cache may have expired since " - "this object file was built. Rebuilding the " - "object file will rebuild the module cache.\n"; - ModuleCacheHintDisplayed = true; - } - } else if (isArchive) { - // If the module cache directory doesn't exist at all and the object - // file is inside a static library, we assume that the static library - // was built on a different machine. We don't want to discourage module - // debugging for convenience libraries within a project though. - if (!ArchiveHintDisplayed) { - WithColor::note() - << "Linking a static library that was built with " - "-gmodules, but the module cache was not found. " - "Redistributable static libraries should never be " - "built with module debugging enabled. The debug " - "experience will be degraded due to incomplete " - "debug information.\n"; - ArchiveHintDisplayed = true; - } - } - } - return Error::success(); - } - - std::unique_ptr Unit; - - // Setup access to the debug info. - auto DwarfContext = DWARFContext::create(*ErrOrObj); - RelocationManager RelocMgr(*this); - - for (const auto &CU : DwarfContext->compile_units()) { - updateDwarfVersion(CU->getVersion()); - // Recursively get all modules imported by this one. - auto CUDie = CU->getUnitDIE(false); - if (!CUDie) - continue; - if (!registerModuleReference(CUDie, *CU, ModuleMap, DMO, Ranges, StringPool, - UniquingStringPool, ODRContexts, - ModulesEndOffset, UnitID, IsLittleEndian, - Indent, Quiet)) { - if (Unit) { - std::string Err = - (Filename + - ": Clang modules are expected to have exactly 1 compile unit.\n") - .str(); - error(Err); - return make_error(Err, inconvertibleErrorCode()); - } - // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is - // fixed in clang, only warn about DWO_id mismatches in verbose mode. - // ASTFileSignatures will change randomly when a module is rebuilt. - uint64_t PCMDwoId = getDwoId(CUDie, *CU); - if (PCMDwoId != DwoId) { - if (!Quiet && Options.Verbose) - reportWarning( - Twine("hash mismatch: this object file was built against a " - "different version of the module ") + - Filename, - DMO); - // Update the cache entry with the DwoId of the module loaded from disk. - ClangModules[Filename] = PCMDwoId; - } - - // Add this module. - Unit = std::make_unique(*CU, UnitID++, !Options.NoODR, - ModuleName); - Unit->setHasInterestingContent(); - analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), - UniquingStringPool, ODRContexts, ModulesEndOffset, - ParseableSwiftInterfaces, - [&](const Twine &Warning, const DWARFDie &DIE) { - reportWarning(Warning, DMO, &DIE); - }); - // Keep everything. - Unit->markEverythingAsKept(); - } - } - if (!Unit->getOrigUnit().getUnitDIE().hasChildren()) - return Error::success(); - if (!Quiet && Options.Verbose) { - outs().indent(Indent); - outs() << "cloning .debug_info from " << Filename << "\n"; - } - - UnitListTy CompileUnits; - CompileUnits.push_back(std::move(Unit)); - DIECloner(*this, RelocMgr, DIEAlloc, CompileUnits, Options) - .cloneAllCompileUnits(*DwarfContext, DMO, Ranges, StringPool, - IsLittleEndian); - return Error::success(); -} - -void DwarfLinker::DIECloner::cloneAllCompileUnits( - DWARFContext &DwarfContext, const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool, bool IsLittleEndian) { - if (!Linker.Streamer) - return; - - for (auto &CurrentUnit : CompileUnits) { - auto InputDIE = CurrentUnit->getOrigUnit().getUnitDIE(); - CurrentUnit->setStartOffset(Linker.OutputDebugInfoSize); - if (!InputDIE) { - Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); - continue; - } - if (CurrentUnit->getInfo(0).Keep) { - // Clone the InputDIE into your Unit DIE in our compile unit since it - // already has a DIE inside of it. - CurrentUnit->createOutputDIE(); - cloneDIE(InputDIE, DMO, *CurrentUnit, StringPool, 0 /* PC offset */, - 11 /* Unit Header size */, 0, IsLittleEndian, - CurrentUnit->getOutputUnitDIE()); - } - - Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); - - if (Linker.Options.NoOutput) - continue; - - // FIXME: for compatibility with the classic dsymutil, we emit - // an empty line table for the unit, even if the unit doesn't - // actually exist in the DIE tree. - if (LLVM_LIKELY(!Linker.Options.Update) || Linker.Options.Translator) - Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext, Ranges, DMO); - - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - - if (LLVM_UNLIKELY(Linker.Options.Update)) - continue; - - Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); - auto ProcessExpr = [&](StringRef Bytes, SmallVectorImpl &Buffer) { - DWARFUnit &OrigUnit = CurrentUnit->getOrigUnit(); - DataExtractor Data(Bytes, IsLittleEndian, OrigUnit.getAddressByteSize()); - cloneExpression(Data, - DWARFExpression(Data, OrigUnit.getVersion(), - OrigUnit.getAddressByteSize()), - DMO, *CurrentUnit, Buffer); - }; - Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext, - ProcessExpr); - } - - if (Linker.Options.NoOutput) - return; - - // Emit all the compile unit's debug information. - for (auto &CurrentUnit : CompileUnits) { - if (LLVM_LIKELY(!Linker.Options.Update)) - Linker.generateUnitRanges(*CurrentUnit); - - CurrentUnit->fixupForwardReferences(); - - if (!CurrentUnit->getOutputUnitDIE()) - continue; - - Linker.Streamer->emitCompileUnitHeader(*CurrentUnit); - Linker.Streamer->emitDIE(*CurrentUnit->getOutputUnitDIE()); - } -} - -void DwarfLinker::updateAccelKind(DWARFContext &Dwarf) { - if (Options.TheAccelTableKind != AccelTableKind::Default) - return; - - auto &DwarfObj = Dwarf.getDWARFObj(); - - if (!AtLeastOneDwarfAccelTable && - (!DwarfObj.getAppleNamesSection().Data.empty() || - !DwarfObj.getAppleTypesSection().Data.empty() || - !DwarfObj.getAppleNamespacesSection().Data.empty() || - !DwarfObj.getAppleObjCSection().Data.empty())) { - AtLeastOneAppleAccelTable = true; - } - - if (!AtLeastOneDwarfAccelTable && - !DwarfObj.getNamesSection().Data.empty()) { - AtLeastOneDwarfAccelTable = true; - } -} - -bool DwarfLinker::emitPaperTrailWarnings(const DebugMapObject &DMO, - const DebugMap &Map, - OffsetsStringPool &StringPool) { - if (DMO.getWarnings().empty() || !DMO.empty()) - return false; - - Streamer->switchToDebugInfoSection(/* Version */ 2); - DIE *CUDie = DIE::get(DIEAlloc, dwarf::DW_TAG_compile_unit); - CUDie->setOffset(11); - StringRef Producer = StringPool.internString("dsymutil"); - StringRef File = StringPool.internString(DMO.getObjectFilename()); - CUDie->addValue(DIEAlloc, dwarf::DW_AT_producer, dwarf::DW_FORM_strp, - DIEInteger(StringPool.getStringOffset(Producer))); - DIEBlock *String = new (DIEAlloc) DIEBlock(); - DIEBlocks.push_back(String); - for (auto &C : File) - String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, - DIEInteger(C)); - String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, - DIEInteger(0)); - - CUDie->addValue(DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_string, String); - for (const auto &Warning : DMO.getWarnings()) { - DIE &ConstDie = CUDie->addChild(DIE::get(DIEAlloc, dwarf::DW_TAG_constant)); - ConstDie.addValue( - DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, - DIEInteger(StringPool.getStringOffset("dsymutil_warning"))); - ConstDie.addValue(DIEAlloc, dwarf::DW_AT_artificial, dwarf::DW_FORM_flag, - DIEInteger(1)); - ConstDie.addValue(DIEAlloc, dwarf::DW_AT_const_value, dwarf::DW_FORM_strp, - DIEInteger(StringPool.getStringOffset(Warning))); - } - unsigned Size = 4 /* FORM_strp */ + File.size() + 1 + - DMO.getWarnings().size() * (4 + 1 + 4) + - 1 /* End of children */; - DIEAbbrev Abbrev = CUDie->generateAbbrev(); - AssignAbbrev(Abbrev); - CUDie->setAbbrevNumber(Abbrev.getNumber()); - Size += getULEB128Size(Abbrev.getNumber()); - // Abbreviation ordering needed for classic compatibility. - for (auto &Child : CUDie->children()) { - Abbrev = Child.generateAbbrev(); - AssignAbbrev(Abbrev); - Child.setAbbrevNumber(Abbrev.getNumber()); - Size += getULEB128Size(Abbrev.getNumber()); - } - CUDie->setSize(Size); - auto &Asm = Streamer->getAsmPrinter(); - Asm.emitInt32(11 + CUDie->getSize() - 4); - Asm.emitInt16(2); - Asm.emitInt32(0); - Asm.emitInt8(Map.getTriple().isArch64Bit() ? 8 : 4); - Streamer->emitDIE(*CUDie); - OutputDebugInfoSize += 11 /* Header */ + Size; - - return true; -} - -static Error copySwiftInterfaces( - const std::map &ParseableSwiftInterfaces, - StringRef Architecture, const LinkOptions &Options) { - std::error_code EC; - SmallString<128> InputPath; - SmallString<128> Path; - sys::path::append(Path, *Options.ResourceDir, "Swift", Architecture); - if ((EC = sys::fs::create_directories(Path.str(), true, - sys::fs::perms::all_all))) - return make_error( - "cannot create directory: " + toString(errorCodeToError(EC)), EC); - unsigned BaseLength = Path.size(); - - for (auto &I : ParseableSwiftInterfaces) { - StringRef ModuleName = I.first; - StringRef InterfaceFile = I.second; - if (!Options.PrependPath.empty()) { - InputPath.clear(); - sys::path::append(InputPath, Options.PrependPath, InterfaceFile); - InterfaceFile = InputPath; - } - sys::path::append(Path, ModuleName); - Path.append(".swiftinterface"); - if (Options.Verbose) - outs() << "copy parseable Swift interface " << InterfaceFile << " -> " - << Path.str() << '\n'; - - // copy_file attempts an APFS clone first, so this should be cheap. - if ((EC = sys::fs::copy_file(InterfaceFile, Path.str()))) - warn(Twine("cannot copy parseable Swift interface ") + - InterfaceFile + ": " + - toString(errorCodeToError(EC))); - Path.resize(BaseLength); - } - return Error::success(); -} - -static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath, - StringRef ArchName, - const remarks::RemarkLinker &RL) { - // Make sure we don't create the directories and the file if there is nothing - // to serialize. - if (RL.empty()) - return Error::success(); - - SmallString<128> InputPath; - SmallString<128> Path; - // Create the "Remarks" directory in the "Resources" directory. - sys::path::append(Path, *Options.ResourceDir, "Remarks"); - if (std::error_code EC = sys::fs::create_directories(Path.str(), true, - sys::fs::perms::all_all)) - return errorCodeToError(EC); - - // Append the file name. - // For fat binaries, also append a dash and the architecture name. - sys::path::append(Path, sys::path::filename(BinaryPath)); - if (Options.NumDebugMaps > 1) { - // More than one debug map means we have a fat binary. - Path += '-'; - Path += ArchName; - } - - std::error_code EC; - raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, sys::fs::OF_None); - if (EC) - return errorCodeToError(EC); - - if (Error E = RL.serialize(OS, Options.RemarksFormat)) - return E; - - return Error::success(); -} - -bool DwarfLinker::link(const DebugMap &Map) { - if (!createStreamer(Map.getTriple(), OutFile)) - return false; - - // Size of the DIEs (and headers) generated for the linked output. - OutputDebugInfoSize = 0; - // A unique ID that identifies each compile unit. - unsigned UnitID = 0; - DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath()); - - // First populate the data structure we need for each iteration of the - // parallel loop. - unsigned NumObjects = Map.getNumberOfObjects(); - std::vector ObjectContexts; - ObjectContexts.reserve(NumObjects); - for (const auto &Obj : Map.objects()) { - ObjectContexts.emplace_back(Map, *this, *Obj.get()); - LinkContext &LC = ObjectContexts.back(); - if (LC.ObjectFile) - updateAccelKind(*LC.DwarfContext); - } - - // This Dwarf string pool which is only used for uniquing. This one should - // never be used for offsets as its not thread-safe or predictable. - UniquingStringPool UniquingStringPool; - - // This Dwarf string pool which is used for emission. It must be used - // serially as the order of calling getStringOffset matters for - // reproducibility. - OffsetsStringPool OffsetsStringPool(Options.Translator); - - // ODR Contexts for the link. - DeclContextTree ODRContexts; - - // If we haven't decided on an accelerator table kind yet, we base ourselves - // on the DWARF we have seen so far. At this point we haven't pulled in debug - // information from modules yet, so it is technically possible that they - // would affect the decision. However, as they're built with the same - // compiler and flags, it is safe to assume that they will follow the - // decision made here. - if (Options.TheAccelTableKind == AccelTableKind::Default) { - if (AtLeastOneDwarfAccelTable && !AtLeastOneAppleAccelTable) - Options.TheAccelTableKind = AccelTableKind::Dwarf; - else - Options.TheAccelTableKind = AccelTableKind::Apple; - } - - for (LinkContext &LinkContext : ObjectContexts) { - if (Options.Verbose) - outs() << "DEBUG MAP OBJECT: " << LinkContext.DMO.getObjectFilename() - << "\n"; - - // N_AST objects (swiftmodule files) should get dumped directly into the - // appropriate DWARF section. - if (LinkContext.DMO.getType() == MachO::N_AST) { - StringRef File = LinkContext.DMO.getObjectFilename(); - auto ErrorOrMem = MemoryBuffer::getFile(File); - if (!ErrorOrMem) { - warn("Could not open '" + File + "'\n"); - continue; - } - sys::fs::file_status Stat; - if (auto Err = sys::fs::status(File, Stat)) { - warn(Err.message()); - continue; - } - if (!Options.NoTimestamp) { - // The modification can have sub-second precision so we need to cast - // away the extra precision that's not present in the debug map. - auto ModificationTime = - std::chrono::time_point_cast( - Stat.getLastModificationTime()); - if (ModificationTime != LinkContext.DMO.getTimestamp()) { - // Not using the helper here as we can easily stream TimePoint<>. - WithColor::warning() - << "Timestamp mismatch for " << File << ": " - << Stat.getLastModificationTime() << " and " - << sys::TimePoint<>(LinkContext.DMO.getTimestamp()) << "\n"; - continue; - } - } - - // Copy the module into the .swift_ast section. - if (!Options.NoOutput) - Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer()); - continue; - } - - if (emitPaperTrailWarnings(LinkContext.DMO, Map, OffsetsStringPool)) - continue; - - if (!LinkContext.ObjectFile) - continue; - - // Look for relocations that correspond to debug map entries. - - if (LLVM_LIKELY(!Options.Update) && - !LinkContext.RelocMgr.findValidRelocsInDebugInfo( - *LinkContext.ObjectFile, LinkContext.DMO)) { - if (Options.Verbose) - outs() << "No valid relocations found. Skipping.\n"; - - // Clear this ObjFile entry as a signal to other loops that we should not - // process this iteration. - LinkContext.ObjectFile = nullptr; - continue; - } - - // Setup access to the debug info. - if (!LinkContext.DwarfContext) - continue; - - startDebugObject(LinkContext); - - // In a first phase, just read in the debug info and load all clang modules. - LinkContext.CompileUnits.reserve( - LinkContext.DwarfContext->getNumCompileUnits()); - - for (const auto &CU : LinkContext.DwarfContext->compile_units()) { - updateDwarfVersion(CU->getVersion()); - auto CUDie = CU->getUnitDIE(false); - if (Options.Verbose) { - outs() << "Input compilation unit:"; - DIDumpOptions DumpOpts; - DumpOpts.ChildRecurseDepth = 0; - DumpOpts.Verbose = Options.Verbose; - CUDie.dump(outs(), 0, DumpOpts); - } - if (CUDie && !LLVM_UNLIKELY(Options.Update)) - registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, - LinkContext.Ranges, OffsetsStringPool, - UniquingStringPool, ODRContexts, 0, UnitID, - LinkContext.DwarfContext->isLittleEndian()); - } - } - - // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway. - if (MaxDwarfVersion == 0) - MaxDwarfVersion = 3; - - // At this point we know how much data we have emitted. We use this value to - // compare canonical DIE offsets in analyzeContextInfo to see if a definition - // is already emitted, without being affected by canonical die offsets set - // later. This prevents undeterminism when analyze and clone execute - // concurrently, as clone set the canonical DIE offset and analyze reads it. - const uint64_t ModulesEndOffset = OutputDebugInfoSize; - - // These variables manage the list of processed object files. - // The mutex and condition variable are to ensure that this is thread safe. - std::mutex ProcessedFilesMutex; - std::condition_variable ProcessedFilesConditionVariable; - BitVector ProcessedFiles(NumObjects, false); - - // Analyzing the context info is particularly expensive so it is executed in - // parallel with emitting the previous compile unit. - auto AnalyzeLambda = [&](size_t i) { - auto &LinkContext = ObjectContexts[i]; - - if (!LinkContext.ObjectFile || !LinkContext.DwarfContext) - return; - - for (const auto &CU : LinkContext.DwarfContext->compile_units()) { - updateDwarfVersion(CU->getVersion()); - // The !registerModuleReference() condition effectively skips - // over fully resolved skeleton units. This second pass of - // registerModuleReferences doesn't do any new work, but it - // will collect top-level errors, which are suppressed. Module - // warnings were already displayed in the first iteration. - bool Quiet = true; - auto CUDie = CU->getUnitDIE(false); - if (!CUDie || LLVM_UNLIKELY(Options.Update) || - !registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, - LinkContext.Ranges, OffsetsStringPool, - UniquingStringPool, ODRContexts, - ModulesEndOffset, UnitID, Quiet)) { - LinkContext.CompileUnits.push_back(std::make_unique( - *CU, UnitID++, !Options.NoODR && !Options.Update, "")); - } - } - - // Now build the DIE parent links that we will use during the next phase. - for (auto &CurrentUnit : LinkContext.CompileUnits) { - auto CUDie = CurrentUnit->getOrigUnit().getUnitDIE(); - if (!CUDie) - continue; - analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, - *CurrentUnit, &ODRContexts.getRoot(), - UniquingStringPool, ODRContexts, ModulesEndOffset, - ParseableSwiftInterfaces, - [&](const Twine &Warning, const DWARFDie &DIE) { - reportWarning(Warning, LinkContext.DMO, &DIE); - }); - } - }; - - // And then the remaining work in serial again. - // Note, although this loop runs in serial, it can run in parallel with - // the analyzeContextInfo loop so long as we process files with indices >= - // than those processed by analyzeContextInfo. - auto CloneLambda = [&](size_t i) { - auto &LinkContext = ObjectContexts[i]; - if (!LinkContext.ObjectFile) - return; - - // Then mark all the DIEs that need to be present in the linked output - // and collect some information about them. - // Note that this loop can not be merged with the previous one because - // cross-cu references require the ParentIdx to be setup for every CU in - // the object file before calling this. - if (LLVM_UNLIKELY(Options.Update)) { - for (auto &CurrentUnit : LinkContext.CompileUnits) - CurrentUnit->markEverythingAsKept(); - Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile); - } else { - for (auto &CurrentUnit : LinkContext.CompileUnits) - lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges, - LinkContext.CompileUnits, - CurrentUnit->getOrigUnit().getUnitDIE(), - LinkContext.DMO, *CurrentUnit, 0); - } - - // The calls to applyValidRelocs inside cloneDIE will walk the reloc - // array again (in the same way findValidRelocsInDebugInfo() did). We - // need to reset the NextValidReloc index to the beginning. - LinkContext.RelocMgr.resetValidRelocs(); - if (LinkContext.RelocMgr.hasValidRelocs() || LLVM_UNLIKELY(Options.Update)) - DIECloner(*this, LinkContext.RelocMgr, DIEAlloc, LinkContext.CompileUnits, - Options) - .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO, - LinkContext.Ranges, OffsetsStringPool, - LinkContext.DwarfContext->isLittleEndian()); - if (!Options.NoOutput && !LinkContext.CompileUnits.empty() && - LLVM_LIKELY(!Options.Update)) - patchFrameInfoForObject( - LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext, - LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize()); - - // Clean-up before starting working on the next object. - endDebugObject(LinkContext); - }; - - auto EmitLambda = [&]() { - // Emit everything that's global. - if (!Options.NoOutput) { - Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion); - Streamer->emitStrings(OffsetsStringPool); - switch (Options.TheAccelTableKind) { - case AccelTableKind::Apple: - Streamer->emitAppleNames(AppleNames); - Streamer->emitAppleNamespaces(AppleNamespaces); - Streamer->emitAppleTypes(AppleTypes); - Streamer->emitAppleObjc(AppleObjc); - break; - case AccelTableKind::Dwarf: - Streamer->emitDebugNames(DebugNames); - break; - case AccelTableKind::Default: - llvm_unreachable("Default should have already been resolved."); - break; - } - } - }; - - remarks::RemarkLinker RL; - if (!Options.RemarksPrependPath.empty()) - RL.setExternalFilePrependPath(Options.RemarksPrependPath); - auto RemarkLinkLambda = [&](size_t i) { - // Link remarks from one object file. - auto &LinkContext = ObjectContexts[i]; - if (const object::ObjectFile *Obj = LinkContext.ObjectFile) - if (Error E = RL.link(*Obj)) - return E; - return Error(Error::success()); - }; - - auto AnalyzeAll = [&]() { - for (unsigned i = 0, e = NumObjects; i != e; ++i) { - AnalyzeLambda(i); - - std::unique_lock LockGuard(ProcessedFilesMutex); - ProcessedFiles.set(i); - ProcessedFilesConditionVariable.notify_one(); - } - }; - - auto CloneAll = [&]() { - for (unsigned i = 0, e = NumObjects; i != e; ++i) { - { - std::unique_lock LockGuard(ProcessedFilesMutex); - if (!ProcessedFiles[i]) { - ProcessedFilesConditionVariable.wait( - LockGuard, [&]() { return ProcessedFiles[i]; }); - } - } - - CloneLambda(i); - } - EmitLambda(); - }; - - auto EmitRemarksLambda = [&]() { - StringRef ArchName = Map.getTriple().getArchName(); - return emitRemarks(Options, Map.getBinaryPath(), ArchName, RL); - }; - - // Instead of making error handling a lot more complicated using futures, - // write to one llvm::Error instance if something went wrong. - // We're assuming RemarkLinkAllError is alive longer than the thread - // executing RemarkLinkAll. - auto RemarkLinkAll = [&](Error &RemarkLinkAllError) { - // Allow assigning to the error only within the lambda. - ErrorAsOutParameter EAO(&RemarkLinkAllError); - for (unsigned i = 0, e = NumObjects; i != e; ++i) - if ((RemarkLinkAllError = RemarkLinkLambda(i))) - return; - - if ((RemarkLinkAllError = EmitRemarksLambda())) - return; - }; - - // To limit memory usage in the single threaded case, analyze and clone are - // run sequentially so the LinkContext is freed after processing each object - // in endDebugObject. - if (Options.Threads == 1) { - for (unsigned i = 0, e = NumObjects; i != e; ++i) { - AnalyzeLambda(i); - CloneLambda(i); - - if (Error E = RemarkLinkLambda(i)) - return error(toString(std::move(E))); - } - EmitLambda(); - - if (Error E = EmitRemarksLambda()) - return error(toString(std::move(E))); - - } else { - // This should not be constructed on the single-threaded path to avoid fatal - // errors from unchecked llvm::Error objects. - Error RemarkLinkAllError = Error::success(); - - ThreadPool pool(3); - pool.async(AnalyzeAll); - pool.async(CloneAll); - pool.async(RemarkLinkAll, std::ref(RemarkLinkAllError)); - pool.wait(); - - // Report errors from RemarkLinkAll, if any. - if (Error E = std::move(RemarkLinkAllError)) - return error(toString(std::move(E))); - } - - if (Options.NoOutput) - return true; - - if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { - StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); - if (auto E = - copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options)) - return error(toString(std::move(E))); - } - - return Streamer->finish(Map, Options.Translator); -} // namespace dsymutil - bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, const DebugMap &DM, LinkOptions Options) { DwarfLinker Linker(OutFile, BinHolder, std::move(Options)); Index: llvm/tools/dsymutil/DwarfStreamer.h =================================================================== --- llvm/tools/dsymutil/DwarfStreamer.h +++ llvm/tools/dsymutil/DwarfStreamer.h @@ -9,14 +9,14 @@ #ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H #define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H -#include "CompileUnit.h" #include "DebugMap.h" #include "LinkUtils.h" -#include "NonRelocatableStringpool.h" #include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" @@ -90,7 +90,11 @@ /// DW_AT_ranges attribute. void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection); - uint32_t getRangesSectionSize() const { return RangesSectionSize; } + void emitPaperTrailWarnings(const Triple &triple, DIE &die); + + uint64_t getRangesSectionSize() const { return RangesSectionSize; } + + uint64_t getDebugInfoSectionSize() const { return DebugInfoSectionSize; } /// 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 @@ -110,7 +114,7 @@ void translateLineTable(DataExtractor LineData, uint64_t Offset); /// Copy over the debug sections that are not modified when updating. - void copyInvariantDebugSection(const object::ObjectFile &Obj); + void emitInvariantSection(const object::ObjectFile &Obj, StringRef SecName); uint64_t getLineSectionSize() const { return LineSectionSize; } @@ -142,7 +146,7 @@ /// Emit Apple type accelerator table. void emitAppleTypes(AccelTable &Table); - uint32_t getFrameSectionSize() const { return FrameSectionSize; } + uint64_t getFrameSectionSize() const { return FrameSectionSize; } private: /// \defgroup MCObjects MC layer objects constructed by the streamer @@ -166,10 +170,11 @@ LinkOptions Options; - uint32_t RangesSectionSize; - uint32_t LocSectionSize; + uint64_t RangesSectionSize; + uint64_t LocSectionSize; uint64_t LineSectionSize; - uint32_t FrameSectionSize; + uint64_t FrameSectionSize; + uint64_t DebugInfoSectionSize; /// Keep track of emitted CUs and their Unique ID. struct EmittedUnit { Index: llvm/tools/dsymutil/DwarfStreamer.cpp =================================================================== --- llvm/tools/dsymutil/DwarfStreamer.cpp +++ llvm/tools/dsymutil/DwarfStreamer.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "DwarfStreamer.h" -#include "CompileUnit.h" #include "LinkUtils.h" #include "MachOUtils.h" #include "llvm/ADT/Triple.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFOptCompileUnit.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCTargetOptionsCommandFlags.inc" #include "llvm/Support/LEB128.h" @@ -123,6 +123,7 @@ LocSectionSize = 0; LineSectionSize = 0; FrameSectionSize = 0; + DebugInfoSectionSize = 0; return true; } @@ -169,6 +170,7 @@ // start of the section. Asm->emitInt32(0); Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize()); + DebugInfoSectionSize += 11; // Remember this CU. EmittedUnits.push_back({Unit.getUniqueID(), Unit.getLabelBegin()}); @@ -188,6 +190,19 @@ void DwarfStreamer::emitDIE(DIE &Die) { MS->SwitchSection(MOFI->getDwarfInfoSection()); Asm->emitDwarfDIE(Die); + + DebugInfoSectionSize += Die.getSize(); +} + +void DwarfStreamer::emitPaperTrailWarnings(const Triple &triple, DIE &die) { + switchToDebugInfoSection(/* Version */ 2); + auto &Asm = getAsmPrinter(); + Asm.emitInt32(11 + die.getSize() - 4); + Asm.emitInt16(2); + Asm.emitInt32(0); + Asm.emitInt8(triple.isArch64Bit() ? 8 : 4); + DebugInfoSectionSize += 11; + emitDIE(die); } /// Emit the debug_str section stored in \p Pool. @@ -690,23 +705,24 @@ } } -void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj) { - if (!Options.Translator) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); - emitSectionContents(Obj, "debug_line", MS); +void DwarfStreamer::emitInvariantSection(const object::ObjectFile &Obj, + StringRef SecName) { + + MCSection *section = + StringSwitch(SecName) + .Case("debug_line", MC->getObjectFileInfo()->getDwarfLineSection()) + .Case("debug_loc", MC->getObjectFileInfo()->getDwarfLocSection()) + .Case("debug_ranges", + MC->getObjectFileInfo()->getDwarfRangesSection()) + .Case("debug_frame", MC->getObjectFileInfo()->getDwarfFrameSection()) + .Case("debug_aranges", + MC->getObjectFileInfo()->getDwarfARangesSection()) + .Default(nullptr); + + if (section) { + MS->SwitchSection(section); + emitSectionContents(Obj, SecName, 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 Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- llvm/tools/dsymutil/LinkUtils.h +++ llvm/tools/dsymutil/LinkUtils.h @@ -15,6 +15,7 @@ #include "llvm/Remarks/RemarkFormat.h" #include "llvm/Support/WithColor.h" +#include "llvm/DebugInfo/DWARF/DWARFOptimizer.h" #include namespace llvm { @@ -25,13 +26,6 @@ Assembly, }; -/// The kind of accelerator tables we should emit. -enum class AccelTableKind { - Apple, ///< .apple_names, .apple_namespaces, .apple_types, .apple_objc. - Dwarf, ///< DWARF v5 .debug_names. - Default, ///< Dwarf for DWARF5 or later, Apple otherwise. -}; - struct LinkOptions { /// Verbosity bool Verbose = false; Index: llvm/tools/dsymutil/MachOUtils.cpp =================================================================== --- llvm/tools/dsymutil/MachOUtils.cpp +++ llvm/tools/dsymutil/MachOUtils.cpp @@ -10,7 +10,7 @@ #include "BinaryHolder.h" #include "DebugMap.h" #include "LinkUtils.h" -#include "NonRelocatableStringpool.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCMachObjectWriter.h" #include "llvm/MC/MCObjectStreamer.h" @@ -442,7 +442,10 @@ } SmallString<0> NewSymtab; - NonRelocatableStringpool NewStrings(Translator); + std::function TranslationLambda = [&](StringRef Input) { + return Translator(Input); + }; + NonRelocatableStringpool NewStrings(Translator ? TranslationLambda : nullptr); unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); unsigned NumSyms = 0; uint64_t NewStringsSize = 0;