class ELFParseError(ELFError):
pass
+class DWARFError(Exception):
+ pass
+
def elf_assert(cond, msg=''):
""" Assert that cond is True, otherwise raise ELFError(msg)
"""
- if not cond:
- raise ELFError(msg)
+ _assert_with_exception(cond, msg, ELFError)
+
+
+def dwarf_assert(cond, msg=''):
+ """ Assert that cond is True, otherwise raise DWARFError(msg)
+ """
+ _assert_with_exception(cond, msg, DWARFError)
+
+def _assert_with_exception(cond, msg, exception_type):
+ if not cond:
+ raise exception_type(msg)
\ No newline at end of file
--- /dev/null
+#-------------------------------------------------------------------------------
+# elftools: dwarf/compileunit.py
+#
+# DWARF compile unit
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+
+
+class CompileUnit(object):
+ def __init__(self, header, cu_die):
+ self.header = header
+ self.cu_die
+
+ def __getitem__(self, name):
+ """ Implement dict-like access to header entries
+ """
+ return self.header[name]
--- /dev/null
+#-------------------------------------------------------------------------------
+# elftools: dwarf/dwarfinfo.py
+#
+# DWARFInfo - Main class for accessing DWARF debug information
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from collections import namedtuple
+
+from ..common.exceptions import DWARFError
+from ..common.utils import struct_parse, dwarf_assert
+from .structs import DWARFStructs
+from .compileunit import CompileUnit
+
+
+# Describes a debug section in a stream: offset and size
+#
+DebugSectionLocator = namedtuple('DebugSectionLocator', 'offset size')
+
+
+class DWARFInfo(object):
+ """ Creation: the constructor accepts a stream (file-like object) that
+ contains debug sections, along with locators (DebugSectionLocator)
+ of the required sections. In addition, little_endian is a boolean
+ parameter specifying endianity, and dwarfclass is 32 or 64, depending
+ on the type of file the DWARF info was read from.
+ """
+ def __init__(self,
+ stream,
+ little_endian,
+ dwarfclass,
+ debug_info_loc,
+ debug_abbrev_loc,
+ debug_str_loc,
+ debug_line_loc):
+ self.stream = stream
+ self.debug_info_loc = debug_info_loc
+ self.debug_abbrev_loc = debug_abbrev_loc
+ self.debug_str_loc = debug_str_loc
+ self.debug_line_loc = debug_line_loc
+
+ self.little_endian = little_endian
+ self.dwarfclass = dwarfclass
+ self.structs = DWARFStructs(
+ little_endian=self.little_endian,
+ dwarfclass=self.dwarfclass)
+
+ self._CU = self._parse_CUs()
+
+ def initial_lenght_field_size(self):
+ """ Size of an initial length field.
+ """
+ return 4 if self.dwarfclass == 32 else 12
+
+ def _parse_CUs(self):
+ """ Parse CU entries from debug_info and return them as a list of
+ containers.
+ """
+ offset = self.debug_info_loc.offset
+ section_boundary = self.debug_info_loc.offset + self.debug_info_loc.length
+ CUlist = []
+ while offset < section_boundary:
+ cu_header = struct_parse(
+ self.structs.Dwarf_CU_header, self.stream, offset)
+ dwarf_assert(self._is_supported_version(cu_header['version']))
+ CUlist.append(CompileUnit(cu_header, None))
+ # Compute the offset of the next CU in the section. The unit_length
+ # field of the CU header contains its size not including the length
+ # field itself.
+ offset = ( offset +
+ cu['unit_length'] +
+ self.initial_lenght_field_size())
+ return CUlist
+
+ def _is_supported_version(self, version):
+ """ DWARF version supported by this parser
+ """
+ return 2 <= version <= 3
+
+
+
+
class DWARFStructs(object):
- """ Accessible attributes:
+ """ Accessible attributes (mostly described by in chapter 7 of the DWARF
+ spec v3):
Dwarf_uint{8,16,32,64):
Data chunks of the common sizes
- Dwarf_xword:
- 32-bit or 64-bit word, depending on dwarfclass
+ Dwarf_xword, Dwarf_offset:
+ 32-bit or 64-bit word, depending on dwarfclass (xword and offset
+ are synonyms here).
Dwarf_initial_length:
- "Initial length field" encoding, as described in DWARFv3 spec
+ "Initial length field" encoding
section 7.4
Dwarf_{u,s}leb128:
- ULEB128 and SLEB128 variable-length encoding, as described in
- DWARFv3 spec section 7.6
+ ULEB128 and SLEB128 variable-length encoding
+
+ Dwarf_CU_header:
+ Compilation unit header
"""
def __init__(self, little_endian=True, dwarfclass=32):
assert dwarfclass == 32 or dwarfclass == 64
self.little_endian = little_endian
self.dwarfclass = dwarfclass
self._create_structs()
- self._create_leb128()
def _create_structs(self):
if self.little_endian:
self.Dwarf_uint32 = UBInt32
self.Dwarf_uint64 = UBInt64
self.Dwarf_xword = UBInt32 if self.dwarfclass == 32 else UBInt64
+ self.Dwarf_offset = self.Dwarf_xword
self._create_initial_length()
-
+ self._create_leb128()
+ self._create_cu_header()
+
def _create_initial_length(self):
def _InitialLength(name):
# Adapts a Struct that parses forward a full initial length field.
self.Dwarf_uleb128 = _ULEB128
self.Dwarf_sleb128 = _SLEB128
+ def _create_cu_header(self):
+ self.Dwarf_CU_header = Struct('Dwarf_CU_header',
+ self.Dwarf_initial_length('unit_length'),
+ self.Dwarf_uint16('version'),
+ self.Dwarf_offset('debug_abbrev_offset'),
+ self.Dwarf_uint8('address_size'))
+
class _InitialLengthAdapter(Adapter):
""" A standard Construct adapter that expects a sub-construct
class ELFFile(object):
- """ Accessible attributes:
+ """ Creation: the constructor accepts a stream (file-like object) with the
+ contents of an ELF file.
+
+ Accessible attributes:
elfclass:
32 or 64 - specifies the word size of the target machine
# read a little-endian, 64-bit file
es = ELFStructs(True, 64)
-stream = open('binfiles/z.elf', 'rb')
+stream = open('tests/testfiles/z.elf', 'rb')
#stream = open('binfiles/z32.elf', 'rb')
efile = ELFFile(stream)
+# Just a script for playing around with pyelftools during testing\r
+# please ignore it!\r
+#\r
+\r
from elftools.dwarf.structs import DWARFStructs\r
+from elftools.dwarf.dwarfinfo import DWARFInfo\r
\r
\r
ds = DWARFStructs(\r