#include <folly/debugging/symbolizer/Dwarf.h>
#include <array>
#include <type_traits>
#include <folly/Optional.h>
#include <folly/debugging/symbolizer/DwarfImpl.h>
#include <folly/debugging/symbolizer/DwarfSection.h>
#include <folly/lang/SafeAssert.h>
#include <folly/portability/Config.h>
#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF
#include <dwarf.h>
namespace folly {
namespace symbolizer {
Dwarf::Dwarf(ElfCacheBase* elfCache, const ElfFile* elf)
: elfCache_(elfCache),
defaultDebugSections_{
.elf = elf,
.debugCuIndex = getElfSection(elf, ".debug_cu_index"),
.debugAbbrev = getElfSection(elf, ".debug_abbrev"),
.debugAddr = getElfSection(elf, ".debug_addr"),
.debugAranges = getElfSection(elf, ".debug_aranges"),
.debugInfo = getElfSection(elf, ".debug_info"),
.debugLine = getElfSection(elf, ".debug_line"),
.debugLineStr = getElfSection(elf, ".debug_line_str"),
.debugLoclists = getElfSection(elf, ".debug_loclists"),
.debugRanges = getElfSection(elf, ".debug_ranges"),
.debugRnglists = getElfSection(elf, ".debug_rnglists"),
.debugStr = getElfSection(elf, ".debug_str"),
.debugStrOffsets = getElfSection(elf, ".debug_str_offsets")} {
if (defaultDebugSections_.debugInfo.empty() ||
defaultDebugSections_.debugAbbrev.empty() ||
defaultDebugSections_.debugLine.empty() ||
defaultDebugSections_.debugStr.empty()) {
defaultDebugSections_.elf = nullptr;
}
}
namespace {
bool findDebugInfoOffset(
uintptr_t address, StringPiece aranges, uint64_t& offset) {
DwarfSection section(aranges);
folly::StringPiece chunk;
while (section.next(chunk)) {
auto version = read<uint16_t>(chunk);
if (version != 2) {
FOLLY_SAFE_DFATAL("invalid aranges version: ", version);
return false;
}
offset = readOffset(chunk, section.is64Bit());
auto addressSize = read<uint8_t>(chunk);
if (addressSize != sizeof(uintptr_t)) {
FOLLY_SAFE_DFATAL("invalid address size: ", addressSize);
return false;
}
auto segmentSize = read<uint8_t>(chunk);
if (segmentSize != 0) {
FOLLY_SAFE_DFATAL("segmented architecture not supported: ", segmentSize);
return false;
}
{
size_t alignment = 2 * sizeof(uintptr_t);
size_t remainder = (chunk.data() - aranges.data()) % alignment;
if (remainder != 0) {
if (alignment - remainder > chunk.size()) {
FOLLY_SAFE_DFATAL(
"invalid padding: alignment: ",
alignment,
" remainder: ",
remainder,
" chunk.size(): ",
chunk.size());
return false;
}
chunk.advance(alignment - remainder);
}
}
for (;;) {
auto start = read<uintptr_t>(chunk);
auto length = read<uintptr_t>(chunk);
if (start == 0 && length == 0) {
break;
}
if (address >= start && address < start + length) {
return true;
}
}
}
return false;
}
}
bool Dwarf::findAddress(
uintptr_t address,
LocationInfoMode mode,
SymbolizedFrame& frame,
folly::Range<SymbolizedFrame*> inlineFrames,
folly::FunctionRef<void(const folly::StringPiece name)> eachParameterName)
const {
if (mode == LocationInfoMode::DISABLED) {
return false;
}
if (!defaultDebugSections_.elf) {
return false;
}
if (!defaultDebugSections_.debugAranges.empty()) {
uint64_t offset = 0;
if (findDebugInfoOffset(
address, defaultDebugSections_.debugAranges, offset)) {
auto unit = getCompilationUnits(
elfCache_,
defaultDebugSections_,
offset,
mode == LocationInfoMode::FULL_WITH_INLINE);
if (unit.mainCompilationUnit.unitType != DW_UT_compile &&
unit.mainCompilationUnit.unitType != DW_UT_skeleton) {
return false;
}
DwarfImpl impl(elfCache_, unit, mode);
return impl.findLocation(
address,
frame,
inlineFrames,
eachParameterName,
false );
} else if (mode == LocationInfoMode::FAST) {
return false;
} else {
FOLLY_SAFE_DCHECK(
mode == LocationInfoMode::FULL ||
mode == LocationInfoMode::FULL_WITH_INLINE,
"unexpected mode");
}
}
uint64_t offset = 0;
while (offset < defaultDebugSections_.debugInfo.size()) {
auto unit = getCompilationUnits(
elfCache_,
defaultDebugSections_,
offset,
mode == LocationInfoMode::FULL_WITH_INLINE);
offset += unit.mainCompilationUnit.size;
if (unit.mainCompilationUnit.unitType != DW_UT_compile &&
unit.mainCompilationUnit.unitType != DW_UT_skeleton) {
continue;
}
DwarfImpl impl(elfCache_, unit, mode);
if (impl.findLocation(
address,
frame,
inlineFrames,
eachParameterName,
true )) {
return true;
}
}
return false;
}
}
}
#endif