Added support for DWARF v4 lineprograms
authorRaphael Taylor-Davies <raphaelt@etxcn-cam-2.cam.broadcom.com>
Mon, 17 Aug 2015 14:27:53 +0000 (15:27 +0100)
committerRaphael Taylor-Davies <raphaelt@etxcn-cam-1.cam.broadcom.com>
Mon, 24 Aug 2015 13:35:04 +0000 (14:35 +0100)
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.

elftools/dwarf/lineprogram.py
elftools/dwarf/structs.py
scripts/readelf.py
test/test_dwarf_lineprogram.py
test/test_dwarf_structs.py
test/testfiles_for_readelf/lineprogram.elf [new file with mode: 0644]

index 810e60398cb68208e1d6bc2efb8e8c00ff141b7b..51cfdb7f26ecc56a94a8171311132d4674d540d1 100644 (file)
@@ -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.
index 39e4815b2f4cf3351d761fe55bc3f4ac2fa36e21..49ffd52a4afbcc3e923e44c39397ffd19866fcc0 100644 (file)
@@ -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'),
index 6bcab9b6d685600b4789c711e6a25d6ed4b4f447..f307e95fc905bf42547d1fe725ee8d426020791b 100755 (executable)
@@ -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()
index 3a10932c96e93c51fb903722bdab4fd6a987e5ff..e592a829edafd15e8ad77288722d9a46305f7d41 100644 (file)
@@ -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)
index 94f77c1c15e214537b4e65fd914d3afbfc68dcb6..bd4a8fc342dd9dfde59754b25d4d5f16ab57b9e7 100644 (file)
@@ -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 (file)
index 0000000..003e1f0
Binary files /dev/null and b/test/testfiles_for_readelf/lineprogram.elf differ