#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"
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
/// 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) {
// 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);
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
: 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 &);
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.
///
/// @{
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
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()) {
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();
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();
}
}