[llvm] r231300 - [dsymutil] Add minimal code to emit DIE trees.

Frederic Riss friss at apple.com
Wed Mar 4 14:07:45 PST 2015


Author: friss
Date: Wed Mar  4 16:07:44 2015
New Revision: 231300

URL: http://llvm.org/viewvc/llvm-project?rev=231300&view=rev
Log:
[dsymutil] Add minimal code to emit DIE trees.

This commit adds code to emit DIE trees that have been pruned from the
parts that haven't been marked as kept in the previous pass.

It works by 'cloning' the input DIE tree (as read by libDebugInfoDwarf)
into a tree of DIE objects. Cloning the DIEs means essentially cloning
their attributes. The code in this commit does only handle scalar and
block attributes (scalar because they are trivial, blocks because they
can't be easily replaced by a scalr placeholder), all the other ones
are replaced by placeholder zero values and will be handled in
further commits.

The added tests mostly check that the DIE tree has the correct layout and
also verify that a few chosen scalar and block attributes correctly make
their way into the output.

Added:
    llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test
Modified:
    llvm/trunk/test/tools/dsymutil/X86/basic-linking-x86.test
    llvm/trunk/tools/dsymutil/DwarfLinker.cpp

Modified: llvm/trunk/test/tools/dsymutil/X86/basic-linking-x86.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/X86/basic-linking-x86.test?rev=231300&r1=231299&r2=231300&view=diff
==============================================================================
--- llvm/trunk/test/tools/dsymutil/X86/basic-linking-x86.test (original)
+++ llvm/trunk/test/tools/dsymutil/X86/basic-linking-x86.test Wed Mar  4 16:07:44 2015
@@ -4,5 +4,70 @@ RUN: llvm-dwarfdump %t1.dwarf | FileChec
 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
+

Added: llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test?rev=231300&view=auto
==============================================================================
--- llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test (added)
+++ llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test Wed Mar  4 16:07:44 2015
@@ -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

Modified: llvm/trunk/tools/dsymutil/DwarfLinker.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/DwarfLinker.cpp?rev=231300&r1=231299&r2=231300&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/DwarfLinker.cpp (original)
+++ llvm/trunk/tools/dsymutil/DwarfLinker.cpp Wed Mar  4 16:07:44 2015
@@ -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<DIEInfo> Info; ///< DIE info indexed by DIE index.
+  std::vector<DIEInfo> Info;  ///< DIE info indexed by DIE index.
+  std::unique_ptr<DIE> 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<DIEAbbrev *> &Abbrevs);
 };
 
 bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) {
@@ -151,8 +200,8 @@ bool DwarfStreamer::init(Triple TheTripl
 
   // Create the output file.
   std::error_code EC;
-  OutFile = llvm::make_unique<raw_fd_ostream>(OutputFilename, EC,
-                                              sys::fs::F_None);
+  OutFile =
+      llvm::make_unique<raw_fd_ostream>(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<DIEAbbrev *> &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<DIEAbbrev> 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<DIEAbbrev *> Abbreviations;
+
+  /// \brief DIELoc objects that need to be destructed (but not freed!).
+  std::vector<DIELoc *> DIELocs;
+  /// \brief DIEBlock objects that need to be destructed (but not freed!).
+  std::vector<DIEBlock *> DIEBlocks;
+  /// \brief Allocator used for all the DIEValue objects.
+  BumpPtrAllocator DIEAlloc;
+  /// @}
+
   /// \defgroup Helpers Various helper methods.
   ///
   /// @{
@@ -408,6 +564,15 @@ void DwarfLinker::startDebugObject(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(cons
     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<DIE *>(Loc) : static_cast<DIE *>(Block);
+  Value = Loc ? static_cast<DIEValue *>(Loc) : static_cast<DIEValue *>(Block);
+  ArrayRef<uint8_t> Bytes = *Val.getAsBlock();
+  for (auto Byte : Bytes)
+    Attr->addValue(static_cast<dwarf::Attribute>(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<dwarf::Tag>(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<DIE>(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 &M
   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 &M
       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();
 }
 }





More information about the llvm-commits mailing list