From d40dd67940585492fee540ff6014ac91347b816a Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Sun, 4 Dec 2011 06:22:24 +0200 Subject: [PATCH] small fixes to lineprogram + unit tests + new unittest runner --- elftools/dwarf/dwarfinfo.py | 2 +- elftools/dwarf/lineprogram.py | 17 ++++--- tests/run_all_unittests.py | 20 ++++++++ tests/test_dwarf_lineprogram.py | 88 +++++++++++++++++++++++++++++++++ tests/test_dwarf_structs.py | 28 +++++------ z.py | 4 +- 6 files changed, 134 insertions(+), 25 deletions(-) create mode 100755 tests/run_all_unittests.py create mode 100644 tests/test_dwarf_lineprogram.py diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py index bb073e0..d805d55 100644 --- a/elftools/dwarf/dwarfinfo.py +++ b/elftools/dwarf/dwarfinfo.py @@ -196,7 +196,7 @@ class DWARFInfo(object): return LineProgram( header=lineprog_header, - dwarfinfo=self, + stream=self.debug_line_sec.stream, structs=structs, program_start_offset=self.debug_line_sec.stream.tell(), program_end_offset=end_offset) diff --git a/elftools/dwarf/lineprogram.py b/elftools/dwarf/lineprogram.py index 11cd57f..2dc4b3f 100644 --- a/elftools/dwarf/lineprogram.py +++ b/elftools/dwarf/lineprogram.py @@ -38,13 +38,14 @@ class LineState(object): a.append(' %s = %s' % (attr, getattr(self, attr))) return '\n'.join(a) + '>\n' + class LineProgram(object): """ Builds a "line table", which is essentially the matrix described in section 6.2 of DWARFv3. It's a list of LineState objects, sorted by increasing address, so it can be used to obtain the state information for each address. """ - def __init__(self, header, dwarfinfo, structs, + def __init__(self, header, stream, structs, program_start_offset, program_end_offset): """ header: @@ -52,19 +53,19 @@ class LineProgram(object): its header by appending file entries if DW_LNE_define_file instructions are encountered. - dwarfinfo: - The DWARFInfo context object which created this one + stream: + The stream this program can be read from. structs: A DWARFStructs instance suitable for this line program program_{start|end}_offset: Offset in the debug_line section stream where this program - starts, and where it ends. The actual range includes start - but not end: [start, end - 1] + starts (the actual program, after the header), and where it + ends. + The actual range includes start but not end: [start, end - 1] """ - self.dwarfinfo = dwarfinfo - self.stream = self.dwarfinfo.debug_line_sec.stream + self.stream = stream self.header = header self.structs = structs self.program_start_offset = program_start_offset @@ -170,7 +171,7 @@ class LineProgram(object): state.address += ((adjusted_opcode / self['line_range']) * self['minimum_instruction_length']) elif opcode == DW_LNS_fixed_advance_pc: - operand = struct_parse(self.structs.Dwarf_uint16, + operand = struct_parse(self.structs.Dwarf_uint16(''), self.stream) state.address += operand elif opcode == DW_LNS_set_prologue_end: diff --git a/tests/run_all_unittests.py b/tests/run_all_unittests.py new file mode 100755 index 0000000..b3a3871 --- /dev/null +++ b/tests/run_all_unittests.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +#------------------------------------------------------------------------------- +# tests/run_all_unittests.py +# +# Run all unit tests +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- +from unittest import TestLoader, TextTestRunner + + +if __name__ == '__main__': + try: + tests = TestLoader().discover('tests', 'test*.py', 'tests') + TextTestRunner().run(tests) + except ImportError as err: + print err + print '!! Please execute from the root directory of pyelfutils' + diff --git a/tests/test_dwarf_lineprogram.py b/tests/test_dwarf_lineprogram.py new file mode 100644 index 0000000..50d8af3 --- /dev/null +++ b/tests/test_dwarf_lineprogram.py @@ -0,0 +1,88 @@ +import sys, unittest +from cStringIO import StringIO + +sys.path.extend(['.', '..']) +from elftools.dwarf.lineprogram import LineProgram, LineState +from elftools.dwarf.structs import DWARFStructs + + +class TestLineProgram(unittest.TestCase): + def _make_program_in_stream(self, stream): + """ Create a LineProgram from the given program encoded in a stream + """ + ds = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4) + header = ds.Dwarf_lineprog_header.parse( + '\x04\x10\x00\x00' + # initial lenght + '\x03\x00' + # version + '\x20\x00\x00\x00' + # header length + '\x01\x01\x01\x0F' + # flags + '\x0A' + # opcode_base + '\x00\x01\x04\x08\x0C\x01\x01\x01\x00' + # 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') + + lp = LineProgram(header, stream, ds, 0, len(stream.getvalue())) + return lp + + def assertLineState(self, state, **kwargs): + """ Assert that the state attributes specified in kwargs have the given + values (the rest are default). + """ + for k, v in kwargs.iteritems(): + self.assertEqual(getattr(state, k), v) + + def test_spec_sample_59(self): + # Sample in figure 59 of DWARFv3 + s = StringIO() + s.write( + '\x02\xb9\x04' + + '\x0b' + + '\x38' + + '\x82' + + '\x73' + + '\x02\x02' + + '\x00\x01\x01') + + lp = self._make_program_in_stream(s) + linetable = lp.get_line_table() + + self.assertLineState(linetable[0], address=0x239, line=3) + self.assertLineState(linetable[1], address=0x23c, line=5) + self.assertLineState(linetable[2], address=0x244, line=6) + self.assertLineState(linetable[3], address=0x24b, line=7, end_sequence=False) + self.assertLineState(linetable[4], address=0x24d, line=7, end_sequence=True) + + def test_spec_sample_60(self): + # Sample in figure 60 of DWARFv3 + s = StringIO() + s.write( + '\x09\x39\x02' + + '\x0b' + + '\x09\x03\x00' + + '\x0b' + + '\x09\x08\x00' + + '\x0a' + + '\x09\x07\x00' + + '\x0a' + + '\x09\x02\x00' + + '\x00\x01\x01') + + lp = self._make_program_in_stream(s) + linetable = lp.get_line_table() + + self.assertLineState(linetable[0], address=0x239, line=3) + self.assertLineState(linetable[1], address=0x23c, line=5) + self.assertLineState(linetable[2], address=0x244, line=6) + self.assertLineState(linetable[3], address=0x24b, line=7, end_sequence=False) + self.assertLineState(linetable[4], address=0x24d, line=7, end_sequence=True) + + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/test_dwarf_structs.py b/tests/test_dwarf_structs.py index 632aa66..a1e30d7 100644 --- a/tests/test_dwarf_structs.py +++ b/tests/test_dwarf_structs.py @@ -9,20 +9,20 @@ class TestDWARFStructs(unittest.TestCase): 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') + '\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) diff --git a/z.py b/z.py index fed0ab7..632f376 100644 --- a/z.py +++ b/z.py @@ -24,9 +24,9 @@ print '===> %s sections!' % efile.num_sections() dwarfinfo = efile.get_dwarf_info() CUs = list(dwarfinfo.iter_CUs()) print 'num CUs:', len(CUs) -print 'first CU:', CUs[0] +print 'CU:', CUs[2] -lp = dwarfinfo.line_program_for_CU(CUs[0]) +lp = dwarfinfo.line_program_for_CU(CUs[2]) print 'lp:', lp, lp.header print 'linetable:', lp.get_line_table() #for lp in dwarfinfo.iter_line_programs(): -- 2.30.2