diff --git a/test/tools/dsymutil/X86/basic-linking-x86.test b/test/tools/dsymutil/X86/basic-linking-x86.test index 8f604001411..b45b104c42a 100644 --- a/test/tools/dsymutil/X86/basic-linking-x86.test +++ b/test/tools/dsymutil/X86/basic-linking-x86.test @@ -4,5 +4,70 @@ RUN: llvm-dwarfdump %t1.dwarf | FileCheck %s RUN: llvm-dsymutil -o %t2 -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 RUN: llvm-dwarfdump %t2 | FileCheck %s RUN: llvm-dsymutil -o - -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 | llvm-dwarfdump - | FileCheck %s +RUN: llvm-dsymutil -o - -oso-prepend-path=%p/.. %p/../Inputs/basic-archive.macho.x86_64 | llvm-dwarfdump - | FileCheck %s CHECK: file format Mach-O 64-bit x86-64 + +CHECK: debug_info contents + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99) +CHECK: DW_TAG_subprogram [2] * +CHECK: DW_AT_decl_line [DW_FORM_data1] (23) +CHECK: DW_AT_prototyped [DW_FORM_flag] (0x01) +CHECK: DW_AT_external [DW_FORM_flag] (0x01) +CHECK: DW_AT_accessibility [DW_FORM_data1] (DW_ACCESS_public) +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: DW_TAG_formal_parameter [3] +CHECK: DW_AT_decl_line [DW_FORM_data1] (23) +CHECK: DW_AT_location [DW_FORM_block1] (<0x02> 91 78 ) +CHECK: DW_TAG_formal_parameter [3] +CHECK: DW_AT_decl_file [DW_FORM_data1] (0x01) +CHECK: DW_AT_decl_line [DW_FORM_data1] (23) +CHECK: DW_AT_location [DW_FORM_block1] (<0x02> 91 70 ) +CHECK: NULL +CHECK: DW_TAG_base_type [4] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000000] = ) +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04) +CHECK: DW_TAG_pointer_type [5] +CHECK: DW_TAG_pointer_type [5] +CHECK: DW_TAG_const_type [6] +CHECK: DW_TAG_base_type [4] +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed_char) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x01) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_TAG_base_type [4] +CHECK: DW_TAG_variable [7] +CHECK: DW_TAG_variable [7] +CHECK: DW_TAG_subprogram [2] * +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: DW_TAG_formal_parameter [3] +CHECK: DW_AT_location [DW_FORM_block1] (<0x02> 91 7c ) +CHECK: NULL +CHECK: DW_TAG_subprogram [8] +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_TAG_variable [9] +CHECK: DW_TAG_volatile_type [10] +CHECK: DW_TAG_base_type [4] +CHECK: DW_TAG_subprogram [2] * +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: DW_TAG_formal_parameter [3] +CHECK: DW_AT_location [DW_FORM_block1] (<0x02> 91 78 ) +CHECK: NULL +CHECK: DW_TAG_subprogram [8] +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) + +CHECK: NULL + diff --git a/test/tools/dsymutil/X86/basic-lto-linking-x86.test b/test/tools/dsymutil/X86/basic-lto-linking-x86.test new file mode 100644 index 00000000000..a56f48cd60f --- /dev/null +++ b/test/tools/dsymutil/X86/basic-lto-linking-x86.test @@ -0,0 +1,61 @@ +RUN: llvm-dsymutil -o - -oso-prepend-path=%p/.. %p/../Inputs/basic-lto.macho.x86_64 | llvm-dwarfdump - | FileCheck %s + +CHECK: file format Mach-O 64-bit x86-64 + +CHECK: debug_info contents + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99) +CHECK: DW_TAG_subprogram [2] * +CHECK: DW_AT_decl_line [DW_FORM_data1] (23) +CHECK: DW_AT_prototyped [DW_FORM_flag] (0x01) +CHECK: DW_AT_external [DW_FORM_flag] (0x01) +CHECK: DW_AT_accessibility [DW_FORM_data1] (DW_ACCESS_public) +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: DW_TAG_formal_parameter [3] +CHECK: DW_AT_location [DW_FORM_block1] (<0x03> 55 93 04 ) +CHECK: DW_TAG_formal_parameter [3] +CHECK: DW_AT_location [DW_FORM_block1] (<0x01> 54 ) +CHECK: NULL +CHECK: DW_TAG_base_type [4 +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04) +CHECK: DW_TAG_pointer_type [5] +CHECK: DW_TAG_pointer_type [5] +CHECK: DW_TAG_const_type [6] +CHECK: DW_TAG_base_type [4] +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed_char) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x01) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_TAG_variable [7] +CHECK: DW_TAG_variable [7] +CHECK: DW_TAG_subprogram [8] * +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: DW_TAG_formal_parameter [9] +CHECK: DW_TAG_inlined_subroutine [10] +CHECK: DW_AT_call_line [DW_FORM_data1] (20) +CHECK: NULL +CHECK: DW_TAG_subprogram [11] +CHECK: DW_AT_inline [DW_FORM_data1] (DW_INL_inlined) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_TAG_variable [12] +CHECK: DW_TAG_volatile_type [13] +CHECK: DW_TAG_subprogram [8] * +CHECK: DW_AT_frame_base [DW_FORM_block1] (<0x01> 56 ) +CHECK: DW_TAG_formal_parameter [9] +CHECK: DW_TAG_lexical_block [14] * +CHECK: DW_TAG_inlined_subroutine [15] +CHECK: NULL +CHECK: NULL +CHECK: DW_TAG_subprogram [11] +CHECK: NULL diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index a30c2d85eac..7c2be163c75 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -11,6 +11,7 @@ #include "DebugMap.h" #include "dsymutil.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/DIE.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" @@ -65,14 +66,42 @@ public: DWARFUnit &getOrigUnit() const { return OrigUnit; } + DIE *getOutputUnitDIE() const { return CUDie.get(); } + void setOutputUnitDIE(DIE *Die) { CUDie.reset(Die); } + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } + uint64_t getStartOffset() const { return StartOffset; } + uint64_t getNextUnitOffset() const { return NextUnitOffset; } + + /// \brief Set the start and end offsets for this unit. Must be + /// called after the CU's DIEs have been cloned. The unit start + /// offset will be set to \p DebugInfoSize. + /// \returns the next unit offset (which is also the current + /// debug_info section size). + uint64_t computeOffsets(uint64_t DebugInfoSize); + private: DWARFUnit &OrigUnit; - std::vector Info; ///< DIE info indexed by DIE index. + std::vector Info; ///< DIE info indexed by DIE index. + std::unique_ptr CUDie; ///< Root of the linked DIE tree. + + uint64_t StartOffset; + uint64_t NextUnitOffset; }; +uint64_t CompileUnit::computeOffsets(uint64_t DebugInfoSize) { + StartOffset = DebugInfoSize; + NextUnitOffset = StartOffset + 11 /* Header size */; + // The root DIE might be null, meaning that the Unit had nothing to + // contribute to the linked output. In that case, we will emit the + // unit header without any actual DIE. + if (CUDie) + NextUnitOffset += CUDie->getSize(); + return NextUnitOffset; +} + /// \brief The Dwarf streaming logic /// /// All interactions with the MC layer that is used to build the debug @@ -103,8 +132,28 @@ public: /// more natural to handle errors through return value. bool init(Triple TheTriple, StringRef OutputFilename); - ///\brief Dump the file to the disk. + /// \brief Dump the file to the disk. bool finish(); + + AsmPrinter &getAsmPrinter() const { return *Asm; } + + /// \brief Set the current output section to debug_info and change + /// the MC Dwarf version to \p DwarfVersion. + void switchToDebugInfoSection(unsigned DwarfVersion); + + /// \brief 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); + + /// \brief Recursively emit the DIE tree rooted at \p Die. + void emitDIE(DIE &Die); + + /// \brief Emit the abbreviation table \p Abbrevs to the + /// debug_abbrev section. + void emitAbbrevs(const std::vector &Abbrevs); }; bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { @@ -151,8 +200,8 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { // Create the output file. std::error_code EC; - OutFile = llvm::make_unique(OutputFilename, EC, - sys::fs::F_None); + OutFile = + llvm::make_unique(OutputFilename, EC, sys::fs::F_None); if (EC) return error(Twine(OutputFilename) + ": " + EC.message(), Context); @@ -178,6 +227,51 @@ bool DwarfStreamer::finish() { return true; } +/// \brief Set the current output section to debug_info and change +/// the MC Dwarf version to \p DwarfVersion. +void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + MC->setDwarfVersion(DwarfVersion); +} + +/// \brief Emit the compilation unit header for \p Unit in the +/// debug_info section. +/// +/// A Dwarf scetion header is encoded as: +/// uint32_t Unit length (omiting this field) +/// uint16_t Version +/// uint32_t Abbreviation table offset +/// uint8_t Address size +/// +/// Leading to a total of 11 bytes. +void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { + unsigned Version = Unit.getOrigUnit().getVersion(); + switchToDebugInfoSection(Version); + + // Emit size of content not including length itself. The size has + // already been computed in CompileUnit::computeOffsets(). Substract + // 4 to that size to account for the length field. + Asm->EmitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4); + Asm->EmitInt16(Version); + // We share one abbreviations table across all units so it's always at the + // start of the section. + Asm->EmitInt32(0); + Asm->EmitInt8(Unit.getOrigUnit().getAddressByteSize()); +} + +/// \brief Emit the \p Abbrevs array as the shared abbreviation table +/// for the linked Dwarf file. +void DwarfStreamer::emitAbbrevs(const std::vector &Abbrevs) { + MS->SwitchSection(MOFI->getDwarfAbbrevSection()); + Asm->emitDwarfAbbrevs(Abbrevs); +} + +/// \brief Recursively emit the DIE tree rooted at \p Die. +void DwarfStreamer::emitDIE(DIE &Die) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + Asm->emitDwarfDIE(Die); +} + /// \brief The core of the Dwarf linking logic. /// /// The link of the dwarf information from the object files will be @@ -198,6 +292,11 @@ public: : OutputFilename(OutputFilename), Options(Options), BinHolder(Options.Verbose) {} + ~DwarfLinker() { + for (auto *Abbrev : Abbreviations) + delete Abbrev; + } + /// \brief Link the contents of the DebugMap. bool link(const DebugMap &); @@ -289,6 +388,63 @@ private: CompileUnit::DIEInfo &Info); /// @} + /// \defgroup Linking Methods used to link the debug information + /// + /// @{ + /// \brief 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. + /// + /// \returns the root of the cloned tree. + DIE *cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &U, + uint32_t OutOffset); + + typedef DWARFAbbreviationDeclaration::AttributeSpec AttributeSpec; + + /// \brief Helper for cloneDIE. + unsigned cloneAttribute(DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, + CompileUnit &U, const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize); + + /// \brief Helper for cloneDIE. + unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec); + + /// \brief Helper for cloneDIE. + unsigned cloneDieReferenceAttribute(DIE &Die, AttributeSpec AttrSpec, + unsigned AttrSize); + + /// \brief Helper for cloneDIE. + unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize); + + /// \brief Helper for cloneDIE. + unsigned cloneScalarAttribute(DIE &Die, + const DWARFDebugInfoEntryMinimal &InputDIE, + const DWARFUnit &U, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize); + + /// \brief Assign an abbreviation number to \p Abbrev + void AssignAbbrev(DIEAbbrev &Abbrev); + + /// \brief FoldingSet that uniques the abbreviations. + FoldingSet AbbreviationsSet; + /// \brief Storage for the unique Abbreviations. + /// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot + /// be changed to a vecot of unique_ptrs. + std::vector Abbreviations; + + /// \brief DIELoc objects that need to be destructed (but not freed!). + std::vector DIELocs; + /// \brief DIEBlock objects that need to be destructed (but not freed!). + std::vector DIEBlocks; + /// \brief Allocator used for all the DIEValue objects. + BumpPtrAllocator DIEAlloc; + /// @} + /// \defgroup Helpers Various helper methods. /// /// @{ @@ -408,6 +564,15 @@ void DwarfLinker::startDebugObject(DWARFContext &Dwarf) { void DwarfLinker::endDebugObject() { Units.clear(); ValidRelocs.clear(); + + for (auto *Block : DIEBlocks) + Block->~DIEBlock(); + for (auto *Loc : DIELocs) + Loc->~DIELoc(); + + DIEBlocks.clear(); + DIELocs.clear(); + DIEAlloc.Reset(); } /// \brief Iterate over the relocations of the given \p Section and @@ -740,6 +905,236 @@ void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE, lookForDIEsToKeep(*Child, DMO, CU, Flags); } +/// \brief Assign an abbreviation numer 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( + new DIEAbbrev(Abbrev.getTag(), Abbrev.hasChildren())); + for (const auto &Attr : Abbrev.getData()) + Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm()); + AbbreviationsSet.InsertNode(Abbreviations.back(), InsertToken); + // Assign the unique abbreviation number. + Abbrev.setNumber(Abbreviations.size()); + Abbreviations.back()->setNumber(Abbreviations.size()); + } +} + +/// \brief Clone a string attribute described by \p AttrSpec and add +/// it to \p Die. +/// \returns the size of the new attribute. +unsigned DwarfLinker::cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec) { + // Switch everything to out of line strings. + // FIXME: Construct the actual string table. + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, + new (DIEAlloc) DIEInteger(0)); + return 4; +} + +/// \brief Clone an attribute referencing another DIE and add +/// it to \p Die. +/// \returns the size of the new attribute. +unsigned DwarfLinker::cloneDieReferenceAttribute(DIE &Die, + AttributeSpec AttrSpec, + unsigned AttrSize) { + // FIXME: Handle DIE references. + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), + new (DIEAlloc) DIEInteger(0)); + return AttrSize; +} + +/// \brief Clone an attribute of block form (locations, constants) and add +/// it to \p Die. +/// \returns the size of the new attribute. +unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, + unsigned AttrSize) { + DIE *Attr; + DIEValue *Value; + DIELoc *Loc = nullptr; + DIEBlock *Block = nullptr; + // Just copy the block data over. + if (AttrSpec.Attr == dwarf::DW_FORM_exprloc) { + Loc = new (DIEAlloc) DIELoc(); + DIELocs.push_back(Loc); + } else { + Block = new (DIEAlloc) DIEBlock(); + DIEBlocks.push_back(Block); + } + Attr = Loc ? static_cast(Loc) : static_cast(Block); + Value = Loc ? static_cast(Loc) : static_cast(Block); + ArrayRef Bytes = *Val.getAsBlock(); + for (auto Byte : Bytes) + Attr->addValue(static_cast(0), dwarf::DW_FORM_data1, + new (DIEAlloc) 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 (Streamer) { + if (Loc) + Loc->ComputeSize(&Streamer->getAsmPrinter()); + else + Block->ComputeSize(&Streamer->getAsmPrinter()); + } + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), + Value); + return AttrSize; +} + +/// \brief Clone a scalar attribute and add it to \p Die. +/// \returns the size of the new attribute. +unsigned DwarfLinker::cloneScalarAttribute( + DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, const DWARFUnit &U, + AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize) { + uint64_t Value; + if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) + Value = *Val.getAsSectionOffset(); + else if (AttrSpec.Form == dwarf::DW_FORM_sdata) + Value = *Val.getAsSignedConstant(); + else if (AttrSpec.Form == dwarf::DW_FORM_addr) + Value = *Val.getAsAddress(&U); + else if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else { + reportWarning("Unsupported scalar attribute form. Dropping attribute.", &U, + &InputDIE); + return 0; + } + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), + new (DIEAlloc) DIEInteger(Value)); + return AttrSize; +} + +/// \brief 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::cloneAttribute(DIE &Die, + const DWARFDebugInfoEntryMinimal &InputDIE, + CompileUnit &Unit, + const DWARFFormValue &Val, + const AttributeSpec AttrSpec, + unsigned AttrSize) { + const DWARFUnit &U = Unit.getOrigUnit(); + + switch (AttrSpec.Form) { + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_string: + return cloneStringAttribute(Die, AttrSpec); + case dwarf::DW_FORM_ref_addr: + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + return cloneDieReferenceAttribute(Die, AttrSpec, AttrSize); + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + case dwarf::DW_FORM_exprloc: + return cloneBlockAttribute(Die, AttrSpec, Val, AttrSize); + case dwarf::DW_FORM_addr: + case dwarf::DW_FORM_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, U, AttrSpec, Val, AttrSize); + default: + reportWarning("Unsupported attribute form in cloneAttribute. Dropping.", &U, + &InputDIE); + } + + return 0; +} + +/// \brief Recursively clone \p InputDIE's subtrees that have been +/// selected to appear in the linked output. +/// +/// \param OutOffset is the Offset where the newly created DIE will +/// lie in the linked compile unit. +/// +/// \returns the cloned DIE object or null if nothing was selected. +DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, + CompileUnit &Unit, uint32_t OutOffset) { + DWARFUnit &U = Unit.getOrigUnit(); + unsigned Idx = U.getDIEIndex(&InputDIE); + + // Should the DIE appear in the output? + if (!Unit.getInfo(Idx).Keep) + return nullptr; + + uint32_t Offset = InputDIE.getOffset(); + + DIE *Die = new DIE(static_cast(InputDIE.getTag())); + Die->setOffset(OutOffset); + + // Extract and clone every attribute. + DataExtractor Data = U.getDebugInfoExtractor(); + const auto *Abbrev = InputDIE.getAbbreviationDeclarationPtr(); + Offset += getULEB128Size(Abbrev->getCode()); + + for (const auto &AttrSpec : Abbrev->attributes()) { + DWARFFormValue Val(AttrSpec.Form); + uint32_t AttrSize = Offset; + Val.extractValue(Data, &Offset, &U); + AttrSize = Offset - AttrSize; + + OutOffset += cloneAttribute(*Die, InputDIE, Unit, Val, AttrSpec, AttrSize); + } + + DIEAbbrev &NewAbbrev = Die->getAbbrev(); + // If a scope DIE is kept, we must have kept at least one child. If + // it's not the case, we'll just be emitting one wasteful end of + // children marker, but things won't break. + if (InputDIE.hasChildren()) + NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); + // Assign a permanent abbrev number + AssignAbbrev(Die->getAbbrev()); + + // Add the size of the abbreviation number to the output offset. + OutOffset += getULEB128Size(Die->getAbbrevNumber()); + + if (!Abbrev->hasChildren()) { + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; + } + + // Recursively clone children. + for (auto *Child = InputDIE.getFirstChild(); Child && !Child->isNULL(); + Child = Child->getSibling()) { + if (DIE *Clone = cloneDIE(*Child, Unit, OutOffset)) { + Die->addChild(std::unique_ptr(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; +} + bool DwarfLinker::link(const DebugMap &Map) { if (Map.begin() == Map.end()) { @@ -750,6 +1145,9 @@ bool DwarfLinker::link(const DebugMap &Map) { if (!createStreamer(Map.getTriple(), OutputFilename)) return false; + // Size of the DIEs (and headers) generated for the linked output. + uint64_t OutputDebugInfoSize = 0; + for (const auto &Obj : Map.objects()) { CurrentDebugObject = Obj.get(); @@ -793,10 +1191,35 @@ bool DwarfLinker::link(const DebugMap &Map) { lookForDIEsToKeep(*CurrentUnit.getOrigUnit().getCompileUnitDIE(), *Obj, CurrentUnit, 0); + // 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. + if (!ValidRelocs.empty()) + for (auto &CurrentUnit : Units) { + const auto *InputDIE = CurrentUnit.getOrigUnit().getCompileUnitDIE(); + DIE *OutputDIE = + cloneDIE(*InputDIE, CurrentUnit, 11 /* Unit Header size */); + CurrentUnit.setOutputUnitDIE(OutputDIE); + OutputDebugInfoSize = CurrentUnit.computeOffsets(OutputDebugInfoSize); + } + + // Emit all the compile unit's debug information. + if (!ValidRelocs.empty() && !Options.NoOutput) + for (auto &CurrentUnit : Units) { + Streamer->emitCompileUnitHeader(CurrentUnit); + if (!CurrentUnit.getOutputUnitDIE()) + continue; + Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE()); + } + // Clean-up before starting working on the next object. endDebugObject(); } + // Emit everything that's global. + if (!Options.NoOutput) + Streamer->emitAbbrevs(Abbreviations); + return Options.NoOutput ? true : Streamer->finish(); } }