2014-12-12 18:31:24 +01:00
|
|
|
//===- tools/dsymutil/DwarfLinker.cpp - Dwarf debug info linker -----------===//
|
|
|
|
//
|
|
|
|
// The LLVM Linker
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DebugMap.h"
|
2015-01-28 19:27:01 +01:00
|
|
|
#include "BinaryHolder.h"
|
|
|
|
#include "DebugMap.h"
|
2014-12-12 18:31:24 +01:00
|
|
|
#include "dsymutil.h"
|
2015-01-30 19:07:45 +01:00
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h"
|
2015-02-14 00:18:29 +01:00
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
2015-02-14 00:18:22 +01:00
|
|
|
#include "llvm/Object/MachO.h"
|
2015-02-14 00:18:34 +01:00
|
|
|
#include "llvm/Support/Dwarf.h"
|
|
|
|
#include "llvm/Support/LEB128.h"
|
2015-01-28 19:27:01 +01:00
|
|
|
#include <string>
|
2014-12-12 18:31:24 +01:00
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
namespace dsymutil {
|
|
|
|
|
2015-01-28 19:27:01 +01:00
|
|
|
namespace {
|
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
/// \brief Stores all information relating to a compile unit, be it in
|
|
|
|
/// its original instance in the object file to its brand new cloned
|
|
|
|
/// and linked DIE tree.
|
|
|
|
class CompileUnit {
|
|
|
|
public:
|
|
|
|
/// \brief Information gathered about a DIE in the object file.
|
|
|
|
struct DIEInfo {
|
2015-02-14 00:18:34 +01:00
|
|
|
uint64_t Address; ///< Linked address of the described entity.
|
|
|
|
uint32_t ParentIdx; ///< The index of this DIE's parent.
|
|
|
|
bool Keep; ///< Is the DIE part of the linked output?
|
|
|
|
bool InDebugMap; ///< Was this DIE's entity found in the map?
|
2015-01-28 23:15:14 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
CompileUnit(DWARFUnit &OrigUnit) : OrigUnit(OrigUnit) {
|
|
|
|
Info.resize(OrigUnit.getNumDIEs());
|
|
|
|
}
|
|
|
|
|
2015-02-14 00:18:27 +01:00
|
|
|
DWARFUnit &getOrigUnit() const { return OrigUnit; }
|
2015-01-28 23:15:14 +01:00
|
|
|
|
|
|
|
DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; }
|
|
|
|
const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
DWARFUnit &OrigUnit;
|
|
|
|
std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index.
|
|
|
|
};
|
|
|
|
|
2015-01-28 19:27:01 +01:00
|
|
|
/// \brief The core of the Dwarf linking logic.
|
2015-02-14 00:18:22 +01:00
|
|
|
///
|
|
|
|
/// 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.
|
2015-01-28 19:27:01 +01:00
|
|
|
class DwarfLinker {
|
|
|
|
public:
|
|
|
|
DwarfLinker(StringRef OutputFilename, bool Verbose)
|
|
|
|
: OutputFilename(OutputFilename), Verbose(Verbose), BinHolder(Verbose) {}
|
|
|
|
|
|
|
|
/// \brief Link the contents of the DebugMap.
|
|
|
|
bool link(const DebugMap &);
|
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
private:
|
|
|
|
/// \brief Called at the start of a debug object link.
|
|
|
|
void startDebugObject(DWARFContext &);
|
|
|
|
|
|
|
|
/// \brief Called at the end of a debug object link.
|
|
|
|
void endDebugObject();
|
|
|
|
|
2015-02-14 00:18:22 +01:00
|
|
|
/// \defgroup FindValidRelocations Translate debug map into a list
|
|
|
|
/// of relevant relocations
|
|
|
|
///
|
|
|
|
/// @{
|
|
|
|
struct ValidReloc {
|
|
|
|
uint32_t Offset;
|
|
|
|
uint32_t Size;
|
|
|
|
uint64_t Addend;
|
|
|
|
const DebugMapObject::DebugMapEntry *Mapping;
|
|
|
|
|
|
|
|
ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend,
|
|
|
|
const DebugMapObject::DebugMapEntry *Mapping)
|
|
|
|
: Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {}
|
|
|
|
|
|
|
|
bool operator<(const ValidReloc &RHS) const { return Offset < RHS.Offset; }
|
|
|
|
};
|
|
|
|
|
|
|
|
/// \brief The valid relocations for the current DebugMapObject.
|
|
|
|
/// This vector is sorted by relocation offset.
|
|
|
|
std::vector<ValidReloc> ValidRelocs;
|
|
|
|
|
|
|
|
/// \brief Index into ValidRelocs of the next relocation to
|
|
|
|
/// consider. As we walk the DIEs in acsending file offset and as
|
|
|
|
/// ValidRelocs is sorted by file offset, keeping this index
|
|
|
|
/// uptodate is all we have to do to have a cheap lookup during the
|
|
|
|
/// root DIE selection.
|
|
|
|
unsigned NextValidReloc;
|
|
|
|
|
|
|
|
bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
|
|
|
|
const DebugMapObject &DMO);
|
|
|
|
|
|
|
|
bool findValidRelocs(const object::SectionRef &Section,
|
|
|
|
const object::ObjectFile &Obj,
|
|
|
|
const DebugMapObject &DMO);
|
|
|
|
|
|
|
|
void findValidRelocsMachO(const object::SectionRef &Section,
|
|
|
|
const object::MachOObjectFile &Obj,
|
|
|
|
const DebugMapObject &DMO);
|
|
|
|
/// @}
|
2015-02-14 00:18:29 +01:00
|
|
|
|
2015-02-14 00:18:34 +01:00
|
|
|
/// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries.
|
|
|
|
///
|
|
|
|
/// @{
|
|
|
|
/// \brief Recursively walk the \p DIE tree and look for DIEs to
|
|
|
|
/// keep. Store that information in \p CU's DIEInfo.
|
|
|
|
void lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
const DebugMapObject &DMO, CompileUnit &CU,
|
|
|
|
unsigned Flags);
|
|
|
|
|
|
|
|
/// \brief Flags passed to DwarfLinker::lookForDIEsToKeep
|
|
|
|
enum TravesalFlags {
|
|
|
|
TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept.
|
|
|
|
TF_InFunctionScope = 1 << 1, ///< Current scope is a fucntion scope.
|
|
|
|
TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE.
|
|
|
|
TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE.
|
|
|
|
};
|
|
|
|
|
|
|
|
/// \brief Mark the passed DIE as well as all the ones it depends on
|
|
|
|
/// as kept.
|
|
|
|
void keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit::DIEInfo &MyInfo,
|
|
|
|
const DebugMapObject &DMO, CompileUnit &CU,
|
|
|
|
unsigned Flags);
|
|
|
|
|
|
|
|
unsigned shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo,
|
|
|
|
unsigned Flags);
|
|
|
|
|
|
|
|
unsigned shouldKeepVariableDIE(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit &Unit,
|
|
|
|
CompileUnit::DIEInfo &MyInfo, unsigned Flags);
|
|
|
|
|
|
|
|
unsigned shouldKeepSubprogramDIE(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit &Unit,
|
|
|
|
CompileUnit::DIEInfo &MyInfo,
|
|
|
|
unsigned Flags);
|
|
|
|
|
|
|
|
bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
|
|
|
|
CompileUnit::DIEInfo &Info);
|
|
|
|
/// @}
|
|
|
|
|
2015-02-14 00:18:29 +01:00
|
|
|
/// \defgroup Helpers Various helper methods.
|
|
|
|
///
|
|
|
|
/// @{
|
|
|
|
const DWARFDebugInfoEntryMinimal *
|
|
|
|
resolveDIEReference(DWARFFormValue &RefValue, const DWARFUnit &Unit,
|
|
|
|
const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit *&ReferencedCU);
|
|
|
|
|
|
|
|
CompileUnit *getUnitForOffset(unsigned Offset);
|
|
|
|
|
|
|
|
void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr,
|
|
|
|
const DWARFDebugInfoEntryMinimal *DIE = nullptr);
|
|
|
|
/// @}
|
|
|
|
|
2015-01-28 19:27:01 +01:00
|
|
|
private:
|
|
|
|
std::string OutputFilename;
|
|
|
|
bool Verbose;
|
|
|
|
BinaryHolder BinHolder;
|
2015-01-28 23:15:14 +01:00
|
|
|
|
|
|
|
/// The units of the current debug map object.
|
|
|
|
std::vector<CompileUnit> Units;
|
2015-02-14 00:18:29 +01:00
|
|
|
|
|
|
|
/// The debug map object curently under consideration.
|
|
|
|
DebugMapObject *CurrentDebugObject;
|
2015-01-28 19:27:01 +01:00
|
|
|
};
|
|
|
|
|
2015-02-14 00:18:29 +01:00
|
|
|
/// \brief Similar to DWARFUnitSection::getUnitForOffset(), but
|
|
|
|
/// returning our CompileUnit object instead.
|
|
|
|
CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) {
|
|
|
|
auto CU =
|
|
|
|
std::upper_bound(Units.begin(), Units.end(), Offset,
|
|
|
|
[](uint32_t LHS, const CompileUnit &RHS) {
|
|
|
|
return LHS < RHS.getOrigUnit().getNextUnitOffset();
|
|
|
|
});
|
|
|
|
return CU != Units.end() ? &*CU : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Resolve the DIE attribute reference that has been
|
|
|
|
/// extracted in \p RefValue. The resulting DIE migh be in another
|
|
|
|
/// CompileUnit which is stored into \p ReferencedCU.
|
|
|
|
/// \returns null if resolving fails for any reason.
|
|
|
|
const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference(
|
|
|
|
DWARFFormValue &RefValue, const DWARFUnit &Unit,
|
|
|
|
const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&RefCU) {
|
|
|
|
assert(RefValue.isFormClass(DWARFFormValue::FC_Reference));
|
|
|
|
uint64_t RefOffset = *RefValue.getAsReference(&Unit);
|
|
|
|
|
|
|
|
if ((RefCU = getUnitForOffset(RefOffset)))
|
|
|
|
if (const auto *RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset))
|
|
|
|
return RefDie;
|
|
|
|
|
|
|
|
reportWarning("could not find referenced DIE", &Unit, &DIE);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Report a warning to the user, optionaly including
|
|
|
|
/// information about a specific \p DIE related to the warning.
|
|
|
|
void DwarfLinker::reportWarning(const Twine &Warning, const DWARFUnit *Unit,
|
|
|
|
const DWARFDebugInfoEntryMinimal *DIE) {
|
|
|
|
if (CurrentDebugObject)
|
|
|
|
errs() << Twine("while processing ") +
|
|
|
|
CurrentDebugObject->getObjectFilename() + ":\n";
|
|
|
|
errs() << Twine("warning: ") + Warning + "\n";
|
|
|
|
|
|
|
|
if (!Verbose || !DIE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
errs() << " in DIE:\n";
|
|
|
|
DIE->dump(errs(), const_cast<DWARFUnit *>(Unit), 0 /* RecurseDepth */,
|
|
|
|
6 /* Indent */);
|
|
|
|
}
|
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
/// \brief Recursive helper to gather the child->parent relationships in the
|
|
|
|
/// original compile unit.
|
2015-02-14 00:18:31 +01:00
|
|
|
static void gatherDIEParents(const DWARFDebugInfoEntryMinimal *DIE,
|
|
|
|
unsigned ParentIdx, CompileUnit &CU) {
|
2015-01-28 23:15:14 +01:00
|
|
|
unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE);
|
|
|
|
CU.getInfo(MyIdx).ParentIdx = ParentIdx;
|
|
|
|
|
|
|
|
if (DIE->hasChildren())
|
|
|
|
for (auto *Child = DIE->getFirstChild(); Child && !Child->isNULL();
|
|
|
|
Child = Child->getSibling())
|
2015-02-14 00:18:31 +01:00
|
|
|
gatherDIEParents(Child, MyIdx, CU);
|
2015-01-28 23:15:14 +01:00
|
|
|
}
|
|
|
|
|
2015-02-14 00:18:34 +01:00
|
|
|
static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) {
|
|
|
|
switch (Tag) {
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
case dwarf::DW_TAG_subprogram:
|
|
|
|
case dwarf::DW_TAG_lexical_block:
|
|
|
|
case dwarf::DW_TAG_subroutine_type:
|
|
|
|
case dwarf::DW_TAG_structure_type:
|
|
|
|
case dwarf::DW_TAG_class_type:
|
|
|
|
case dwarf::DW_TAG_union_type:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
llvm_unreachable("Invalid Tag");
|
|
|
|
}
|
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
void DwarfLinker::startDebugObject(DWARFContext &Dwarf) {
|
|
|
|
Units.reserve(Dwarf.getNumCompileUnits());
|
2015-02-14 00:18:22 +01:00
|
|
|
NextValidReloc = 0;
|
2015-01-28 23:15:14 +01:00
|
|
|
}
|
|
|
|
|
2015-02-14 00:18:22 +01:00
|
|
|
void DwarfLinker::endDebugObject() {
|
|
|
|
Units.clear();
|
|
|
|
ValidRelocs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief 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::findValidRelocsMachO(const object::SectionRef &Section,
|
|
|
|
const object::MachOObjectFile &Obj,
|
|
|
|
const DebugMapObject &DMO) {
|
|
|
|
StringRef Contents;
|
|
|
|
Section.getContents(Contents);
|
|
|
|
DataExtractor Data(Contents, Obj.isLittleEndian(), 0);
|
|
|
|
|
|
|
|
for (const object::RelocationRef &Reloc : Section.relocations()) {
|
|
|
|
object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl();
|
|
|
|
MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef);
|
|
|
|
unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc);
|
|
|
|
uint64_t Offset64;
|
|
|
|
if ((RelocSize != 4 && RelocSize != 8) || Reloc.getOffset(Offset64)) {
|
2015-02-14 00:18:29 +01:00
|
|
|
reportWarning(" unsupported relocation in debug_info section.");
|
2015-02-14 00:18:22 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
uint32_t Offset = Offset64;
|
|
|
|
// Mach-o uses REL relocations, the addend is at the relocation offset.
|
|
|
|
uint64_t Addend = Data.getUnsigned(&Offset, RelocSize);
|
|
|
|
|
|
|
|
auto Sym = Reloc.getSymbol();
|
|
|
|
if (Sym != Obj.symbol_end()) {
|
|
|
|
StringRef SymbolName;
|
|
|
|
if (Sym->getName(SymbolName)) {
|
2015-02-14 00:18:29 +01:00
|
|
|
reportWarning("error getting relocation symbol name.");
|
2015-02-14 00:18:22 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (const auto *Mapping = DMO.lookupSymbol(SymbolName))
|
|
|
|
ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping);
|
|
|
|
} else if (const auto *Mapping = DMO.lookupObjectAddress(Addend)) {
|
|
|
|
// Do not store the addend. The addend was the address of the
|
|
|
|
// symbol in the object file, the address in the binary that is
|
|
|
|
// stored in the debug map doesn't need to be offseted.
|
|
|
|
ValidRelocs.emplace_back(Offset64, RelocSize, 0, Mapping);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Dispatch the valid relocation finding logic to the
|
|
|
|
/// appropriate handler depending on the object file format.
|
|
|
|
bool DwarfLinker::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<object::MachOObjectFile>(&Obj))
|
|
|
|
findValidRelocsMachO(Section, *MachOObj, DMO);
|
|
|
|
else
|
2015-02-14 00:18:29 +01:00
|
|
|
reportWarning(Twine("unsupported object file type: ") + Obj.getFileName());
|
2015-02-14 00:18:22 +01:00
|
|
|
|
|
|
|
if (ValidRelocs.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Sort the relocations by offset. We will walk the DIEs linearly in
|
|
|
|
// the file, this allows us to just keep an index in the relocation
|
|
|
|
// array that we advance during our walk, rather than resorting to
|
|
|
|
// some associative container. See DwarfLinker::NextValidReloc.
|
|
|
|
std::sort(ValidRelocs.begin(), ValidRelocs.end());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Look for relocations in the debug_info section that match
|
|
|
|
/// entries in the debug map. These relocations will drive the Dwarf
|
|
|
|
/// link by indicating which DIEs refer to symbols present in the
|
|
|
|
/// linked binary.
|
|
|
|
/// \returns wether there are any valid relocations in the debug info.
|
|
|
|
bool DwarfLinker::findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
|
|
|
|
const DebugMapObject &DMO) {
|
|
|
|
// Find the debug_info section.
|
|
|
|
for (const object::SectionRef &Section : Obj.sections()) {
|
|
|
|
StringRef SectionName;
|
|
|
|
Section.getName(SectionName);
|
|
|
|
SectionName = SectionName.substr(SectionName.find_first_not_of("._"));
|
|
|
|
if (SectionName != "debug_info")
|
|
|
|
continue;
|
|
|
|
return findValidRelocs(Section, Obj, DMO);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2015-01-28 23:15:14 +01:00
|
|
|
|
2015-02-14 00:18:34 +01:00
|
|
|
/// \brief Checks that there is a relocation against an actual debug
|
|
|
|
/// map entry 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.
|
|
|
|
bool DwarfLinker::hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
|
|
|
|
CompileUnit::DIEInfo &Info) {
|
|
|
|
assert(NextValidReloc == 0 ||
|
|
|
|
StartOffset > ValidRelocs[NextValidReloc - 1].Offset);
|
|
|
|
if (NextValidReloc >= ValidRelocs.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
uint64_t RelocOffset = ValidRelocs[NextValidReloc].Offset;
|
|
|
|
|
|
|
|
// We might need to skip some relocs that we didn't consider. For
|
|
|
|
// example the high_pc of a discarded DIE might contain a reloc that
|
|
|
|
// is in the list because it actually corresponds to the start of a
|
|
|
|
// function that is in the debug map.
|
|
|
|
while (RelocOffset < StartOffset && NextValidReloc < ValidRelocs.size() - 1)
|
|
|
|
RelocOffset = ValidRelocs[++NextValidReloc].Offset;
|
|
|
|
|
|
|
|
if (RelocOffset < StartOffset || RelocOffset >= EndOffset)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto &ValidReloc = ValidRelocs[NextValidReloc++];
|
|
|
|
if (Verbose)
|
|
|
|
outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey()
|
|
|
|
<< " " << format("\t%016" PRIx64 " => %016" PRIx64,
|
|
|
|
ValidReloc.Mapping->getValue().ObjectAddress,
|
|
|
|
ValidReloc.Mapping->getValue().BinaryAddress);
|
|
|
|
|
|
|
|
Info.Address =
|
|
|
|
ValidReloc.Mapping->getValue().BinaryAddress + ValidReloc.Addend;
|
|
|
|
Info.InDebugMap = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief 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<uint32_t, uint32_t>
|
|
|
|
getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx,
|
|
|
|
unsigned Offset, const DWARFUnit &Unit) {
|
|
|
|
DataExtractor Data = Unit.getDebugInfoExtractor();
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < Idx; ++i)
|
|
|
|
DWARFFormValue::skipValue(Abbrev->getFormByIndex(i), Data, &Offset, &Unit);
|
|
|
|
|
|
|
|
uint32_t End = Offset;
|
|
|
|
DWARFFormValue::skipValue(Abbrev->getFormByIndex(Idx), Data, &End, &Unit);
|
|
|
|
|
|
|
|
return std::make_pair(Offset, End);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Check if a variable describing DIE should be kept.
|
|
|
|
/// \returns updated TraversalFlags.
|
|
|
|
unsigned DwarfLinker::shouldKeepVariableDIE(
|
|
|
|
const DWARFDebugInfoEntryMinimal &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) != -1U) {
|
|
|
|
MyInfo.InDebugMap = true;
|
|
|
|
return Flags | TF_Keep;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t LocationIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_location);
|
|
|
|
if (LocationIdx == -1U)
|
|
|
|
return Flags;
|
|
|
|
|
|
|
|
uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
|
|
|
|
const DWARFUnit &OrigUnit = Unit.getOrigUnit();
|
|
|
|
uint32_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 in 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 (!hasValidRelocation(LocationOffset, LocationEndOffset, MyInfo) ||
|
|
|
|
(Flags & TF_InFunctionScope))
|
|
|
|
return Flags;
|
|
|
|
|
|
|
|
if (Verbose)
|
|
|
|
DIE.dump(outs(), const_cast<DWARFUnit *>(&OrigUnit), 0, 8 /* Indent */);
|
|
|
|
|
|
|
|
return Flags | TF_Keep;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Check if a function describing DIE should be kept.
|
|
|
|
/// \returns updated TraversalFlags.
|
|
|
|
unsigned DwarfLinker::shouldKeepSubprogramDIE(
|
|
|
|
const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit,
|
|
|
|
CompileUnit::DIEInfo &MyInfo, unsigned Flags) {
|
|
|
|
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
|
|
|
|
|
|
|
|
Flags |= TF_InFunctionScope;
|
|
|
|
|
|
|
|
uint32_t LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc);
|
|
|
|
if (LowPcIdx == -1U)
|
|
|
|
return Flags;
|
|
|
|
|
|
|
|
uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
|
|
|
|
const DWARFUnit &OrigUnit = Unit.getOrigUnit();
|
|
|
|
uint32_t LowPcOffset, LowPcEndOffset;
|
|
|
|
std::tie(LowPcOffset, LowPcEndOffset) =
|
|
|
|
getAttributeOffsets(Abbrev, LowPcIdx, Offset, OrigUnit);
|
|
|
|
|
|
|
|
uint64_t LowPc =
|
|
|
|
DIE.getAttributeValueAsAddress(&OrigUnit, dwarf::DW_AT_low_pc, -1ULL);
|
|
|
|
assert(LowPc != -1ULL && "low_pc attribute is not an address.");
|
|
|
|
if (LowPc == -1ULL ||
|
|
|
|
!hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo))
|
|
|
|
return Flags;
|
|
|
|
|
|
|
|
if (Verbose)
|
|
|
|
DIE.dump(outs(), const_cast<DWARFUnit *>(&OrigUnit), 0, 8 /* Indent */);
|
|
|
|
|
|
|
|
return Flags | TF_Keep;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Check if a DIE should be kept.
|
|
|
|
/// \returns updated TraversalFlags.
|
|
|
|
unsigned DwarfLinker::shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit &Unit,
|
|
|
|
CompileUnit::DIEInfo &MyInfo,
|
|
|
|
unsigned Flags) {
|
|
|
|
switch (DIE.getTag()) {
|
|
|
|
case dwarf::DW_TAG_constant:
|
|
|
|
case dwarf::DW_TAG_variable:
|
|
|
|
return shouldKeepVariableDIE(DIE, Unit, MyInfo, Flags);
|
|
|
|
case dwarf::DW_TAG_subprogram:
|
|
|
|
return shouldKeepSubprogramDIE(DIE, Unit, MyInfo, Flags);
|
|
|
|
case dwarf::DW_TAG_module:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// \brief 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::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
CompileUnit::DIEInfo &MyInfo,
|
|
|
|
const DebugMapObject &DMO,
|
|
|
|
CompileUnit &CU, unsigned Flags) {
|
|
|
|
const DWARFUnit &Unit = CU.getOrigUnit();
|
|
|
|
MyInfo.Keep = true;
|
|
|
|
|
|
|
|
// First mark all the parent chain as kept.
|
|
|
|
unsigned AncestorIdx = MyInfo.ParentIdx;
|
|
|
|
while (!CU.getInfo(AncestorIdx).Keep) {
|
|
|
|
lookForDIEsToKeep(*Unit.getDIEAtIndex(AncestorIdx), DMO, CU,
|
|
|
|
TF_ParentWalk | TF_Keep | TF_DependencyWalk);
|
|
|
|
AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then we need to mark all the DIEs referenced by this DIE's
|
|
|
|
// attributes as kept.
|
|
|
|
DataExtractor Data = Unit.getDebugInfoExtractor();
|
|
|
|
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
|
|
|
|
uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
|
|
|
|
|
|
|
|
// Mark all DIEs referenced through atttributes as kept.
|
|
|
|
for (const auto &AttrSpec : Abbrev->attributes()) {
|
|
|
|
DWARFFormValue Val(AttrSpec.Form);
|
|
|
|
|
|
|
|
if (!Val.isFormClass(DWARFFormValue::FC_Reference)) {
|
|
|
|
DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, &Unit);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Val.extractValue(Data, &Offset, &Unit);
|
|
|
|
CompileUnit *ReferencedCU;
|
|
|
|
if (const auto *RefDIE = resolveDIEReference(Val, Unit, DIE, ReferencedCU))
|
|
|
|
lookForDIEsToKeep(*RefDIE, DMO, *ReferencedCU,
|
|
|
|
TF_Keep | TF_DependencyWalk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief 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.
|
|
|
|
void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE,
|
|
|
|
const DebugMapObject &DMO, CompileUnit &CU,
|
|
|
|
unsigned Flags) {
|
|
|
|
unsigned Idx = CU.getOrigUnit().getDIEIndex(&DIE);
|
|
|
|
CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx);
|
|
|
|
bool AlreadyKept = MyInfo.Keep;
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
if ((Flags & TF_DependencyWalk) && AlreadyKept)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We must not call shouldKeepDIE while called from keepDIEAndDenpendencies,
|
|
|
|
// because it would screw up the relocation finding logic.
|
|
|
|
if (!(Flags & TF_DependencyWalk))
|
|
|
|
Flags = shouldKeepDIE(DIE, CU, MyInfo, Flags);
|
|
|
|
|
|
|
|
// If it is a newly kept DIE mark it as well as all its dependencies as kept.
|
|
|
|
if (!AlreadyKept && (Flags & TF_Keep))
|
|
|
|
keepDIEAndDenpendencies(DIE, MyInfo, DMO, CU, Flags);
|
|
|
|
|
|
|
|
// 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(DIE.getTag()))
|
|
|
|
Flags &= ~TF_ParentWalk;
|
|
|
|
|
|
|
|
if (!DIE.hasChildren() || (Flags & TF_ParentWalk))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto *Child = DIE.getFirstChild(); Child && !Child->isNULL();
|
|
|
|
Child = Child->getSibling())
|
|
|
|
lookForDIEsToKeep(*Child, DMO, CU, Flags);
|
|
|
|
}
|
|
|
|
|
2015-01-28 19:27:01 +01:00
|
|
|
bool DwarfLinker::link(const DebugMap &Map) {
|
|
|
|
|
|
|
|
if (Map.begin() == Map.end()) {
|
|
|
|
errs() << "Empty debug map.\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &Obj : Map.objects()) {
|
2015-02-14 00:18:29 +01:00
|
|
|
CurrentDebugObject = Obj.get();
|
|
|
|
|
2015-01-28 19:27:01 +01:00
|
|
|
if (Verbose)
|
|
|
|
outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n";
|
|
|
|
auto ErrOrObj = BinHolder.GetObjectFile(Obj->getObjectFilename());
|
|
|
|
if (std::error_code EC = ErrOrObj.getError()) {
|
2015-02-14 00:18:29 +01:00
|
|
|
reportWarning(Twine(Obj->getObjectFilename()) + ": " + EC.message());
|
2015-01-28 19:27:01 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-02-14 00:18:22 +01:00
|
|
|
// Look for relocations that correspond to debug map entries.
|
|
|
|
if (!findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) {
|
|
|
|
if (Verbose)
|
|
|
|
outs() << "No valid relocations found. Skipping.\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
// Setup access to the debug info.
|
2015-01-28 19:27:01 +01:00
|
|
|
DWARFContextInMemory DwarfContext(*ErrOrObj);
|
2015-01-28 23:15:14 +01:00
|
|
|
startDebugObject(DwarfContext);
|
2015-01-28 19:27:01 +01:00
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
// In a first phase, just read in the debug info and store the DIE
|
|
|
|
// parent links that we will use during the next phase.
|
2015-01-28 19:27:01 +01:00
|
|
|
for (const auto &CU : DwarfContext.compile_units()) {
|
|
|
|
auto *CUDie = CU->getCompileUnitDIE(false);
|
|
|
|
if (Verbose) {
|
|
|
|
outs() << "Input compilation unit:";
|
|
|
|
CUDie->dump(outs(), CU.get(), 0);
|
|
|
|
}
|
2015-01-28 23:15:14 +01:00
|
|
|
Units.emplace_back(*CU);
|
2015-02-14 00:18:31 +01:00
|
|
|
gatherDIEParents(CUDie, 0, Units.back());
|
2015-01-28 19:27:01 +01:00
|
|
|
}
|
2015-01-28 23:15:14 +01:00
|
|
|
|
2015-02-14 00:18:34 +01:00
|
|
|
// 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 becaue cross-cu
|
|
|
|
// references require the ParentIdx to be setup for every CU in
|
|
|
|
// the object file before calling this.
|
|
|
|
for (auto &CurrentUnit : Units)
|
|
|
|
lookForDIEsToKeep(*CurrentUnit.getOrigUnit().getCompileUnitDIE(), *Obj,
|
|
|
|
CurrentUnit, 0);
|
|
|
|
|
2015-01-28 23:15:14 +01:00
|
|
|
// Clean-up before starting working on the next object.
|
|
|
|
endDebugObject();
|
2015-01-28 19:27:01 +01:00
|
|
|
}
|
|
|
|
|
2014-12-12 18:31:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-01-28 19:27:01 +01:00
|
|
|
|
|
|
|
bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, bool Verbose) {
|
|
|
|
DwarfLinker Linker(OutputFilename, Verbose);
|
|
|
|
return Linker.link(DM);
|
|
|
|
}
|
|
|
|
}
|
2014-12-12 18:31:24 +01:00
|
|
|
}
|