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
+