moving on with line program stuffz
diff --git a/elftools/dwarf/constants.py b/elftools/dwarf/constants.py
index 8dd489e..ae7e6b2 100644
--- a/elftools/dwarf/constants.py
+++ b/elftools/dwarf/constants.py
@@ -118,3 +118,21 @@
DW_ORD_col_major=1
+# Line program opcodes
+#
+DW_LNS_copy = 0x01
+DW_LNS_advance_pc = 0x02
+DW_LNS_advance_line = 0x03
+DW_LNS_set_file = 0x04
+DW_LNS_set_column = 0x05
+DW_LNS_negate_stmt = 0x06
+DW_LNS_set_basic_block = 0x07
+DW_LNS_const_add_pc = 0x08
+DW_LNS_fixed_advance_pc = 0x09
+DW_LNS_set_prologue_end = 0x0a
+DW_LNS_set_epilogue_begin = 0x0b
+DW_LNS_set_isa = 0x0c
+DW_LNE_end_sequence = 0x01
+DW_LNE_set_address = 0x02
+DW_LNE_define_file = 0x03
+
diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py
index 64bf123..90ba52a 100644
--- a/elftools/dwarf/dwarfinfo.py
+++ b/elftools/dwarf/dwarfinfo.py
@@ -199,18 +199,26 @@
dwarf_format=dwarf_format,
address_size=4)
+ # Now parse the header fully using up-to-date structs. After this,
+ # the section stream will point at the beginning of the program
+ # itself, right after the header.
lineprog_header = struct_parse(
lineprog_structs.Dwarf_lineprog_header,
self.debug_line_sec.stream,
offset)
+ # Calculate the offset to the next line program (see DWARF 6.2.4)
+ end_offset = ( offset + lineprog_header['unit_length'] +
+ lineprog_structs.initial_length_field_size()))
+
lineprograms.append(LineProgram(
header=lineprog_header,
dwarfinfo=self,
- structs=lineprog_structs))
+ structs=lineprog_structs,
+ program_start_offset=self.debug_line_sec.stream.tell()),
+ program_end_offset=end_offset)
- # Calculate the offset to the next line program (see DWARF 6.2.4)
- offset += ( lineprog_header['unit_length'] +
- lineprog_structs.initial_length_field_size())
+ offset = end_offset
+
return lineprograms
diff --git a/elftools/dwarf/lineprogram.py b/elftools/dwarf/lineprogram.py
index d865141..ce92075 100644
--- a/elftools/dwarf/lineprogram.py
+++ b/elftools/dwarf/lineprogram.py
@@ -6,13 +6,65 @@
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
+from ..common.utils import struct_parse
+from .constants import *
+
+
+class LineState(object):
+ """ Represents a line program state (or a "row" in the matrix
+ describing debug location information for addresses).
+ """
+ def __init__(self, default_is_stmt):
+ self.address = 0
+ self.file = 1
+ self.line = 1
+ self.column = 0
+ self.is_stmt = default_is_stmt
+ self.basic_block = False
+ self.end_sequence = False
+ self.prologue_end = False
+ self.epilogue_begin = False
+ self.isa = 0
class LineProgram(object):
- def __init__(self, header, dwarfinfo, structs):
+ """ 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,
+ program_start_offset, program_end_offset):
+ """
+ header:
+ The header of this line program
+
+ dwarfinfo:
+ The DWARFInfo context object which created this one
+
+ 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]
+ """
self.dwarfinfo = dwarfinfo
+ self.stream = self.dwarfinfo.debug_line_sec.stream
self.header = header
- pass
+ self.structs = structs
+ self.program_start_offset = program_start_offset
+ self.program_end_offset = program_end_offset
+
+ self._line_table = None
+
+ def get_line_table(self):
+ """ Get the decoded line table for this line program
+ """
+ if self._line_table is None:
+ self._line_table = self._decode_line_program()
+ return self._line_table
#------ PRIVATE ------#
@@ -21,3 +73,44 @@
"""
return self.header[name]
+ def _decode_line_program(self):
+ linetable = []
+ state = LineState(self.header['default_is_stmt'])
+
+ offset = self.program_start_offset
+ while offset < self.program_end_offset:
+ opcode = struct_parse(
+ self.structs.Dwarf_uint8,
+ self.stream,
+ offset)
+
+ # As an exercise in avoiding premature optimization, if...elif
+ # chains are used here for standard and extended opcodes instead
+ # of dispatch tables. This keeps the code much cleaner. Besides,
+ # the majority of instructions are special opcodes anyway.
+ if opcode == 0:
+ # Extended opcode: start with a zero byte, followed by
+ # instruction size and the instruction itself.
+ pass
+ elif opcode < self.header['opcode_base']:
+ # Standard opcode
+ if opcode == DW_LNS_copy:
+ linetable.append(state)
+ state.basic_block = False
+ state.prologue_end = False
+ state.epilogue_begin = False
+ elif opcode == DW_LNS_advance_pc:
+ operand = struct_parse(self.Dwarf_uleb128, self.stream)
+ state.address += (
+ operand * self.header['minimum_instruction_length'])
+ elif opcode = DW_LNS_advance_line:
+ operand = struct_parse(self.Dwarf_sleb128, self.stream)
+ state.line += operand
+ # ZZZ! go on now...
+ else:
+ # Special opcode
+ pass
+
+ def _handle_LNS_copy(self, opcode, state, linetable):
+ pass
+