From: Raphael Taylor-Davies Date: Mon, 17 Aug 2015 14:27:53 +0000 (+0100) Subject: Added support for DWARF v4 lineprograms X-Git-Tag: v0.24~15^2 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=3b7ab324c16f7994c453af38fee9bbba6a9e7ac8;p=pyelftools.git Added support for DWARF v4 lineprograms DWARF v4 lineprograms add additional debugging information for VLIW architectures, specifically they add an op_index field identifying the operation within the instruction to which this lineprogram refers. This field has been added to the LineState object. On non-VLIW architectures and for non DWARF v4 lineprograms this field will always be 0. --- diff --git a/elftools/dwarf/lineprogram.py b/elftools/dwarf/lineprogram.py index 810e603..51cfdb7 100644 --- a/elftools/dwarf/lineprogram.py +++ b/elftools/dwarf/lineprogram.py @@ -51,6 +51,7 @@ class LineState(object): self.file = 1 self.line = 1 self.column = 0 + self.op_index = 0 self.is_stmt = default_is_stmt self.basic_block = False self.end_sequence = False @@ -151,14 +152,25 @@ class LineProgram(object): # opcodes anyway. if opcode >= self.header['opcode_base']: # Special opcode (follow the recipe in 6.2.5.1) + maximum_operations_per_instruction = self['maximum_operations_per_instruction'] + + adjusted_opcode = opcode - self['opcode_base'] - address_addend = ((adjusted_opcode // self['line_range']) * - self['minimum_instruction_length']) + operation_advance = adjusted_opcode // self['line_range'] + + address_addend = self['minimum_instruction_length'] * ((state.op_index + operation_advance) // maximum_operations_per_instruction) + state.address += address_addend - line_addend = (self['line_base'] + - adjusted_opcode % self['line_range']) + + state.op_index = (state.op_index + operation_advance) % maximum_operations_per_instruction + + line_addend = self['line_base'] + (adjusted_opcode % self['line_range']) + state.line += line_addend - add_entry_new_state(opcode, [line_addend, address_addend]) + + add_entry_new_state(opcode, [line_addend, address_addend, state.op_index]) + + elif opcode == 0: # Extended opcode: start with a zero byte, followed by # instruction size and the instruction itself. diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py index 39e4815..49ffd52 100644 --- a/elftools/dwarf/structs.py +++ b/elftools/dwarf/structs.py @@ -226,6 +226,11 @@ class DWARFStructs(object): self.Dwarf_uint16('version'), self.Dwarf_offset('header_length'), self.Dwarf_uint8('minimum_instruction_length'), + + If(lambda ctx: ctx['version'] >= 4, + self.Dwarf_uint8("maximum_operations_per_instruction"), + 1), + self.Dwarf_uint8('default_is_stmt'), self.Dwarf_int8('line_base'), self.Dwarf_uint8('line_range'), diff --git a/scripts/readelf.py b/scripts/readelf.py index 6bcab9b..f307e95 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -929,11 +929,19 @@ class ReadElf(object): # readelf doesn't print the state after end_sequence # instructions. I think it's a bug but to be compatible # I don't print them too. - self._emitline('%-35s %11d %18s' % ( - bytes2str(lineprogram['file_entry'][state.file - 1].name), - state.line, - '0' if state.address == 0 else - self._format_hex(state.address))) + if lineprogram['version'] < 4: + self._emitline('%-35s %11d %18s' % ( + bytes2str(lineprogram['file_entry'][state.file - 1].name), + state.line, + '0' if state.address == 0 else + self._format_hex(state.address))) + else: + self._emitline('%-35s %11d %18s[%d]' % ( + bytes2str(lineprogram['file_entry'][state.file - 1].name), + state.line, + '0' if state.address == 0 else + self._format_hex(state.address), + state.op_index)) if entry.command == DW_LNS_copy: # Another readelf oddity... self._emitline() diff --git a/test/test_dwarf_lineprogram.py b/test/test_dwarf_lineprogram.py index 3a10932..e592a82 100644 --- a/test/test_dwarf_lineprogram.py +++ b/test/test_dwarf_lineprogram.py @@ -68,7 +68,7 @@ class TestLineProgram(unittest.TestCase): self.assertEqual(linetable[0].args, [0x239]) self.assertLineState(linetable[1].state, address=0x239, line=3) self.assertEqual(linetable[1].command, 0xb) - self.assertEqual(linetable[1].args, [2, 0]) + self.assertEqual(linetable[1].args, [2, 0, 0]) self.assertLineState(linetable[2].state, address=0x23c, line=5) self.assertLineState(linetable[3].state, address=0x244, line=6) self.assertLineState(linetable[4].state, address=0x24b, line=7, end_sequence=False) diff --git a/test/test_dwarf_structs.py b/test/test_dwarf_structs.py index 94f77c1..bd4a8fc 100644 --- a/test/test_dwarf_structs.py +++ b/test/test_dwarf_structs.py @@ -18,8 +18,8 @@ class TestDWARFStructs(unittest.TestCase): ds = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4) c = ds.Dwarf_lineprog_header.parse( - b'\x04\x10\x00\x00' + # initial lenght - b'\x05\x02' + # version + b'\x04\x10\x00\x00' + # initial length + b'\x02\x00' + # version b'\x20\x00\x00\x00' + # header length b'\x05\x10\x40\x50' + # until and including line_range b'\x06' + # opcode_base @@ -31,9 +31,8 @@ class TestDWARFStructs(unittest.TestCase): # and another entry b'\x45\x50\x51\x00\x86\x12\x07\x08' + # followed by NULL - b'\x00') - - self.assertEqual(c.version, 0x205) + b'\x00') + self.assertEqual(c.version, 2) self.assertEqual(c.opcode_base, 6) self.assertEqual(c.standard_opcode_lengths, [0, 1, 4, 8, 12]) self.assertEqual(c.include_directory, [b'ab', b'p']) diff --git a/test/testfiles_for_readelf/lineprogram.elf b/test/testfiles_for_readelf/lineprogram.elf new file mode 100644 index 0000000..003e1f0 Binary files /dev/null and b/test/testfiles_for_readelf/lineprogram.elf differ