From 57406d2fa5bf87db51b02aa21d3c78339b998a6a Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Fri, 2 Dec 2011 09:03:08 +0200 Subject: [PATCH] added Dwarf_lineprog_header struct, with some rudimentary tests --- elftools/common/construct_utils.py | 45 ++++++++++++++++++++++++++++++ elftools/dwarf/structs.py | 37 +++++++++++++++++++++++- tests/test_dwarf_structs.py | 39 ++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 elftools/common/construct_utils.py create mode 100644 tests/test_dwarf_structs.py diff --git a/elftools/common/construct_utils.py b/elftools/common/construct_utils.py new file mode 100644 index 0000000..d3e311a --- /dev/null +++ b/elftools/common/construct_utils.py @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------- +# elftools: common/construct_utils.py +# +# Some complementary construct utilities +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- +from ..construct import Subconstruct, ConstructError, ArrayError + + +class RepeatUntilExcluding(Subconstruct): + """ A version of construct's RepeatUntil that doesn't include the last + element (which casued the repeat to exit) in the return value. + + Only parsing is currently implemented. + + P.S. removed some code duplication + """ + __slots__ = ["predicate"] + def __init__(self, predicate, subcon): + Subconstruct.__init__(self, subcon) + self.predicate = predicate + self._clear_flag(self.FLAG_COPY_CONTEXT) + self._set_flag(self.FLAG_DYNAMIC) + def _parse(self, stream, context): + obj = [] + try: + context_for_subcon = context + if self.subcon.conflags & self.FLAG_COPY_CONTEXT: + context_for_subcon = context.__copy__() + + while True: + subobj = self.subcon._parse(stream, context_for_subcon) + if self.predicate(subobj, context): + break + obj.append(subobj) + except ConstructError, ex: + raise ArrayError("missing terminator", ex) + return obj + def _build(self, obj, stream, context): + raise NotImplementedError('no building') + def _sizeof(self, context): + raise SizeofError("can't calculate size") + diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py index a35f362..6d71657 100644 --- a/elftools/dwarf/structs.py +++ b/elftools/dwarf/structs.py @@ -11,8 +11,9 @@ from ..construct import ( UBInt8, UBInt16, UBInt32, UBInt64, ULInt8, ULInt16, ULInt32, ULInt64, SBInt8, SBInt16, SBInt32, SBInt64, SLInt8, SLInt16, SLInt32, SLInt64, Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename, Enum, - PrefixedArray, CString, + Array, PrefixedArray, CString, Embed, ) +from ..common.construct_utils import RepeatUntilExcluding from .enums import * @@ -55,6 +56,9 @@ class DWARFStructs(object): A dictionary mapping 'DW_FORM_*' keys into construct Structs that parse such forms. These Structs have already been given dummy names. + + Dwarf_lineprog_header (+): + Line program header See also the documentation of public methods. """ @@ -112,6 +116,7 @@ class DWARFStructs(object): self._create_cu_header() self._create_abbrev_declaration() self._create_dw_form() + self._create_lineprog_header() def _create_initial_length(self): def _InitialLength(name): @@ -180,6 +185,36 @@ class DWARFStructs(object): DW_FORM_indirect=self.Dwarf_uleb128(''), ) + def _create_lineprog_header(self): + # A file entry is terminated by a NULL byte, so we don't want to parse + # past it. Therefore an If is used. + file_entry = Struct('file_entry', + CString('name'), + If(lambda ctx: len(ctx.name) != 0, + Embed(Struct('', + self.Dwarf_uleb128('dir_index'), + self.Dwarf_uleb128('mtime'), + self.Dwarf_uleb128('length'))))) + + self.Dwarf_lineprog_header = Struct('Dwarf_lineprog_header', + self.Dwarf_initial_length('unit_length'), + self.Dwarf_uint16('version'), + self.Dwarf_offset('header_length'), + self.Dwarf_uint8('minimum_instruction_length'), + self.Dwarf_uint8('default_is_stmt'), + self.Dwarf_int8('line_base'), + self.Dwarf_uint8('line_range'), + self.Dwarf_uint8('opcode_base'), + Array(lambda ctx: ctx['opcode_base'] - 1, + self.Dwarf_uint8('standard_opcode_lengths')), + RepeatUntilExcluding( + lambda obj, ctx: obj == '', + CString('include_directory')), + RepeatUntilExcluding( + lambda obj, ctx: len(obj.name) == 0, + file_entry), + ) + def _make_block_struct(self, length_field): """ Create a struct for DW_FORM_block """ diff --git a/tests/test_dwarf_structs.py b/tests/test_dwarf_structs.py new file mode 100644 index 0000000..632aa66 --- /dev/null +++ b/tests/test_dwarf_structs.py @@ -0,0 +1,39 @@ +import sys, unittest + +sys.path.extend(['.', '..']) +from elftools.dwarf.structs import DWARFStructs + + +class TestDWARFStructs(unittest.TestCase): + def test_lineprog_header(self): + ds = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4) + + c = ds.Dwarf_lineprog_header.parse( + '\x04\x10\x00\x00' + # initial lenght + '\x05\x02' + # version + '\x20\x00\x00\x00' + # header length + '\x05\x10\x40\x50' + # until and including line_range + '\x06' + # opcode_base + '\x00\x01\x04\x08\x0C' + # standard_opcode_lengths + # 2 dir names followed by a NULL + '\x61\x62\x00\x70\x00\x00' + + # a file entry + '\x61\x72\x00\x0C\x0D\x0F' + + # and another entry + '\x45\x50\x51\x00\x86\x12\x07\x08' + + # followed by NULL + '\x00') + + self.assertEqual(c.version, 0x205) + self.assertEqual(c.opcode_base, 6) + self.assertEqual(c.standard_opcode_lengths, [0, 1, 4, 8, 12]) + self.assertEqual(c.include_directory, ['ab', 'p']) + self.assertEqual(len(c.file_entry), 2) + self.assertEqual(c.file_entry[0].name, 'ar') + self.assertEqual(c.file_entry[1].name, 'EPQ') + self.assertEqual(c.file_entry[1].dir_index, 0x12 * 128 + 6) + + +if __name__ == '__main__': + unittest.main() + -- 2.30.2