Commit 490b287c authored by Lucian Grijincu's avatar Lucian Grijincu Committed by Facebook GitHub Bot

folly::symbolizer: add support for DWARF5

Summary:
folly::symbolizer now supports both DWARF4 and DWARF5 debug info
- http://www.dwarfstd.org/doc/DWARF4.pdf
- http://www.dwarfstd.org/doc/DWARF5.pdf

Split DWARF (Debug Fission)
- with `-fsplit-dwarf-inlining`: folly::symbolizer will use the debug info from the skeleton CU.
- without `-fsplit-dwarf-inlining`: folly::symbolizer won't read .dwo sections and can't symbolize it (yet).

Major things that changed between the standards that affect folly::symbolizer:
- new forms DW_FORM_addrx*, DW_FORM_loclistx, DW_FORM_rnglistx, DW_FORM_strx*, whose values are interpreted against new CU attributes DW_AT_addr_base, DW_AT_loclists_base, DW_AT_rnglists_base, DW_AT_str_offsets_base
- The .debug_line line header format is completely re-written and the constraint that the file register must be >= 1 was lifted.
- DWARF5 `.debug_loclists` replaces DWARF4 `.debug_ranges`

Differential Revision: D25521092

fbshipit-source-id: 5b48e8abed495f379d8b119177913f55ae11bd2b
parent 0c8f7e29
...@@ -42,6 +42,7 @@ struct DIEAbbreviation { ...@@ -42,6 +42,7 @@ struct DIEAbbreviation {
struct CompilationUnit { struct CompilationUnit {
bool is64Bit = false; bool is64Bit = false;
uint8_t version = 0; uint8_t version = 0;
uint8_t unitType = DW_UT_compile; // DW_UT_compile or DW_UT_skeleton
uint8_t addrSize = 0; uint8_t addrSize = 0;
// Offset in .debug_info of this compilation unit. // Offset in .debug_info of this compilation unit.
uint32_t offset = 0; uint32_t offset = 0;
...@@ -49,6 +50,19 @@ struct CompilationUnit { ...@@ -49,6 +50,19 @@ struct CompilationUnit {
// Offset in .debug_info for the first DIE in this compilation unit. // Offset in .debug_info for the first DIE in this compilation unit.
uint32_t firstDie = 0; uint32_t firstDie = 0;
uint64_t abbrevOffset = 0; uint64_t abbrevOffset = 0;
// The beginning of the CU's contribution to .debug_addr
folly::Optional<uint64_t> addrBase; // DW_AT_addr_base (DWARF 5)
// The beginning of the offsets table (immediately following the
// header) of the CU's contribution to .debug_loclists
folly::Optional<uint64_t> loclistsBase; // DW_AT_loclists_base (DWARF 5)
// The beginning of the offsets table (immediately following the
// header) of the CU's contribution to .debug_rnglists
folly::Optional<uint64_t> rnglistsBase; // DW_AT_rnglists_base (DWARF 5)
// Points to the first string offset of the compilation unit’s
// contribution to the .debug_str_offsets (or .debug_str_offsets.dwo) section.
folly::Optional<uint64_t> strOffsetsBase; // DW_AT_str_offsets_base (DWARF 5)
// Only the CompilationUnit that contains the caller functions needs this. // Only the CompilationUnit that contains the caller functions needs this.
// Indexed by (abbr.code - 1) if (abbr.code - 1) < abbrCache.size(); // Indexed by (abbr.code - 1) if (abbr.code - 1) < abbrCache.size();
folly::Range<DIEAbbreviation*> abbrCache; folly::Range<DIEAbbreviation*> abbrCache;
...@@ -67,6 +81,7 @@ struct Die { ...@@ -67,6 +81,7 @@ struct Die {
struct AttributeSpec { struct AttributeSpec {
uint64_t name = 0; uint64_t name = 0;
uint64_t form = 0; uint64_t form = 0;
int64_t implicitConst = 0; // only set when form=DW_FORM_implicit_const
explicit operator bool() const { return name != 0 || form != 0; } explicit operator bool() const { return name != 0 || form != 0; }
}; };
...@@ -106,6 +121,16 @@ typename std::enable_if<std::is_pod<T>::value, T>::type read( ...@@ -106,6 +121,16 @@ typename std::enable_if<std::is_pod<T>::value, T>::type read(
return x; return x;
} }
// Read (bitwise) an unsigned number of N bytes (N in 1, 2, 3, 4).
template <size_t N>
uint64_t readU64(folly::StringPiece& sp) {
FOLLY_SAFE_CHECK(sp.size() >= N, "underflow");
uint64_t x = 0;
memcpy(&x, sp.data(), N);
sp.advance(N);
return x;
}
// Read ULEB (unsigned) varint value; algorithm from the DWARF spec // Read ULEB (unsigned) varint value; algorithm from the DWARF spec
uint64_t readULEB(folly::StringPiece& sp, uint8_t& shift, uint8_t& val) { uint64_t readULEB(folly::StringPiece& sp, uint8_t& shift, uint8_t& val) {
uint64_t r = 0; uint64_t r = 0;
...@@ -169,7 +194,13 @@ void skipPadding(folly::StringPiece& sp, const char* start, size_t alignment) { ...@@ -169,7 +194,13 @@ void skipPadding(folly::StringPiece& sp, const char* start, size_t alignment) {
} }
detail::AttributeSpec readAttributeSpec(folly::StringPiece& sp) { detail::AttributeSpec readAttributeSpec(folly::StringPiece& sp) {
return {readULEB(sp), readULEB(sp)}; detail::AttributeSpec spec;
spec.name = readULEB(sp);
spec.form = readULEB(sp);
if (spec.form == DW_FORM_implicit_const) {
spec.implicitConst = readSLEB(sp);
}
return spec;
} }
// Reads an abbreviation from a StringPiece, return true if at end; advance sp // Reads an abbreviation from a StringPiece, return true if at end; advance sp
...@@ -207,120 +238,6 @@ folly::StringPiece getStringFromStringSection( ...@@ -207,120 +238,6 @@ folly::StringPiece getStringFromStringSection(
return readNullTerminated(str); return readNullTerminated(str);
} }
detail::Attribute readAttribute(
const detail::Die& die,
detail::AttributeSpec spec,
folly::StringPiece& info,
folly::StringPiece str) {
switch (spec.form) {
case DW_FORM_addr:
return {spec, die, read<uintptr_t>(info)};
case DW_FORM_block1:
return {spec, die, readBytes(info, read<uint8_t>(info))};
case DW_FORM_block2:
return {spec, die, readBytes(info, read<uint16_t>(info))};
case DW_FORM_block4:
return {spec, die, readBytes(info, read<uint32_t>(info))};
case DW_FORM_block:
FOLLY_FALLTHROUGH;
case DW_FORM_exprloc:
return {spec, die, readBytes(info, readULEB(info))};
case DW_FORM_data1:
FOLLY_FALLTHROUGH;
case DW_FORM_ref1:
return {spec, die, read<uint8_t>(info)};
case DW_FORM_data2:
FOLLY_FALLTHROUGH;
case DW_FORM_ref2:
return {spec, die, read<uint16_t>(info)};
case DW_FORM_data4:
FOLLY_FALLTHROUGH;
case DW_FORM_ref4:
return {spec, die, read<uint32_t>(info)};
case DW_FORM_data8:
FOLLY_FALLTHROUGH;
case DW_FORM_ref8:
FOLLY_FALLTHROUGH;
case DW_FORM_ref_sig8:
return {spec, die, read<uint64_t>(info)};
case DW_FORM_sdata:
return {spec, die, readSLEB(info)};
case DW_FORM_udata:
FOLLY_FALLTHROUGH;
case DW_FORM_ref_udata:
return {spec, die, readULEB(info)};
case DW_FORM_flag:
return {spec, die, read<uint8_t>(info)};
case DW_FORM_flag_present:
return {spec, die, 1};
case DW_FORM_sec_offset:
FOLLY_FALLTHROUGH;
case DW_FORM_ref_addr:
return {spec, die, readOffset(info, die.is64Bit)};
case DW_FORM_string:
return {spec, die, readNullTerminated(info)};
case DW_FORM_strp:
return {
spec,
die,
getStringFromStringSection(str, readOffset(info, die.is64Bit))};
case DW_FORM_indirect: // form is explicitly specified
// Update spec with the actual FORM.
spec.form = readULEB(info);
return readAttribute(die, spec, info, str);
default:
FOLLY_SAFE_CHECK(false, "invalid attribute form");
}
return {spec, die, 0};
}
detail::CompilationUnit getCompilationUnit(
folly::StringPiece info, uint64_t offset) {
FOLLY_SAFE_DCHECK(offset < info.size(), "unexpected offset");
detail::CompilationUnit cu;
folly::StringPiece chunk(info);
cu.offset = offset;
chunk.advance(offset);
auto initialLength = read<uint32_t>(chunk);
cu.is64Bit = (initialLength == (uint32_t)-1);
cu.size = cu.is64Bit ? read<uint64_t>(chunk) : initialLength;
FOLLY_SAFE_CHECK(cu.size <= chunk.size(), "invalid chunk size");
cu.size += cu.is64Bit ? 12 : 4;
cu.version = read<uint16_t>(chunk);
FOLLY_SAFE_CHECK(cu.version >= 2 && cu.version <= 4, "invalid info version");
cu.abbrevOffset = readOffset(chunk, cu.is64Bit);
cu.addrSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(cu.addrSize == sizeof(uintptr_t), "invalid address size");
cu.firstDie = chunk.data() - info.data();
return cu;
}
// Finds the Compilation Unit starting at offset.
detail::CompilationUnit findCompilationUnit(
folly::StringPiece info, uint64_t targetOffset) {
FOLLY_SAFE_DCHECK(targetOffset < info.size(), "unexpected target address");
uint64_t offset = 0;
while (offset < info.size()) {
folly::StringPiece chunk(info);
chunk.advance(offset);
auto initialLength = read<uint32_t>(chunk);
auto is64Bit = (initialLength == (uint32_t)-1);
auto size = is64Bit ? read<uint64_t>(chunk) : initialLength;
FOLLY_SAFE_CHECK(size <= chunk.size(), "invalid chunk size");
size += is64Bit ? 12 : 4;
if (offset + size > targetOffset) {
break;
}
offset += size;
}
return getCompilationUnit(info, offset);
}
void readCompilationUnitAbbrs( void readCompilationUnitAbbrs(
folly::StringPiece abbrev, detail::CompilationUnit& cu) { folly::StringPiece abbrev, detail::CompilationUnit& cu) {
abbrev.advance(cu.abbrevOffset); abbrev.advance(cu.abbrevOffset);
...@@ -338,18 +255,24 @@ void readCompilationUnitAbbrs( ...@@ -338,18 +255,24 @@ void readCompilationUnitAbbrs(
Dwarf::Dwarf(const ElfFile* elf) Dwarf::Dwarf(const ElfFile* elf)
: elf_(elf), : elf_(elf),
debugInfo_(getSection(".debug_info")),
debugAbbrev_(getSection(".debug_abbrev")), debugAbbrev_(getSection(".debug_abbrev")),
debugAddr_(getSection(".debug_addr")),
debugAranges_(getSection(".debug_aranges")),
debugInfo_(getSection(".debug_info")),
debugLine_(getSection(".debug_line")), debugLine_(getSection(".debug_line")),
debugLineStr_(getSection(".debug_line_str")),
debugLoclists_(getSection(".debug_loclists")),
debugRanges_(getSection(".debug_ranges")),
debugRnglists_(getSection(".debug_rnglists")),
debugStr_(getSection(".debug_str")), debugStr_(getSection(".debug_str")),
debugAranges_(getSection(".debug_aranges")), debugStrOffsets_(getSection(".debug_str_offsets")) {
debugRanges_(getSection(".debug_ranges")) {
// Optional sections: // Optional sections:
// - debugAranges_: for fast address range lookup. // - debugAranges_: for fast address range lookup.
// If missing .debug_info can be used - but it's much slower (linear // If missing .debug_info can be used - but it's much slower (linear
// scan). // scan).
// - debugRanges_: contains non-contiguous address ranges of debugging // - debugRanges_ (DWARF 4) / debugRnglists_ (DWARF 5): non-contiguous
// information entries. Used for inline function address lookup. // address ranges of debugging information entries.
// Used for inline function address lookup.
if (debugInfo_.empty() || debugAbbrev_.empty() || debugLine_.empty() || if (debugInfo_.empty() || debugAbbrev_.empty() || debugLine_.empty() ||
debugStr_.empty()) { debugStr_.empty()) {
elf_ = nullptr; elf_ = nullptr;
...@@ -369,7 +292,7 @@ bool Dwarf::Section::next(folly::StringPiece& chunk) { ...@@ -369,7 +292,7 @@ bool Dwarf::Section::next(folly::StringPiece& chunk) {
// a 96-bit value (0xffffffff followed by the 64-bit length) for a 64-bit // a 96-bit value (0xffffffff followed by the 64-bit length) for a 64-bit
// section. // section.
auto initialLength = read<uint32_t>(chunk); auto initialLength = read<uint32_t>(chunk);
is64Bit_ = (initialLength == (uint32_t)-1); is64Bit_ = (initialLength == uint32_t(-1));
auto length = is64Bit_ ? read<uint64_t>(chunk) : initialLength; auto length = is64Bit_ ? read<uint64_t>(chunk) : initialLength;
FOLLY_SAFE_CHECK(length <= chunk.size(), "invalid DWARF section"); FOLLY_SAFE_CHECK(length <= chunk.size(), "invalid DWARF section");
chunk.reset(chunk.data(), length); chunk.reset(chunk.data(), length);
...@@ -390,6 +313,110 @@ folly::StringPiece Dwarf::getSection(const char* name) const { ...@@ -390,6 +313,110 @@ folly::StringPiece Dwarf::getSection(const char* name) const {
return elf_->getSectionBody(*elfSection); return elf_->getSectionBody(*elfSection);
} }
detail::CompilationUnit Dwarf::getCompilationUnit(uint64_t offset) const {
FOLLY_SAFE_DCHECK(offset < debugInfo_.size(), "unexpected offset");
detail::CompilationUnit cu;
folly::StringPiece chunk(debugInfo_);
cu.offset = offset;
chunk.advance(offset);
// 1) unit_length
auto initialLength = read<uint32_t>(chunk);
cu.is64Bit = (initialLength == uint32_t(-1));
cu.size = cu.is64Bit ? read<uint64_t>(chunk) : initialLength;
FOLLY_SAFE_CHECK(cu.size <= chunk.size(), "invalid chunk size");
cu.size += cu.is64Bit ? 12 : 4;
// 2) version
cu.version = read<uint16_t>(chunk);
FOLLY_SAFE_CHECK(cu.version >= 2 && cu.version <= 5, "invalid info version");
if (cu.version == 5) {
// DWARF5: 7.5.1.1 Full and Partial Compilation Unit Headers
// 3) unit_type (new DWARF 5)
cu.unitType = read<uint8_t>(chunk);
if (cu.unitType != DW_UT_compile && cu.unitType != DW_UT_skeleton) {
return cu;
}
// 4) address_size
cu.addrSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(cu.addrSize == sizeof(uintptr_t), "invalid address size");
// 5) debug_abbrev_offset
cu.abbrevOffset = readOffset(chunk, cu.is64Bit);
if (cu.unitType == DW_UT_skeleton) {
// 6) dwo_id
read<uint64_t>(chunk);
}
} else {
// DWARF4 has a single type of unit in .debug_info
cu.unitType = DW_UT_compile;
// 3) debug_abbrev_offset
cu.abbrevOffset = readOffset(chunk, cu.is64Bit);
// 4) address_size
cu.addrSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(cu.addrSize == sizeof(uintptr_t), "invalid address size");
}
cu.firstDie = chunk.data() - debugInfo_.data();
if (cu.version < 5) {
return cu;
}
detail::Die die = getDieAtOffset(cu, cu.firstDie);
if (die.abbr.tag != DW_TAG_compile_unit) {
return cu;
}
// Read the DW_AT_*_base attributes.
// Attributes which use FORMs relative to these base attrs
// will not have valid values during this first pass!
forEachAttribute(cu, die, [&](const detail::Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_addr_base:
case DW_AT_GNU_addr_base:
cu.addrBase = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_loclists_base:
cu.loclistsBase = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_rnglists_base:
case DW_AT_GNU_ranges_base:
cu.rnglistsBase = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_str_offsets_base:
cu.strOffsetsBase = boost::get<uint64_t>(attr.attrValue);
break;
}
return true; // continue forEachAttribute
});
return cu;
}
// Finds the Compilation Unit starting at offset.
detail::CompilationUnit Dwarf::findCompilationUnit(
uint64_t targetOffset) const {
FOLLY_SAFE_DCHECK(
targetOffset < debugInfo_.size(), "unexpected target address");
uint64_t offset = 0;
while (offset < debugInfo_.size()) {
folly::StringPiece chunk(debugInfo_);
chunk.advance(offset);
auto initialLength = read<uint32_t>(chunk);
auto is64Bit = (initialLength == (uint32_t)-1);
auto size = is64Bit ? read<uint64_t>(chunk) : initialLength;
FOLLY_SAFE_CHECK(size <= chunk.size(), "invalid chunk size");
size += is64Bit ? 12 : 4;
if (offset + size > targetOffset) {
break;
}
offset += size;
}
return getCompilationUnit(offset);
}
detail::DIEAbbreviation Dwarf::getAbbreviation( detail::DIEAbbreviation Dwarf::getAbbreviation(
uint64_t code, uint64_t offset) const { uint64_t code, uint64_t offset) const {
// Linear search in the .debug_abbrev section, starting at offset // Linear search in the .debug_abbrev section, starting at offset
...@@ -465,11 +492,9 @@ bool Dwarf::findLocation( ...@@ -465,11 +492,9 @@ bool Dwarf::findLocation(
bool checkAddress) const { bool checkAddress) const {
detail::Die die = getDieAtOffset(cu, cu.firstDie); detail::Die die = getDieAtOffset(cu, cu.firstDie);
// Partial compilation unit (DW_TAG_partial_unit) is not supported. // Partial compilation unit (DW_TAG_partial_unit) is not supported.
FOLLY_SAFE_CHECK( FOLLY_SAFE_CHECK(die.abbr.tag == DW_TAG_compile_unit, "");
die.abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
// Offset in .debug_line for the line number VM program for this // Offset in .debug_line for the line number VM program for this CU
// compilation unit
folly::Optional<uint64_t> lineOffset; folly::Optional<uint64_t> lineOffset;
folly::StringPiece compilationDirectory; folly::StringPiece compilationDirectory;
folly::Optional<folly::StringPiece> mainFileName; folly::Optional<folly::StringPiece> mainFileName;
...@@ -519,7 +544,7 @@ bool Dwarf::findLocation( ...@@ -519,7 +544,7 @@ bool Dwarf::findLocation(
foundAddress = true; foundAddress = true;
} else if (rangesOffset) { } else if (rangesOffset) {
if (!isAddrInRangeList( if (!isAddrInRangeList(
address, baseAddrCU, *rangesOffset, cu.addrSize)) { cu, address, baseAddrCU, *rangesOffset, cu.addrSize)) {
return false; return false;
} }
foundAddress = true; foundAddress = true;
...@@ -546,7 +571,7 @@ bool Dwarf::findLocation( ...@@ -546,7 +571,7 @@ bool Dwarf::findLocation(
rangesOffset = boost::get<uint64_t>(attr.attrValue); rangesOffset = boost::get<uint64_t>(attr.attrValue);
if (seenLowPC) { if (seenLowPC) {
if (!isAddrInRangeList( if (!isAddrInRangeList(
address, baseAddrCU, *rangesOffset, cu.addrSize)) { cu, address, baseAddrCU, *rangesOffset, cu.addrSize)) {
return false; return false;
} }
foundAddress = true; foundAddress = true;
...@@ -568,7 +593,8 @@ bool Dwarf::findLocation( ...@@ -568,7 +593,8 @@ bool Dwarf::findLocation(
folly::StringPiece lineSection(debugLine_); folly::StringPiece lineSection(debugLine_);
lineSection.advance(*lineOffset); lineSection.advance(*lineOffset);
LineNumberVM lineVM(lineSection, compilationDirectory); LineNumberVM lineVM(
lineSection, compilationDirectory, debugStr_, debugLineStr_);
// Execute line number VM program to find file and line // Execute line number VM program to find file and line
locationInfo.hasFileAndLine = locationInfo.hasFileAndLine =
...@@ -712,7 +738,10 @@ bool Dwarf::findAddress( ...@@ -712,7 +738,10 @@ bool Dwarf::findAddress(
uint64_t offset = 0; uint64_t offset = 0;
if (findDebugInfoOffset(address, debugAranges_, offset)) { if (findDebugInfoOffset(address, debugAranges_, offset)) {
// Read compilation unit header from .debug_info // Read compilation unit header from .debug_info
auto unit = getCompilationUnit(debugInfo_, offset); auto unit = getCompilationUnit(offset);
if (unit.unitType != DW_UT_compile && unit.unitType != DW_UT_skeleton) {
return false;
}
return findLocation( return findLocation(
address, address,
mode, mode,
...@@ -740,8 +769,11 @@ bool Dwarf::findAddress( ...@@ -740,8 +769,11 @@ bool Dwarf::findAddress(
// and look for the address in each compilation unit. // and look for the address in each compilation unit.
uint64_t offset = 0; uint64_t offset = 0;
while (offset < debugInfo_.size()) { while (offset < debugInfo_.size()) {
auto unit = getCompilationUnit(debugInfo_, offset); auto unit = getCompilationUnit(offset);
offset += unit.size; offset += unit.size;
if (unit.unitType != DW_UT_compile && unit.unitType != DW_UT_skeleton) {
continue;
}
if (findLocation( if (findLocation(
address, address,
mode, mode,
...@@ -816,6 +848,179 @@ size_t Dwarf::forEachChild( ...@@ -816,6 +848,179 @@ size_t Dwarf::forEachChild(
return childDie.offset + 1; return childDie.offset + 1;
} }
detail::Attribute Dwarf::readAttribute(
const detail::CompilationUnit& cu,
const detail::Die& die,
detail::AttributeSpec spec,
folly::StringPiece& info) const {
// DWARF 5 introduces new FORMs whose values are relative to some base attrs:
// DW_AT_str_offsets_base, DW_AT_rnglists_base, DW_AT_addr_base.
// Debug Fission DWARF 4 uses GNU DW_AT_GNU_ranges_base & DW_AT_GNU_addr_base.
//
// The order in which attributes appear in a CU is not defined.
// The DW_AT_*_base attrs may appear after attributes that need them.
// The DW_AT_*_base attrs are CU specific; so we read them just after
// reading the CU header. During this first pass return empty values
// when encountering a FORM that depends on DW_AT_*_base.
auto getStringUsingOffsetTable = [&](uint64_t index) {
if (!cu.strOffsetsBase.has_value()) {
return folly::StringPiece();
}
// DWARF 5: 7.26 String Offsets Table
// The DW_AT_str_offsets_base attribute points to the first entry following
// the header. The entries are indexed sequentially from this base entry,
// starting from 0.
auto sp = debugStrOffsets_.subpiece(
*cu.strOffsetsBase +
index * (cu.is64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
uint64_t strOffset = readOffset(sp, cu.is64Bit);
return getStringFromStringSection(debugStr_, strOffset);
};
auto readDebugAddr = [&](uint64_t index) {
if (!cu.addrBase.has_value()) {
return uint64_t(0);
}
// DWARF 5: 7.27 Address Table
// The DW_AT_addr_base attribute points to the first entry following the
// header. The entries are indexed sequentially from this base entry,
// starting from 0.
auto sp = debugAddr_.subpiece(*cu.addrBase + index * sizeof(uint64_t));
return read<uint64_t>(sp);
};
switch (spec.form) {
case DW_FORM_addr:
return {spec, die, read<uintptr_t>(info)};
case DW_FORM_block1:
return {spec, die, readBytes(info, read<uint8_t>(info))};
case DW_FORM_block2:
return {spec, die, readBytes(info, read<uint16_t>(info))};
case DW_FORM_block4:
return {spec, die, readBytes(info, read<uint32_t>(info))};
case DW_FORM_block:
FOLLY_FALLTHROUGH;
case DW_FORM_exprloc:
return {spec, die, readBytes(info, readULEB(info))};
case DW_FORM_data1:
FOLLY_FALLTHROUGH;
case DW_FORM_ref1:
return {spec, die, read<uint8_t>(info)};
case DW_FORM_data2:
FOLLY_FALLTHROUGH;
case DW_FORM_ref2:
return {spec, die, read<uint16_t>(info)};
case DW_FORM_data4:
FOLLY_FALLTHROUGH;
case DW_FORM_ref4:
return {spec, die, read<uint32_t>(info)};
case DW_FORM_data8:
FOLLY_FALLTHROUGH;
case DW_FORM_ref8:
FOLLY_FALLTHROUGH;
case DW_FORM_ref_sig8:
return {spec, die, read<uint64_t>(info)};
case DW_FORM_sdata:
return {spec, die, readSLEB(info)};
case DW_FORM_udata:
FOLLY_FALLTHROUGH;
case DW_FORM_ref_udata:
return {spec, die, readULEB(info)};
case DW_FORM_flag:
return {spec, die, read<uint8_t>(info)};
case DW_FORM_flag_present:
return {spec, die, 1};
case DW_FORM_sec_offset:
FOLLY_FALLTHROUGH;
case DW_FORM_ref_addr:
return {spec, die, readOffset(info, die.is64Bit)};
case DW_FORM_string:
return {spec, die, readNullTerminated(info)};
case DW_FORM_strp:
return {
spec,
die,
getStringFromStringSection(debugStr_, readOffset(info, die.is64Bit))};
case DW_FORM_indirect: // form is explicitly specified
// Update spec with the actual FORM.
spec.form = readULEB(info);
return readAttribute(cu, die, spec, info);
// DWARF 5:
case DW_FORM_implicit_const: // form is explicitly specified
// For attributes with this form, the attribute specification contains a
// third part, which is a signed LEB128 number. The value of this number
// is used as the value of the attribute, and no value is stored in the
// .debug_info section.
return {spec, die, spec.implicitConst};
case DW_FORM_addrx:
return {spec, die, readDebugAddr(readULEB(info))};
case DW_FORM_addrx1:
return {spec, die, readDebugAddr(readU64<1>(info))};
case DW_FORM_addrx2:
return {spec, die, readDebugAddr(readU64<2>(info))};
case DW_FORM_addrx3:
return {spec, die, readDebugAddr(readU64<3>(info))};
case DW_FORM_addrx4:
return {spec, die, readDebugAddr(readU64<4>(info))};
case DW_FORM_line_strp:
return {
spec,
die,
getStringFromStringSection(
debugLineStr_, readOffset(info, die.is64Bit))};
case DW_FORM_strx:
return {spec, die, getStringUsingOffsetTable(readULEB(info))};
case DW_FORM_strx1:
return {spec, die, getStringUsingOffsetTable(readU64<1>(info))};
case DW_FORM_strx2:
return {spec, die, getStringUsingOffsetTable(readU64<2>(info))};
case DW_FORM_strx3:
return {spec, die, getStringUsingOffsetTable(readU64<3>(info))};
case DW_FORM_strx4:
return {spec, die, getStringUsingOffsetTable(readU64<4>(info))};
case DW_FORM_rnglistx: {
auto index = readULEB(info);
if (!cu.rnglistsBase.has_value()) {
return {spec, die, 0};
}
const uint64_t offsetSize =
cu.is64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
auto sp = debugRnglists_.subpiece(*cu.rnglistsBase + index * offsetSize);
auto offset = readOffset(sp, cu.is64Bit);
return {spec, die, *cu.rnglistsBase + offset};
} break;
case DW_FORM_loclistx: {
auto index = readULEB(info);
if (!cu.loclistsBase.has_value()) {
return {spec, die, 0};
}
const uint64_t offsetSize =
cu.is64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
auto sp = debugLoclists_.subpiece(*cu.loclistsBase + index * offsetSize);
auto offset = readOffset(sp, cu.is64Bit);
return {spec, die, *cu.loclistsBase + offset};
} break;
case DW_FORM_data16:
return {spec, die, readBytes(info, 16)};
case DW_FORM_ref_sup4:
case DW_FORM_ref_sup8:
case DW_FORM_strp_sup:
FOLLY_SAFE_CHECK(false, "Unexpected DWARF5 supplimentary object files");
default:
FOLLY_SAFE_CHECK(false, "invalid attribute form");
}
return {spec, die, 0};
}
/* /*
* Iterate over all attributes of the given DIE, calling the given callable * Iterate over all attributes of the given DIE, calling the given callable
* for each. Iteration is stopped early if any of the calls return false. * for each. Iteration is stopped early if any of the calls return false.
...@@ -829,7 +1034,7 @@ size_t Dwarf::forEachAttribute( ...@@ -829,7 +1034,7 @@ size_t Dwarf::forEachAttribute(
debugInfo_.data() + die.offset + die.attrOffset, debugInfo_.data() + die.offset + die.attrOffset,
debugInfo_.data() + cu.offset + cu.size}; debugInfo_.data() + cu.offset + cu.size};
while (auto spec = readAttributeSpec(attrs)) { while (auto spec = readAttributeSpec(attrs)) {
auto attr = readAttribute(die, spec, values, debugStr_); auto attr = readAttribute(cu, die, spec, values);
if (!f(attr)) { if (!f(attr)) {
return static_cast<size_t>(-1); return static_cast<size_t>(-1);
} }
...@@ -854,15 +1059,13 @@ folly::Optional<T> Dwarf::getAttribute( ...@@ -854,15 +1059,13 @@ folly::Optional<T> Dwarf::getAttribute(
} }
bool Dwarf::isAddrInRangeList( bool Dwarf::isAddrInRangeList(
const detail::CompilationUnit& cu,
uint64_t address, uint64_t address,
folly::Optional<uint64_t> baseAddr, folly::Optional<uint64_t> baseAddr,
size_t offset, size_t offset,
uint8_t addrSize) const { uint8_t addrSize) const {
FOLLY_SAFE_CHECK(addrSize == 4 || addrSize == 8, "wrong address size"); FOLLY_SAFE_CHECK(addrSize == 4 || addrSize == 8, "wrong address size");
if (debugRanges_.empty()) { if (cu.version <= 4 && !debugRanges_.empty()) {
return false;
}
const bool is64BitAddr = addrSize == 8; const bool is64BitAddr = addrSize == 8;
folly::StringPiece sp = debugRanges_; folly::StringPiece sp = debugRanges_;
sp.advance(offset); sp.advance(offset);
...@@ -880,17 +1083,100 @@ bool Dwarf::isAddrInRangeList( ...@@ -880,17 +1083,100 @@ bool Dwarf::isAddrInRangeList(
if (begin == 0 && end == 0) { if (begin == 0 && end == 0) {
break; break;
} }
// Check if the given address falls in the range list entry. // Check if the given address falls in the range list entry.
// 2.17.3 Non-Contiguous Address Ranges // 2.17.3 Non-Contiguous Address Ranges
// The applicable base address of a range list entry is determined by the // The applicable base address of a range list entry is determined by the
// closest preceding base address selection entry (see below) in the same // closest preceding base address selection entry (see below) in the same
// range list. If there is no such selection entry, then the applicable base // range list. If there is no such selection entry, then the applicable
// address defaults to the base address of the compilation unit. // base address defaults to the base address of the compilation unit.
if (baseAddr && address >= begin + *baseAddr && address < end + *baseAddr) { if (baseAddr && address >= begin + *baseAddr &&
address < end + *baseAddr) {
return true; return true;
} }
}; };
}
if (cu.version == 5 && !debugRnglists_.empty() && cu.addrBase.has_value()) {
auto debugRnglists = debugRnglists_;
debugRnglists.advance(offset);
while (!debugRnglists.empty()) {
auto kind = read<uint8_t>(debugRnglists);
switch (kind) {
case DW_RLE_end_of_list:
return false;
case DW_RLE_base_addressx: {
auto index = readULEB(debugRnglists);
auto sp =
debugAddr_.subpiece(*cu.addrBase + index * sizeof(uint64_t));
baseAddr = read<uint64_t>(sp);
} break;
case DW_RLE_startx_endx: {
auto indexStart = readULEB(debugRnglists);
auto indexEnd = readULEB(debugRnglists);
auto spStart =
debugAddr_.subpiece(*cu.addrBase + indexStart * sizeof(uint64_t));
auto start = read<uint64_t>(spStart);
auto spEnd =
debugAddr_.subpiece(*cu.addrBase + indexEnd * sizeof(uint64_t));
auto end = read<uint64_t>(spEnd);
if (address >= start && address < end) {
return true;
}
} break;
case DW_RLE_startx_length: {
auto indexStart = readULEB(debugRnglists);
auto length = readULEB(debugRnglists);
auto spStart =
debugAddr_.subpiece(*cu.addrBase + indexStart * sizeof(uint64_t));
auto start = read<uint64_t>(spStart);
auto spEnd = debugAddr_.subpiece(
*cu.addrBase + indexStart * sizeof(uint64_t) + length);
auto end = read<uint64_t>(spEnd);
if (start != end && address >= start && address < end) {
return true;
}
} break;
case DW_RLE_offset_pair: {
auto offsetStart = readULEB(debugRnglists);
auto offsetEnd = readULEB(debugRnglists);
if (baseAddr && address >= (*baseAddr + offsetStart) &&
address < (*baseAddr + offsetEnd)) {
return true;
}
} break;
case DW_RLE_base_address:
baseAddr = read<uint64_t>(debugRnglists);
break;
case DW_RLE_start_end: {
uint64_t start = read<uint64_t>(debugRnglists);
uint64_t end = read<uint64_t>(debugRnglists);
if (address >= start && address < end) {
return true;
}
} break;
case DW_RLE_start_length: {
uint64_t start = read<uint64_t>(debugRnglists);
uint64_t end = start + readULEB(debugRnglists);
if (address >= start && address < end) {
return true;
}
} break;
default:
FOLLY_SAFE_CHECK(false, "Unexpected debug_rnglists entry kind");
}
}
}
return false; return false;
} }
...@@ -915,9 +1201,14 @@ bool Dwarf::findSubProgramDieForAddress( ...@@ -915,9 +1201,14 @@ bool Dwarf::findSubProgramDieForAddress(
lowPc = boost::get<uint64_t>(attr.attrValue); lowPc = boost::get<uint64_t>(attr.attrValue);
break; break;
case DW_AT_high_pc: case DW_AT_high_pc:
// Value of DW_AT_high_pc attribute can be an address // The value of the DW_AT_high_pc attribute can be
// (DW_FORM_addr) or an offset (DW_FORM_data). // an address (DW_FORM_addr*) or an offset (DW_FORM_data*).
isHighPcAddr = (attr.spec.form == DW_FORM_addr); isHighPcAddr = attr.spec.form == DW_FORM_addr || //
attr.spec.form == DW_FORM_addrx || //
attr.spec.form == DW_FORM_addrx1 || //
attr.spec.form == DW_FORM_addrx2 || //
attr.spec.form == DW_FORM_addrx3 || //
attr.spec.form == DW_FORM_addrx4;
highPc = boost::get<uint64_t>(attr.attrValue); highPc = boost::get<uint64_t>(attr.attrValue);
break; break;
} }
...@@ -934,7 +1225,7 @@ bool Dwarf::findSubProgramDieForAddress( ...@@ -934,7 +1225,7 @@ bool Dwarf::findSubProgramDieForAddress(
bool rangeMatch = bool rangeMatch =
rangeOffset && rangeOffset &&
isAddrInRangeList( isAddrInRangeList(
address, baseAddrCU, rangeOffset.value(), cu.addrSize); cu, address, baseAddrCU, rangeOffset.value(), cu.addrSize);
if (rangeMatch) { if (rangeMatch) {
subprogram = childDie; subprogram = childDie;
return false; // stop forEachChild return false; // stop forEachChild
...@@ -1006,9 +1297,14 @@ void Dwarf::findInlinedSubroutineDieForAddress( ...@@ -1006,9 +1297,14 @@ void Dwarf::findInlinedSubroutineDieForAddress(
lowPc = boost::get<uint64_t>(attr.attrValue); lowPc = boost::get<uint64_t>(attr.attrValue);
break; break;
case DW_AT_high_pc: case DW_AT_high_pc:
// Value of DW_AT_high_pc attribute can be an address // The value of the DW_AT_high_pc attribute can be
// (DW_FORM_addr) or an offset (DW_FORM_data). // an address (DW_FORM_addr*) or an offset (DW_FORM_data*).
isHighPcAddr = (attr.spec.form == DW_FORM_addr); isHighPcAddr = attr.spec.form == DW_FORM_addr || //
attr.spec.form == DW_FORM_addrx || //
attr.spec.form == DW_FORM_addrx1 || //
attr.spec.form == DW_FORM_addrx2 || //
attr.spec.form == DW_FORM_addrx3 || //
attr.spec.form == DW_FORM_addrx4;
highPc = boost::get<uint64_t>(attr.attrValue); highPc = boost::get<uint64_t>(attr.attrValue);
break; break;
case DW_AT_abstract_origin: case DW_AT_abstract_origin:
...@@ -1038,11 +1334,11 @@ void Dwarf::findInlinedSubroutineDieForAddress( ...@@ -1038,11 +1334,11 @@ void Dwarf::findInlinedSubroutineDieForAddress(
// have DW_AT_low_pc/DW_AT_high_pc pairs and DW_AT_ranges. // have DW_AT_low_pc/DW_AT_high_pc pairs and DW_AT_ranges.
// TODO: Support relocated address which requires lookup in relocation map. // TODO: Support relocated address which requires lookup in relocation map.
bool pcMatch = lowPc && highPc && isHighPcAddr && address >= *lowPc && bool pcMatch = lowPc && highPc && isHighPcAddr && address >= *lowPc &&
(address < (*isHighPcAddr ? *highPc : *lowPc + *highPc)); (address < (*isHighPcAddr ? *highPc : (*lowPc + *highPc)));
bool rangeMatch = bool rangeMatch =
rangeOffset && rangeOffset &&
isAddrInRangeList( isAddrInRangeList(
address, baseAddrCU, rangeOffset.value(), cu.addrSize); cu, address, baseAddrCU, rangeOffset.value(), cu.addrSize);
if (!pcMatch && !rangeMatch) { if (!pcMatch && !rangeMatch) {
// Address doesn't match. Keep searching other children. // Address doesn't match. Keep searching other children.
return true; return true;
...@@ -1102,8 +1398,7 @@ void Dwarf::findInlinedSubroutineDieForAddress( ...@@ -1102,8 +1398,7 @@ void Dwarf::findInlinedSubroutineDieForAddress(
locations[numFound].name = (*abstractOriginRefType != DW_FORM_ref_addr) locations[numFound].name = (*abstractOriginRefType != DW_FORM_ref_addr)
? getFunctionName(cu, cu.offset + *abstractOrigin) ? getFunctionName(cu, cu.offset + *abstractOrigin)
: getFunctionName( : getFunctionName(
findCompilationUnit(debugInfo_, *abstractOrigin), findCompilationUnit(*abstractOrigin), *abstractOrigin);
*abstractOrigin);
findInlinedSubroutineDieForAddress( findInlinedSubroutineDieForAddress(
cu, childDie, lineVM, address, baseAddrCU, locations, ++numFound); cu, childDie, lineVM, address, baseAddrCU, locations, ++numFound);
...@@ -1113,8 +1408,13 @@ void Dwarf::findInlinedSubroutineDieForAddress( ...@@ -1113,8 +1408,13 @@ void Dwarf::findInlinedSubroutineDieForAddress(
} }
Dwarf::LineNumberVM::LineNumberVM( Dwarf::LineNumberVM::LineNumberVM(
folly::StringPiece data, folly::StringPiece compilationDirectory) folly::StringPiece data,
: compilationDirectory_(compilationDirectory) { folly::StringPiece compilationDirectory,
folly::StringPiece debugStr,
folly::StringPiece debugLineStr)
: compilationDirectory_(compilationDirectory),
debugStr_(debugStr),
debugLineStr_(debugLineStr) {
Section section(data); Section section(data);
FOLLY_SAFE_CHECK(section.next(data_), "invalid line number VM"); FOLLY_SAFE_CHECK(section.next(data_), "invalid line number VM");
is64Bit_ = section.is64Bit(); is64Bit_ = section.is64Bit();
...@@ -1136,10 +1436,140 @@ void Dwarf::LineNumberVM::reset() { ...@@ -1136,10 +1436,140 @@ void Dwarf::LineNumberVM::reset() {
discriminator_ = 0; discriminator_ = 0;
} }
struct LineNumberAttribute {
uint64_t contentTypeCode;
uint64_t formCode;
boost::variant<uint64_t, folly::StringPiece> attrValue;
};
LineNumberAttribute readLineNumberAttribute(
bool is64Bit,
folly::StringPiece& format,
folly::StringPiece& entries,
folly::StringPiece debugStr,
folly::StringPiece debugLineStr) {
uint64_t contentTypeCode = readULEB(format);
uint64_t formCode = readULEB(format);
boost::variant<uint64_t, folly::StringPiece> attrValue;
switch (contentTypeCode) {
case DW_LNCT_path: {
switch (formCode) {
case DW_FORM_string:
attrValue = readNullTerminated(entries);
break;
case DW_FORM_line_strp: {
auto off = readOffset(entries, is64Bit);
attrValue = getStringFromStringSection(debugLineStr, off);
} break;
case DW_FORM_strp:
attrValue = getStringFromStringSection(
debugStr, readOffset(entries, is64Bit));
break;
case DW_FORM_strp_sup:
FOLLY_SAFE_CHECK(false, "Unexpected DW_FORM_strp_sup");
break;
default:
FOLLY_SAFE_CHECK(false, "Unexpected form for DW_LNCT_path");
break;
}
} break;
case DW_LNCT_directory_index: {
switch (formCode) {
case DW_FORM_data1:
attrValue = read<uint8_t>(entries);
break;
case DW_FORM_data2:
attrValue = read<uint16_t>(entries);
break;
case DW_FORM_udata:
attrValue = readULEB(entries);
break;
default:
FOLLY_SAFE_CHECK(
false, "Unexpected form for DW_LNCT_directory_index");
break;
}
} break;
case DW_LNCT_timestamp: {
switch (formCode) {
case DW_FORM_udata:
attrValue = readULEB(entries);
break;
case DW_FORM_data4:
attrValue = read<uint32_t>(entries);
break;
case DW_FORM_data8:
attrValue = read<uint64_t>(entries);
break;
case DW_FORM_block:
attrValue = readBytes(entries, readULEB(entries));
break;
default:
FOLLY_SAFE_CHECK(false, "Unexpected form for DW_LNCT_timestamp");
}
} break;
case DW_LNCT_size: {
switch (formCode) {
case DW_FORM_udata:
attrValue = readULEB(entries);
break;
case DW_FORM_data1:
attrValue = read<uint8_t>(entries);
break;
case DW_FORM_data2:
attrValue = read<uint16_t>(entries);
break;
case DW_FORM_data4:
attrValue = read<uint32_t>(entries);
break;
case DW_FORM_data8:
attrValue = read<uint64_t>(entries);
break;
default:
FOLLY_SAFE_CHECK(false, "Unexpected form for DW_LNCT_size");
break;
}
} break;
case DW_LNCT_MD5: {
switch (formCode) {
case DW_FORM_data16:
attrValue = readBytes(entries, 16);
break;
default:
FOLLY_SAFE_CHECK(false, "Unexpected form for DW_LNCT_MD5");
break;
}
} break;
default:
// TODO: skip over vendor data as specified by the form instead.
FOLLY_SAFE_CHECK(false, "Unexpected vendor content type code");
break;
}
return {
.contentTypeCode = contentTypeCode,
.formCode = formCode,
.attrValue = attrValue,
};
}
void Dwarf::LineNumberVM::init() { void Dwarf::LineNumberVM::init() {
version_ = read<uint16_t>(data_); version_ = read<uint16_t>(data_);
FOLLY_SAFE_CHECK( FOLLY_SAFE_CHECK(
version_ >= 2 && version_ <= 4, "invalid version in line number VM"); version_ >= 2 && version_ <= 5, "invalid version in line number VM");
if (version_ == 5) {
auto addressSize = read<uint8_t>(data_);
FOLLY_SAFE_CHECK(
addressSize == sizeof(uintptr_t),
"Unexpected Line Number Table address_size");
auto segment_selector_size = read<uint8_t>(data_);
FOLLY_SAFE_CHECK(segment_selector_size == 0, "Segments not supported");
}
uint64_t headerLength = readOffset(data_, is64Bit_); uint64_t headerLength = readOffset(data_, is64Bit_);
FOLLY_SAFE_CHECK( FOLLY_SAFE_CHECK(
headerLength <= data_.size(), "invalid line number VM header length"); headerLength <= data_.size(), "invalid line number VM header length");
...@@ -1147,7 +1577,7 @@ void Dwarf::LineNumberVM::init() { ...@@ -1147,7 +1577,7 @@ void Dwarf::LineNumberVM::init() {
data_.assign(header.end(), data_.end()); data_.assign(header.end(), data_.end());
minLength_ = read<uint8_t>(header); minLength_ = read<uint8_t>(header);
if (version_ == 4) { // Version 2 and 3 records don't have this if (version_ >= 4) { // Version 2 and 3 records don't have this
uint8_t maxOpsPerInstruction = read<uint8_t>(header); uint8_t maxOpsPerInstruction = read<uint8_t>(header);
FOLLY_SAFE_CHECK(maxOpsPerInstruction == 1, "VLIW not supported"); FOLLY_SAFE_CHECK(maxOpsPerInstruction == 1, "VLIW not supported");
} }
...@@ -1159,24 +1589,66 @@ void Dwarf::LineNumberVM::init() { ...@@ -1159,24 +1589,66 @@ void Dwarf::LineNumberVM::init() {
standardOpcodeLengths_ = reinterpret_cast<const uint8_t*>(header.data()); standardOpcodeLengths_ = reinterpret_cast<const uint8_t*>(header.data());
header.advance(opcodeBase_ - 1); header.advance(opcodeBase_ - 1);
if (version_ <= 4) {
// We don't want to use heap, so we don't keep an unbounded amount of state. // We don't want to use heap, so we don't keep an unbounded amount of state.
// We'll just skip over include directories and file names here, and // We'll just skip over include directories and file names here, and
// we'll loop again when we actually need to retrieve one. // we'll loop again when we actually need to retrieve one.
folly::StringPiece sp; folly::StringPiece sp;
const char* tmp = header.data(); const char* tmp = header.data();
includeDirectoryCount_ = 0; v4_.includeDirectoryCount = 0;
while (!(sp = readNullTerminated(header)).empty()) { while (!(sp = readNullTerminated(header)).empty()) {
++includeDirectoryCount_; ++v4_.includeDirectoryCount;
} }
includeDirectories_.assign(tmp, header.data()); v4_.includeDirectories.assign(tmp, header.data());
tmp = header.data(); tmp = header.data();
FileName fn; FileName fn;
fileNameCount_ = 0; v4_.fileNameCount = 0;
while (readFileName(header, fn)) { while (readFileName(header, fn)) {
++fileNameCount_; ++v4_.fileNameCount;
}
v4_.fileNames.assign(tmp, header.data());
} else if (version_ == 5) {
v5_.directoryEntryFormatCount = read<uint8_t>(header);
const char* tmp = header.data();
for (uint8_t i = 0; i < v5_.directoryEntryFormatCount; i++) {
// A sequence of directory entry format descriptions. Each description
// consists of a pair of ULEB128 values:
readULEB(header); // A content type code
readULEB(header); // A form code using the attribute form codes
}
v5_.directoryEntryFormat.assign(tmp, header.data());
v5_.directoriesCount = readULEB(header);
tmp = header.data();
for (uint64_t i = 0; i < v5_.directoriesCount; i++) {
folly::StringPiece format = v5_.directoryEntryFormat;
for (uint8_t f = 0; f < v5_.directoryEntryFormatCount; f++) {
readLineNumberAttribute(
is64Bit_, format, header, debugStr_, debugLineStr_);
}
}
v5_.directories.assign(tmp, header.data());
v5_.fileNameEntryFormatCount = read<uint8_t>(header);
tmp = header.data();
for (uint8_t i = 0; i < v5_.fileNameEntryFormatCount; i++) {
// A sequence of file entry format descriptions. Each description
// consists of a pair of ULEB128 values:
readULEB(header); // A content type code
readULEB(header); // A form code using the attribute form codes
}
v5_.fileNameEntryFormat.assign(tmp, header.data());
v5_.fileNamesCount = readULEB(header);
tmp = header.data();
for (uint64_t i = 0; i < v5_.fileNamesCount; i++) {
folly::StringPiece format = v5_.fileNameEntryFormat;
for (uint8_t f = 0; f < v5_.fileNameEntryFormatCount; f++) {
readLineNumberAttribute(
is64Bit_, format, header, debugStr_, debugLineStr_);
}
}
v5_.fileNames.assign(tmp, header.data());
} }
fileNames_.assign(tmp, header.data());
} }
bool Dwarf::LineNumberVM::next(folly::StringPiece& program) { bool Dwarf::LineNumberVM::next(folly::StringPiece& program) {
...@@ -1190,11 +1662,11 @@ bool Dwarf::LineNumberVM::next(folly::StringPiece& program) { ...@@ -1190,11 +1662,11 @@ bool Dwarf::LineNumberVM::next(folly::StringPiece& program) {
Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName( Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName(
uint64_t index) const { uint64_t index) const {
if (version_ <= 4) {
FOLLY_SAFE_CHECK(index != 0, "invalid file index 0"); FOLLY_SAFE_CHECK(index != 0, "invalid file index 0");
FileName fn; FileName fn;
if (index <= fileNameCount_) { if (index <= v4_.fileNameCount) {
folly::StringPiece fileNames = fileNames_; folly::StringPiece fileNames = v4_.fileNames;
for (; index; --index) { for (; index; --index) {
if (!readFileName(fileNames, fn)) { if (!readFileName(fileNames, fn)) {
abort(); abort();
...@@ -1203,7 +1675,7 @@ Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName( ...@@ -1203,7 +1675,7 @@ Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName(
return fn; return fn;
} }
index -= fileNameCount_; index -= v4_.fileNameCount;
folly::StringPiece program = data_; folly::StringPiece program = data_;
for (; index; --index) { for (; index; --index) {
...@@ -1211,18 +1683,46 @@ Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName( ...@@ -1211,18 +1683,46 @@ Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName(
} }
return fn; return fn;
} else {
FileName fn;
FOLLY_SAFE_CHECK(index < v5_.fileNamesCount, "invalid file index");
folly::StringPiece fileNames = v5_.fileNames;
for (uint64_t i = 0; i < v5_.fileNamesCount; i++) {
folly::StringPiece format = v5_.fileNameEntryFormat;
for (uint8_t f = 0; f < v5_.fileNameEntryFormatCount; f++) {
auto attr = readLineNumberAttribute(
is64Bit_, format, fileNames, debugStr_, debugLineStr_);
if (i == index) {
switch (attr.contentTypeCode) {
case DW_LNCT_path:
fn.relativeName = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_LNCT_directory_index:
fn.directoryIndex = boost::get<uint64_t>(attr.attrValue);
break;
}
}
}
}
return fn;
}
} }
folly::StringPiece Dwarf::LineNumberVM::getIncludeDirectory( folly::StringPiece Dwarf::LineNumberVM::getIncludeDirectory(
uint64_t index) const { uint64_t index) const {
if (version_ <= 4) {
if (index == 0) { if (index == 0) {
// In DWARF <= 4 the current directory is not represented in the
// directories field and a directory index of 0 implicitly referred to
// that directory as found in the DW_AT_comp_dir attribute of the
// compilation unit debugging information entry.
return folly::StringPiece(); return folly::StringPiece();
} }
FOLLY_SAFE_CHECK( FOLLY_SAFE_CHECK(
index <= includeDirectoryCount_, "invalid include directory"); index <= v4_.includeDirectoryCount, "invalid include directory");
folly::StringPiece includeDirectories = includeDirectories_; folly::StringPiece includeDirectories = v4_.includeDirectories;
folly::StringPiece dir; folly::StringPiece dir;
for (; index; --index) { for (; index; --index) {
dir = readNullTerminated(includeDirectories); dir = readNullTerminated(includeDirectories);
...@@ -1232,6 +1732,23 @@ folly::StringPiece Dwarf::LineNumberVM::getIncludeDirectory( ...@@ -1232,6 +1732,23 @@ folly::StringPiece Dwarf::LineNumberVM::getIncludeDirectory(
} }
return dir; return dir;
} else {
FOLLY_SAFE_CHECK(index < v5_.directoriesCount, "invalid file index");
folly::StringPiece directories = v5_.directories;
for (uint64_t i = 0; i < v5_.directoriesCount; i++) {
folly::StringPiece format = v5_.directoryEntryFormat;
for (uint8_t f = 0; f < v5_.directoryEntryFormatCount; f++) {
auto attr = readLineNumberAttribute(
is64Bit_, format, directories, debugStr_, debugLineStr_);
if (i == index && attr.contentTypeCode == DW_LNCT_path) {
return boost::get<folly::StringPiece>(attr.attrValue);
}
}
}
// This could only happen if DWARF5's directory_entry_format doesn't contain
// a DW_LNCT_path. Highly unlikely, but we shouldn't crash.
return folly::StringPiece("<directory not found>");
}
} }
bool Dwarf::LineNumberVM::readFileName( bool Dwarf::LineNumberVM::readFileName(
...@@ -1273,6 +1790,7 @@ bool Dwarf::LineNumberVM::nextDefineFile( ...@@ -1273,6 +1790,7 @@ bool Dwarf::LineNumberVM::nextDefineFile(
--length; --length;
if (opcode == DW_LNE_define_file) { if (opcode == DW_LNE_define_file) {
FOLLY_SAFE_CHECK(version_ < 5, "DW_LNE_define_file deprecated in DWARF5");
FOLLY_SAFE_CHECK( FOLLY_SAFE_CHECK(
readFileName(program, fn), readFileName(program, fn),
"invalid empty file in DW_LNE_define_file"); "invalid empty file in DW_LNE_define_file");
...@@ -1381,6 +1899,7 @@ Dwarf::LineNumberVM::StepResult Dwarf::LineNumberVM::step( ...@@ -1381,6 +1899,7 @@ Dwarf::LineNumberVM::StepResult Dwarf::LineNumberVM::step(
address_ = read<uintptr_t>(program); address_ = read<uintptr_t>(program);
return CONTINUE; return CONTINUE;
case DW_LNE_define_file: case DW_LNE_define_file:
FOLLY_SAFE_CHECK(version_ < 5, "DW_LNE_define_file deprecated in DWARF5");
// We can't process DW_LNE_define_file here, as it would require us to // We can't process DW_LNE_define_file here, as it would require us to
// use unbounded amounts of state (ie. use the heap). We'll do a second // use unbounded amounts of state (ie. use the heap). We'll do a second
// pass (using nextDefineFile()) if necessary. // pass (using nextDefineFile()) if necessary.
...@@ -1397,6 +1916,17 @@ Dwarf::LineNumberVM::StepResult Dwarf::LineNumberVM::step( ...@@ -1397,6 +1916,17 @@ Dwarf::LineNumberVM::StepResult Dwarf::LineNumberVM::step(
return CONTINUE; return CONTINUE;
} }
Path Dwarf::LineNumberVM::getFullFileName(uint64_t index) const {
auto fn = getFileName(index);
return Path(
// DWARF <= 4: the current dir is not represented in the CU's Line Number
// Program Header and relies on the CU's DW_AT_comp_dir.
// DWARF 5: the current directory is explicitly present.
version_ == 5 ? "" : compilationDirectory_,
getIncludeDirectory(fn.directoryIndex),
fn.relativeName);
}
bool Dwarf::LineNumberVM::findAddress( bool Dwarf::LineNumberVM::findAddress(
uintptr_t target, Path& file, uint64_t& line) { uintptr_t target, Path& file, uint64_t& line) {
folly::StringPiece program = data_; folly::StringPiece program = data_;
...@@ -1430,14 +1960,17 @@ bool Dwarf::LineNumberVM::findAddress( ...@@ -1430,14 +1960,17 @@ bool Dwarf::LineNumberVM::findAddress(
// Found it! Note that ">" is indeed correct (not ">="), as each // Found it! Note that ">" is indeed correct (not ">="), as each
// sequence is guaranteed to have one entry past-the-end (emitted by // sequence is guaranteed to have one entry past-the-end (emitted by
// DW_LNE_end_sequence) // DW_LNE_end_sequence)
if (prevFile == 0) {
// NOTE: In DWARF <= 4 the file register is non-zero.
// See DWARF 4: 6.2.4 The Line Number Program Header
// "The line number program assigns numbers to each of the file
// entries in order, beginning with 1, and uses those numbers instead
// of file names in the file register."
// DWARF 5 has a different include directory/file header and 0 is valid.
if (version_ <= 4 && prevFile == 0) {
return false; return false;
} }
auto fn = getFileName(prevFile); file = getFullFileName(prevFile);
file = Path(
compilationDirectory_,
getIncludeDirectory(fn.directoryIndex),
fn.relativeName);
line = prevLine; line = prevLine;
return true; return true;
} }
......
...@@ -165,6 +165,9 @@ class Dwarf { ...@@ -165,6 +165,9 @@ class Dwarf {
/** Get an ELF section by name. */ /** Get an ELF section by name. */
folly::StringPiece getSection(const char* name) const; folly::StringPiece getSection(const char* name) const;
detail::CompilationUnit getCompilationUnit(uint64_t offset) const;
detail::CompilationUnit findCompilationUnit(uint64_t targetOffset) const;
/** cu must exist during the life cycle of created detail::Die. */ /** cu must exist during the life cycle of created detail::Die. */
detail::Die getDieAtOffset( detail::Die getDieAtOffset(
const detail::CompilationUnit& cu, uint64_t offset) const; const detail::CompilationUnit& cu, uint64_t offset) const;
...@@ -191,6 +194,12 @@ class Dwarf { ...@@ -191,6 +194,12 @@ class Dwarf {
*/ */
detail::DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const; detail::DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const;
detail::Attribute readAttribute(
const detail::CompilationUnit& cu,
const detail::Die& die,
detail::AttributeSpec spec,
folly::StringPiece& info) const;
/** /**
* Iterates over all attributes of a debugging info entry, calling the given * Iterates over all attributes of a debugging info entry, calling the given
* callable for each. If all attributes are visited, then return the offset of * callable for each. If all attributes are visited, then return the offset of
...@@ -212,18 +221,24 @@ class Dwarf { ...@@ -212,18 +221,24 @@ class Dwarf {
* .debug_ranges. * .debug_ranges.
*/ */
bool isAddrInRangeList( bool isAddrInRangeList(
const detail::CompilationUnit& cu,
uint64_t address, uint64_t address,
folly::Optional<uint64_t> baseAddr, folly::Optional<uint64_t> baseAddr,
size_t offset, size_t offset,
uint8_t addrSize) const; uint8_t addrSize) const;
const ElfFile* elf_; const ElfFile* elf_;
const folly::StringPiece debugInfo_; // .debug_info
const folly::StringPiece debugAbbrev_; // .debug_abbrev const folly::StringPiece debugAbbrev_; // .debug_abbrev
const folly::StringPiece debugLine_; // .debug_line const folly::StringPiece debugAddr_; // .debug_addr (DWARF 5)
const folly::StringPiece debugStr_; // .debug_str
const folly::StringPiece debugAranges_; // .debug_aranges const folly::StringPiece debugAranges_; // .debug_aranges
const folly::StringPiece debugInfo_; // .debug_info
const folly::StringPiece debugLine_; // .debug_line
const folly::StringPiece debugLineStr_; // .debug_line_str (DWARF 5)
const folly::StringPiece debugLoclists_; // .debug_loclists (DWARF 5)
const folly::StringPiece debugRanges_; // .debug_ranges const folly::StringPiece debugRanges_; // .debug_ranges
const folly::StringPiece debugRnglists_; // .debug_rnglists (DWARF 5)
const folly::StringPiece debugStr_; // .debug_str
const folly::StringPiece debugStrOffsets_; // .debug_str_offsets (DWARF 5)
}; };
class Dwarf::Section { class Dwarf::Section {
...@@ -250,15 +265,15 @@ class Dwarf::Section { ...@@ -250,15 +265,15 @@ class Dwarf::Section {
class Dwarf::LineNumberVM { class Dwarf::LineNumberVM {
public: public:
LineNumberVM( LineNumberVM(
folly::StringPiece data, folly::StringPiece compilationDirectory); folly::StringPiece data,
folly::StringPiece compilationDirectory,
folly::StringPiece debugStr,
folly::StringPiece debugLineStr);
bool findAddress(uintptr_t target, Path& file, uint64_t& line); bool findAddress(uintptr_t target, Path& file, uint64_t& line);
/** Gets full file name at given index including directory. */ /** Gets full file name at given index including directory. */
Path getFullFileName(uint64_t index) const { Path getFullFileName(uint64_t index) const;
auto fn = getFileName(index);
return Path({}, getIncludeDirectory(fn.directoryIndex), fn.relativeName);
}
private: private:
void init(); void init();
...@@ -304,6 +319,8 @@ class Dwarf::LineNumberVM { ...@@ -304,6 +319,8 @@ class Dwarf::LineNumberVM {
bool is64Bit_; bool is64Bit_;
folly::StringPiece data_; folly::StringPiece data_;
folly::StringPiece compilationDirectory_; folly::StringPiece compilationDirectory_;
folly::StringPiece debugStr_; // needed for DWARF 5
folly::StringPiece debugLineStr_; // DWARF 5
// Header // Header
uint16_t version_; uint16_t version_;
...@@ -314,11 +331,25 @@ class Dwarf::LineNumberVM { ...@@ -314,11 +331,25 @@ class Dwarf::LineNumberVM {
uint8_t opcodeBase_; uint8_t opcodeBase_;
const uint8_t* standardOpcodeLengths_; const uint8_t* standardOpcodeLengths_;
folly::StringPiece includeDirectories_; // 6.2.4 The Line Number Program Header.
size_t includeDirectoryCount_; struct {
size_t includeDirectoryCount;
folly::StringPiece fileNames_; folly::StringPiece includeDirectories;
size_t fileNameCount_; size_t fileNameCount;
folly::StringPiece fileNames;
} v4_;
struct {
uint8_t directoryEntryFormatCount;
folly::StringPiece directoryEntryFormat;
uint64_t directoriesCount;
folly::StringPiece directories;
uint8_t fileNameEntryFormatCount;
folly::StringPiece fileNameEntryFormat;
uint64_t fileNamesCount;
folly::StringPiece fileNames;
} v5_;
// State machine registers // State machine registers
uint64_t address_; uint64_t address_;
......
...@@ -261,6 +261,8 @@ class ElfFile { ...@@ -261,6 +261,8 @@ class ElfFile {
/** Find the section containing the given address */ /** Find the section containing the given address */
const ElfShdr* getSectionContainingAddress(ElfAddr addr) const noexcept; const ElfShdr* getSectionContainingAddress(ElfAddr addr) const noexcept;
const char* filepath() const { return filepath_; }
private: private:
OpenResult init() noexcept; OpenResult init() noexcept;
void reset() noexcept; void reset() noexcept;
......
...@@ -151,9 +151,12 @@ void expectFrameEq( ...@@ -151,9 +151,12 @@ void expectFrameEq(
auto demangled = folly::demangle(frame.name); auto demangled = folly::demangle(frame.name);
EXPECT_TRUE(demangled == shortName || demangled == fullName) EXPECT_TRUE(demangled == shortName || demangled == fullName)
<< "Found: demangled=" << demangled << "Found: demangled=" << demangled
<< " expecting shortName=" << shortName << " or fullName=" << fullName; << " expecting shortName=" << shortName << " or fullName=" << fullName
<< " address: " << frame.addr << " hex(address): " << std::hex
<< frame.addr;
EXPECT_EQ(normalizePath(frame.location.file.toString()), normalizePath(file)) EXPECT_EQ(normalizePath(frame.location.file.toString()), normalizePath(file))
<< ' ' << fullName; << ' ' << fullName << " address: " << frame.addr
<< " hex(address): " << std::hex << frame.addr;
EXPECT_EQ(frame.location.line, lineno) << ' ' << fullName; EXPECT_EQ(frame.location.line, lineno) << ' ' << fullName;
} }
...@@ -474,6 +477,12 @@ TEST(Dwarf, FindParameterNames) { ...@@ -474,6 +477,12 @@ TEST(Dwarf, FindParameterNames) {
extraInlineFrames, extraInlineFrames,
[&](const folly::StringPiece name) { names.push_back(name); }); [&](const folly::StringPiece name) { names.push_back(name); });
if (names.empty()) {
// When using -fsplit-dwarf-inlining info about parameters will not be
// emitted for the inlined debug info.
return;
}
ASSERT_EQ(2, names.size()); ASSERT_EQ(2, names.size());
ASSERT_EQ("a", names[0]); ASSERT_EQ("a", names[0]);
ASSERT_EQ("b", names[1]); ASSERT_EQ("b", names[1]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment