proxygen
folly::symbolizer::Dwarf Class Reference

#include <Dwarf.h>

Classes

struct  DIEAbbreviation
 
class  LineNumberVM
 
struct  LocationInfo
 
class  Path
 
class  Section
 

Public Types

enum  LocationInfoMode { LocationInfoMode::DISABLED, LocationInfoMode::FAST, LocationInfoMode::FULL }
 

Public Member Functions

 Dwarf (const ElfFile *elf)
 
bool findAddress (uintptr_t address, LocationInfo &info, LocationInfoMode mode) const
 

Private Types

typedef boost::variant< uint64_t, folly::StringPieceAttributeValue
 

Private Member Functions

void init ()
 
bool findLocation (uintptr_t address, StringPiece &infoEntry, LocationInfo &info) const
 
DIEAbbreviation getAbbreviation (uint64_t code, uint64_t offset) const
 
AttributeValue readAttributeValue (folly::StringPiece &sp, uint64_t form, bool is64Bit) const
 
bool getSection (const char *name, folly::StringPiece *section) const
 
folly::StringPiece getStringFromStringSection (uint64_t offset) const
 

Static Private Member Functions

static bool findDebugInfoOffset (uintptr_t address, StringPiece aranges, uint64_t &offset)
 
static bool readAbbreviation (folly::StringPiece &sp, DIEAbbreviation &abbr)
 
static DIEAbbreviation::Attribute readAttribute (folly::StringPiece &sp)
 

Private Attributes

const ElfFileelf_
 
folly::StringPiece info_
 
folly::StringPiece abbrev_
 
folly::StringPiece aranges_
 
folly::StringPiece line_
 
folly::StringPiece strings_
 

Detailed Description

DWARF record parser.

We only implement enough DWARF functionality to convert from PC address to file and line number information.

This means (although they're not part of the public API of this class), we can parse Debug Information Entries (DIEs), abbreviations, attributes (of all forms), and we can interpret bytecode for the line number VM.

We can interpret DWARF records of version 2, 3, or 4, although we don't actually support many of the version 4 features (such as VLIW, multiple operations per instruction)

Note that the DWARF record parser does not allocate heap memory at all. This is on purpose: you can use the parser from memory-constrained situations (such as an exception handler for std::out_of_memory) If it weren't for this requirement, some things would be much simpler: the Path class would be unnecessary and would be replaced with a std::string; the list of file names in the line number VM would be kept as a vector of strings instead of re-executing the program to look for DW_LNE_define_file instructions, etc.

Definition at line 52 of file Dwarf.h.

Member Typedef Documentation

Definition at line 275 of file Dwarf.h.

Member Enumeration Documentation

Enumerator
DISABLED 
FAST 
FULL 

Definition at line 111 of file Dwarf.h.

111  {
112  // Don't resolve location info.
113  DISABLED,
114  // Perform CU lookup using .debug_aranges (might be incomplete).
115  FAST,
116  // Scan all CU in .debug_info (slow!) on .debug_aranges lookup failure.
117  FULL,
118  };

Constructor & Destructor Documentation

folly::symbolizer::Dwarf::Dwarf ( const ElfFile elf)
explicit

Create a DWARF parser around an ELF file.

Definition at line 32 of file Dwarf.cpp.

References init().

32  : elf_(elf) {
33  init();
34 }
const ElfFile * elf_
Definition: Dwarf.h:145

Member Function Documentation

bool folly::symbolizer::Dwarf::findAddress ( uintptr_t  address,
LocationInfo info,
LocationInfoMode  mode 
) const

Find the file and line number information corresponding to address.

Definition at line 575 of file Dwarf.cpp.

References folly::Range< Iter >::advance(), aranges_, DISABLED, elf_, folly::Range< Iter >::empty(), FAST, findDebugInfoOffset(), findLocation(), FULL, folly::symbolizer::Dwarf::LocationInfo::hasFileAndLine, info_, and uint64_t.

Referenced by dummy().

578  {
579  locationInfo = LocationInfo();
580 
582  return false;
583  }
584 
585  if (!elf_) { // No file.
586  return false;
587  }
588 
589  if (!aranges_.empty()) {
590  // Fast path: find the right .debug_info entry by looking up the
591  // address in .debug_aranges.
592  uint64_t offset = 0;
593  if (findDebugInfoOffset(address, aranges_, offset)) {
594  // Read compilation unit header from .debug_info
595  folly::StringPiece infoEntry(info_);
596  infoEntry.advance(offset);
597  findLocation(address, infoEntry, locationInfo);
598  return locationInfo.hasFileAndLine;
599  } else if (mode == LocationInfoMode::FAST) {
600  // NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries
601  // in .debug_aranges for some functions, but always generates
602  // .debug_info entries. Scanning .debug_info is slow, so fall back to
603  // it only if such behavior is requested via LocationInfoMode.
604  return false;
605  } else {
606  DCHECK(mode == LocationInfoMode::FULL);
607  // Fall back to the linear scan.
608  }
609  }
610 
611  // Slow path (linear scan): Iterate over all .debug_info entries
612  // and look for the address in each compilation unit.
613  folly::StringPiece infoEntry(info_);
614  while (!infoEntry.empty() && !locationInfo.hasFileAndLine) {
615  findLocation(address, infoEntry, locationInfo);
616  }
617  return locationInfo.hasFileAndLine;
618 }
folly::Optional< PskKeyExchangeMode > mode
folly::StringPiece info_
Definition: Dwarf.h:285
constexpr bool empty() const
Definition: Range.h:443
folly::StringPiece aranges_
Definition: Dwarf.h:287
bool findLocation(uintptr_t address, StringPiece &infoEntry, LocationInfo &info) const
Definition: Dwarf.cpp:485
const ElfFile * elf_
Definition: Dwarf.h:145
static bool findDebugInfoOffset(uintptr_t address, StringPiece aranges, uint64_t &offset)
Definition: Dwarf.cpp:442
bool folly::symbolizer::Dwarf::findDebugInfoOffset ( uintptr_t  address,
StringPiece  aranges,
uint64_t offset 
)
staticprivate

Find in .debug_aranges and return the offset in .debug_info for compilation unit to which this address belongs.

Definition at line 442 of file Dwarf.cpp.

References folly::Range< Iter >::data(), FOLLY_SAFE_CHECK, folly::symbolizer::Dwarf::Section::is64Bit(), folly::symbolizer::Dwarf::Section::next(), start, and version.

Referenced by findAddress().

445  {
446  Section arangesSection(aranges);
447  folly::StringPiece chunk;
448  while (arangesSection.next(chunk)) {
449  auto version = read<uint16_t>(chunk);
450  FOLLY_SAFE_CHECK(version == 2, "invalid aranges version");
451 
452  offset = readOffset(chunk, arangesSection.is64Bit());
453  auto addressSize = read<uint8_t>(chunk);
454  FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
455  auto segmentSize = read<uint8_t>(chunk);
456  FOLLY_SAFE_CHECK(segmentSize == 0, "segmented architecture not supported");
457 
458  // Padded to a multiple of 2 addresses.
459  // Strangely enough, this is the only place in the DWARF spec that requires
460  // padding.
461  skipPadding(chunk, aranges.data(), 2 * sizeof(uintptr_t));
462  for (;;) {
463  auto start = read<uintptr_t>(chunk);
464  auto length = read<uintptr_t>(chunk);
465 
466  if (start == 0 && length == 0) {
467  break;
468  }
469 
470  // Is our address in this range?
471  if (address >= start && address < start + length) {
472  return true;
473  }
474  }
475  }
476  return false;
477 }
ProtocolVersion version
constexpr Iter data() const
Definition: Range.h:446
#define FOLLY_SAFE_CHECK(expr, msg)
Definition: SafeAssert.h:35
auto start
bool folly::symbolizer::Dwarf::findLocation ( uintptr_t  address,
StringPiece infoEntry,
LocationInfo locationInfo 
) const
private

Find the for in the compilation unit represented by the .debug_info entry. Returns whether the address was found. Advances to the next entry in .debug_info.

Definition at line 485 of file Dwarf.cpp.

References folly::Range< Iter >::advance(), folly::Range< Iter >::begin(), folly::Range< Iter >::empty(), folly::Range< Iter >::end(), folly::symbolizer::Dwarf::LocationInfo::file, folly::symbolizer::Dwarf::LineNumberVM::findAddress(), FOLLY_SAFE_CHECK, folly::symbolizer::Dwarf::DIEAbbreviation::Attribute::form, getAbbreviation(), folly::symbolizer::Dwarf::LocationInfo::hasFileAndLine, folly::symbolizer::Dwarf::LocationInfo::hasMainFile, folly::symbolizer::Dwarf::Section::is64Bit(), folly::symbolizer::Dwarf::LocationInfo::line, line_, folly::symbolizer::Dwarf::LocationInfo::mainFile, folly::symbolizer::Dwarf::DIEAbbreviation::Attribute::name, folly::symbolizer::Dwarf::Section::next(), folly::symbolizer::Dwarf::Path::Path(), readAttribute(), readAttributeValue(), uint64_t, val, and version.

Referenced by findAddress().

488  {
489  // For each compilation unit compiled with a DWARF producer, a
490  // contribution is made to the .debug_info section of the object
491  // file. Each such contribution consists of a compilation unit
492  // header (see Section 7.5.1.1) followed by a single
493  // DW_TAG_compile_unit or DW_TAG_partial_unit debugging information
494  // entry, together with its children.
495 
496  // 7.5.1.1 Compilation Unit Header
497  // 1. unit_length (4B or 12B): read by Section::next
498  // 2. version (2B)
499  // 3. debug_abbrev_offset (4B or 8B): offset into the .debug_abbrev section
500  // 4. address_size (1B)
501 
502  Section debugInfoSection(infoEntry);
503  folly::StringPiece chunk;
504  FOLLY_SAFE_CHECK(debugInfoSection.next(chunk), "invalid debug info");
505 
506  auto version = read<uint16_t>(chunk);
507  FOLLY_SAFE_CHECK(version >= 2 && version <= 4, "invalid info version");
508  uint64_t abbrevOffset = readOffset(chunk, debugInfoSection.is64Bit());
509  auto addressSize = read<uint8_t>(chunk);
510  FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
511 
512  // We survived so far. The first (and only) DIE should be DW_TAG_compile_unit
513  // NOTE: - binutils <= 2.25 does not issue DW_TAG_partial_unit.
514  // - dwarf compression tools like `dwz` may generate it.
515  // TODO(tudorb): Handle DW_TAG_partial_unit?
516  auto code = readULEB(chunk);
517  FOLLY_SAFE_CHECK(code != 0, "invalid code");
518  auto abbr = getAbbreviation(code, abbrevOffset);
520  abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
521  // Skip children entries, advance to the next compilation unit entry.
522  infoEntry.advance(chunk.end() - infoEntry.begin());
523 
524  // Read attributes, extracting the few we care about
525  bool foundLineOffset = false;
526  uint64_t lineOffset = 0;
527  folly::StringPiece compilationDirectory;
528  folly::StringPiece mainFileName;
529 
530  DIEAbbreviation::Attribute attr;
531  folly::StringPiece attributes = abbr.attributes;
532  for (;;) {
533  attr = readAttribute(attributes);
534  if (attr.name == 0 && attr.form == 0) {
535  break;
536  }
537  auto val = readAttributeValue(chunk, attr.form, debugInfoSection.is64Bit());
538  switch (attr.name) {
539  case DW_AT_stmt_list:
540  // Offset in .debug_line for the line number VM program for this
541  // compilation unit
542  lineOffset = boost::get<uint64_t>(val);
543  foundLineOffset = true;
544  break;
545  case DW_AT_comp_dir:
546  // Compilation directory
547  compilationDirectory = boost::get<folly::StringPiece>(val);
548  break;
549  case DW_AT_name:
550  // File name of main file being compiled
551  mainFileName = boost::get<folly::StringPiece>(val);
552  break;
553  }
554  }
555 
556  if (!mainFileName.empty()) {
557  locationInfo.hasMainFile = true;
558  locationInfo.mainFile = Path(compilationDirectory, "", mainFileName);
559  }
560 
561  if (!foundLineOffset) {
562  return false;
563  }
564 
565  folly::StringPiece lineSection(line_);
566  lineSection.advance(lineOffset);
567  LineNumberVM lineVM(lineSection, compilationDirectory);
568 
569  // Execute line number VM program to find file and line
570  locationInfo.hasFileAndLine =
571  lineVM.findAddress(address, locationInfo.file, locationInfo.line);
572  return locationInfo.hasFileAndLine;
573 }
DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const
Definition: Dwarf.cpp:364
static DIEAbbreviation::Attribute readAttribute(folly::StringPiece &sp)
Definition: Dwarf.cpp:360
double val
Definition: String.cpp:273
folly::StringPiece line_
Definition: Dwarf.h:288
ProtocolVersion version
constexpr bool empty() const
Definition: Range.h:443
#define FOLLY_SAFE_CHECK(expr, msg)
Definition: SafeAssert.h:35
constexpr Iter end() const
Definition: Range.h:455
AttributeValue readAttributeValue(folly::StringPiece &sp, uint64_t form, bool is64Bit) const
Definition: Dwarf.cpp:380
Dwarf::DIEAbbreviation folly::symbolizer::Dwarf::getAbbreviation ( uint64_t  code,
uint64_t  offset 
) const
private

Definition at line 364 of file Dwarf.cpp.

References abbrev_, folly::Range< Iter >::advance(), folly::symbolizer::Dwarf::DIEAbbreviation::code, FOLLY_SAFE_CHECK, and readAbbreviation().

Referenced by findLocation().

365  {
366  // Linear search in the .debug_abbrev section, starting at offset
367  folly::StringPiece section = abbrev_;
368  section.advance(offset);
369 
370  Dwarf::DIEAbbreviation abbr;
371  while (readAbbreviation(section, abbr)) {
372  if (abbr.code == code) {
373  return abbr;
374  }
375  }
376 
377  FOLLY_SAFE_CHECK(false, "could not find abbreviation code");
378 }
void advance(size_type n)
Definition: Range.h:672
folly::StringPiece abbrev_
Definition: Dwarf.h:286
#define FOLLY_SAFE_CHECK(expr, msg)
Definition: SafeAssert.h:35
static bool readAbbreviation(folly::StringPiece &sp, DIEAbbreviation &abbr)
Definition: Dwarf.cpp:331
bool folly::symbolizer::Dwarf::getSection ( const char *  name,
folly::StringPiece section 
) const
private

Definition at line 302 of file Dwarf.cpp.

References elf_, folly::symbolizer::ElfFile::getSectionBody(), and folly::symbolizer::ElfFile::getSectionByName().

Referenced by init().

302  {
303  const ElfW(Shdr)* elfSection = elf_->getSectionByName(name);
304  if (!elfSection) {
305  return false;
306  }
307 #ifdef SHF_COMPRESSED
308  if (elfSection->sh_flags & SHF_COMPRESSED) {
309  return false;
310  }
311 #endif
312  *section = elf_->getSectionBody(*elfSection);
313  return true;
314 }
folly::StringPiece getSectionBody(const ElfShdr &section) const
Definition: Elf.cpp:318
const char * name
Definition: http_parser.c:437
const ElfShdr * getSectionByName(const char *name) const
Definition: Elf.cpp:352
const ElfFile * elf_
Definition: Dwarf.h:145
folly::StringPiece folly::symbolizer::Dwarf::getStringFromStringSection ( uint64_t  offset) const
private

Definition at line 431 of file Dwarf.cpp.

References FOLLY_SAFE_CHECK, folly::Range< Iter >::size(), and strings_.

Referenced by readAttributeValue().

431  {
432  FOLLY_SAFE_CHECK(offset < strings_.size(), "invalid strp offset");
434  sp.advance(offset);
435  return readNullTerminated(sp);
436 }
constexpr size_type size() const
Definition: Range.h:431
folly::StringPiece strings_
Definition: Dwarf.h:289
#define FOLLY_SAFE_CHECK(expr, msg)
Definition: SafeAssert.h:35
void folly::symbolizer::Dwarf::init ( )
private

Definition at line 316 of file Dwarf.cpp.

References abbrev_, aranges_, elf_, getSection(), info_, line_, and strings_.

Referenced by Dwarf().

316  {
317  // Make sure that all .debug_* sections exist
318  if (!getSection(".debug_info", &info_) ||
319  !getSection(".debug_abbrev", &abbrev_) ||
320  !getSection(".debug_line", &line_) ||
321  !getSection(".debug_str", &strings_)) {
322  elf_ = nullptr;
323  return;
324  }
325 
326  // Optional: fast address range lookup. If missing .debug_info can
327  // be used - but it's much slower (linear scan).
328  getSection(".debug_aranges", &aranges_);
329 }
bool getSection(const char *name, folly::StringPiece *section) const
Definition: Dwarf.cpp:302
folly::StringPiece line_
Definition: Dwarf.h:288
folly::StringPiece info_
Definition: Dwarf.h:285
folly::StringPiece strings_
Definition: Dwarf.h:289
folly::StringPiece abbrev_
Definition: Dwarf.h:286
folly::StringPiece aranges_
Definition: Dwarf.h:287
const ElfFile * elf_
Definition: Dwarf.h:145
bool folly::symbolizer::Dwarf::readAbbreviation ( folly::StringPiece sp,
DIEAbbreviation abbr 
)
staticprivate

Definition at line 331 of file Dwarf.cpp.

References folly::Range< Iter >::assign(), folly::symbolizer::Dwarf::DIEAbbreviation::attributes, folly::symbolizer::Dwarf::DIEAbbreviation::code, folly::Range< Iter >::data(), folly::Range< Iter >::empty(), FOLLY_SAFE_CHECK, folly::symbolizer::Dwarf::DIEAbbreviation::hasChildren, readAttribute(), and folly::symbolizer::Dwarf::DIEAbbreviation::tag.

Referenced by getAbbreviation().

333  {
334  // abbreviation code
335  abbr.code = readULEB(section);
336  if (abbr.code == 0) {
337  return false;
338  }
339 
340  // abbreviation tag
341  abbr.tag = readULEB(section);
342 
343  // does this entry have children?
344  abbr.hasChildren = (read<uint8_t>(section) != DW_CHILDREN_no);
345 
346  // attributes
347  const char* attributeBegin = section.data();
348  for (;;) {
349  FOLLY_SAFE_CHECK(!section.empty(), "invalid attribute section");
350  auto attr = readAttribute(section);
351  if (attr.name == 0 && attr.form == 0) {
352  break;
353  }
354  }
355 
356  abbr.attributes.assign(attributeBegin, section.data());
357  return true;
358 }
static DIEAbbreviation::Attribute readAttribute(folly::StringPiece &sp)
Definition: Dwarf.cpp:360
#define FOLLY_SAFE_CHECK(expr, msg)
Definition: SafeAssert.h:35
Dwarf::DIEAbbreviation::Attribute folly::symbolizer::Dwarf::readAttribute ( folly::StringPiece sp)
staticprivate

Definition at line 360 of file Dwarf.cpp.

Referenced by findLocation(), and readAbbreviation().

360  {
361  return {readULEB(sp), readULEB(sp)};
362 }
Dwarf::AttributeValue folly::symbolizer::Dwarf::readAttributeValue ( folly::StringPiece sp,
uint64_t  form,
bool  is64Bit 
) const
private

Definition at line 380 of file Dwarf.cpp.

References FOLLY_SAFE_CHECK, and getStringFromStringSection().

Referenced by findLocation().

383  {
384  switch (form) {
385  case DW_FORM_addr:
386  return read<uintptr_t>(sp);
387  case DW_FORM_block1:
388  return readBytes(sp, read<uint8_t>(sp));
389  case DW_FORM_block2:
390  return readBytes(sp, read<uint16_t>(sp));
391  case DW_FORM_block4:
392  return readBytes(sp, read<uint32_t>(sp));
393  case DW_FORM_block: // fallthrough
394  case DW_FORM_exprloc:
395  return readBytes(sp, readULEB(sp));
396  case DW_FORM_data1: // fallthrough
397  case DW_FORM_ref1:
398  return read<uint8_t>(sp);
399  case DW_FORM_data2: // fallthrough
400  case DW_FORM_ref2:
401  return read<uint16_t>(sp);
402  case DW_FORM_data4: // fallthrough
403  case DW_FORM_ref4:
404  return read<uint32_t>(sp);
405  case DW_FORM_data8: // fallthrough
406  case DW_FORM_ref8:
407  return read<uint64_t>(sp);
408  case DW_FORM_sdata:
409  return readSLEB(sp);
410  case DW_FORM_udata: // fallthrough
411  case DW_FORM_ref_udata:
412  return readULEB(sp);
413  case DW_FORM_flag:
414  return read<uint8_t>(sp);
415  case DW_FORM_flag_present:
416  return 1;
417  case DW_FORM_sec_offset: // fallthrough
418  case DW_FORM_ref_addr:
419  return readOffset(sp, is64Bit);
420  case DW_FORM_string:
421  return readNullTerminated(sp);
422  case DW_FORM_strp:
423  return getStringFromStringSection(readOffset(sp, is64Bit));
424  case DW_FORM_indirect: // form is explicitly specified
425  return readAttributeValue(sp, readULEB(sp), is64Bit);
426  default:
427  FOLLY_SAFE_CHECK(false, "invalid attribute form");
428  }
429 }
folly::StringPiece getStringFromStringSection(uint64_t offset) const
Definition: Dwarf.cpp:431
#define FOLLY_SAFE_CHECK(expr, msg)
Definition: SafeAssert.h:35
AttributeValue readAttributeValue(folly::StringPiece &sp, uint64_t form, bool is64Bit) const
Definition: Dwarf.cpp:380

Member Data Documentation

folly::StringPiece folly::symbolizer::Dwarf::abbrev_
private

Definition at line 286 of file Dwarf.h.

Referenced by getAbbreviation(), and init().

folly::StringPiece folly::symbolizer::Dwarf::aranges_
private

Definition at line 287 of file Dwarf.h.

Referenced by findAddress(), and init().

const ElfFile* folly::symbolizer::Dwarf::elf_
private

Definition at line 145 of file Dwarf.h.

Referenced by findAddress(), getSection(), and init().

folly::StringPiece folly::symbolizer::Dwarf::info_
private

Definition at line 285 of file Dwarf.h.

Referenced by findAddress(), and init().

folly::StringPiece folly::symbolizer::Dwarf::line_
private

Definition at line 288 of file Dwarf.h.

Referenced by findLocation(), and init().

folly::StringPiece folly::symbolizer::Dwarf::strings_
private

Definition at line 289 of file Dwarf.h.

Referenced by getStringFromStringSection(), and init().


The documentation for this class was generated from the following files: