renamed 'location expression' to the more general 'dwarf expression'
[pyelftools.git] / elftools / dwarf / dwarfinfo.py
index 8bda113eb1a16326caad82442225bc3a5c524dfc..d805d555e49ae4d22ed59e8d31000e51eff77cfe 100644 (file)
@@ -63,13 +63,8 @@ class DWARFInfo(object):
             dwarf_format=32,
             address_size=4)
         
-        # Populate the list with CUs found in debug_info. For each CU only its
-        # header is parsed immediately (the abbrev table isn't loaded before
-        # it's being referenced by one of the CU's DIEs). 
-        # Since there usually aren't many CUs in a single object, this
-        # shouldn't present a performance problem.
-        #
-        self._CU = self._parse_CUs()
+        # A list of CUs. Populated lazily when they're actually requested.
+        self._CUs = None
         
         # Cache for abbrev tables: a dict keyed by offset
         self._abbrevtable_cache = {}
@@ -77,8 +72,10 @@ class DWARFInfo(object):
     def iter_CUs(self):
         """ Yield all the compile units (CompileUnit objects) in the debug info
         """
-        return iter(self._CU)
-    
+        if self._CUs is None:
+            self._CUs = self._parse_CUs()
+        return iter(self._CUs)
+
     def get_abbrev_table(self, offset):
         """ Get an AbbrevTable from the given offset in the debug_abbrev
             section.
@@ -107,6 +104,20 @@ class DWARFInfo(object):
         """
         return parse_cstring_from_stream(self.debug_str_sec.stream, offset)
     
+    def line_program_for_CU(self, CU):
+        """ Given a CU object, fetch the line program it points to from the
+            .debug_line section.
+            If the CU doesn't point to a line program, return None.
+        """
+        # The line program is pointed to by the DW_AT_stmt_list attribute of
+        # the top DIE of a CU.
+        top_DIE = CU.get_top_DIE()
+        if 'DW_AT_stmt_list' in top_DIE.attributes:
+            return self._parse_line_program_at_offset(
+                    top_DIE.attributes['DW_AT_stmt_list'].value, CU.structs)
+        else:
+            return None
+        
     #------ PRIVATE ------#
     
     def _parse_CUs(self):
@@ -169,38 +180,24 @@ class DWARFInfo(object):
         """
         return 2 <= version <= 3
 
-    def _parse_line_programs(self):
-        """ Parse line programs from debug_line
+    def _parse_line_program_at_offset(self, debug_line_offset, structs):
+        """ Given an offset to the .debug_line section, parse the line program
+            starting at this offset in the section and return it.
+            structs is the DWARFStructs object used to do this parsing.
         """
-        offset = 0
-        lineprograms = []
-        while offset < self.debug_line_sec.size:
-            # Similarly to CU parsing, peek at the initial_length field of the
-            # header to figure out the DWARF format for it.
-            initial_length = struct_parse(
-                self.structs.Dwarf_uint32(''), self.debug_line_sec, offset)
-            dwarf_format = 64 if initial_length == 0xFFFFFFFF else 32
+        lineprog_header = struct_parse(
+            structs.Dwarf_lineprog_header,
+            self.debug_line_sec.stream,
+            debug_line_offset)
 
-            # Prepare the structs for this line program, based on its format
-            # and the default endianness. The address_size plays no role for
-            # line programs so we just give it a default value.
-            lineprog_structs = DWARFStructs(
-                little_endian=self.little_endian,
-                dwarf_format=dwarf_format,
-                address_size=4)
-
-            lineprog_header = struct_parse(
-                lineprog_structs.Dwarf_lineprog_header,
-                self.debug_line_sec.stream,
-                offset)
-
-            lineprograms.append(LineProgram(
-                header=lineprog_header,
-                dwarfinfo=self,
-                structs=lineprog_structs))
+        # Calculate the offset to the next line program (see DWARF 6.2.4)
+        end_offset = (  debug_line_offset + lineprog_header['unit_length'] +
+                        structs.initial_length_field_size())
 
-            # Calculate the offset to the next line program (see DWARF 6.2.4)
-            offset += ( lineprog_header['unit_length'] +
-                        lineprog_structs.initial_length_field_size())
-        return lineprograms
+        return LineProgram(
+            header=lineprog_header,
+            stream=self.debug_line_sec.stream,
+            structs=structs,
+            program_start_offset=self.debug_line_sec.stream.tell(),
+            program_end_offset=end_offset)