From 445565134b1289f4c58d50b290c30a7cf8378c32 Mon Sep 17 00:00:00 2001 From: eliben Date: Mon, 19 Sep 2011 12:54:32 +0300 Subject: [PATCH] Added dwarf/compileunit.py and dwarf/dwarfinfo.py and started filling in details --- elftools/common/exceptions.py | 3 ++ elftools/common/utils.py | 13 +++++- elftools/dwarf/compileunit.py | 19 ++++++++ elftools/dwarf/dwarfinfo.py | 83 +++++++++++++++++++++++++++++++++++ elftools/dwarf/structs.py | 29 ++++++++---- elftools/elf/elffile.py | 5 ++- z.py | 2 +- zd.py | 5 +++ 8 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 elftools/dwarf/compileunit.py create mode 100644 elftools/dwarf/dwarfinfo.py diff --git a/elftools/common/exceptions.py b/elftools/common/exceptions.py index 8a40877..c2da24c 100644 --- a/elftools/common/exceptions.py +++ b/elftools/common/exceptions.py @@ -12,3 +12,6 @@ class ELFError(Exception): class ELFParseError(ELFError): pass +class DWARFError(Exception): + pass + diff --git a/elftools/common/utils.py b/elftools/common/utils.py index 8b9b9ae..c099013 100644 --- a/elftools/common/utils.py +++ b/elftools/common/utils.py @@ -28,6 +28,15 @@ def struct_parse(struct, stream, stream_pos=None): 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 diff --git a/elftools/dwarf/compileunit.py b/elftools/dwarf/compileunit.py new file mode 100644 index 0000000..7791b86 --- /dev/null +++ b/elftools/dwarf/compileunit.py @@ -0,0 +1,19 @@ +#------------------------------------------------------------------------------- +# 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] diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py new file mode 100644 index 0000000..651c5da --- /dev/null +++ b/elftools/dwarf/dwarfinfo.py @@ -0,0 +1,83 @@ +#------------------------------------------------------------------------------- +# 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 + + + + diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py index e6aa81c..104cee9 100644 --- a/elftools/dwarf/structs.py +++ b/elftools/dwarf/structs.py @@ -15,28 +15,31 @@ from ..construct import ( 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: @@ -51,9 +54,12 @@ class DWARFStructs(object): 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. @@ -72,6 +78,13 @@ class DWARFStructs(object): 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 diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 2c00fb5..8195c7f 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -17,7 +17,10 @@ from .segments import Segment, InterpSegment 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 diff --git a/z.py b/z.py index dc0eee9..bddd6e0 100644 --- a/z.py +++ b/z.py @@ -10,7 +10,7 @@ from elftools.elf.sections import * # 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) diff --git a/zd.py b/zd.py index 6294100..44e6dbf 100644 --- a/zd.py +++ b/zd.py @@ -1,4 +1,9 @@ +# Just a script for playing around with pyelftools during testing +# please ignore it! +# + from elftools.dwarf.structs import DWARFStructs +from elftools.dwarf.dwarfinfo import DWARFInfo ds = DWARFStructs( -- 2.30.2