abbrev table almost functioning
[pyelftools.git] / elftools / dwarf / dwarfinfo.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/dwarfinfo.py
3 #
4 # DWARFInfo - Main class for accessing DWARF debug information
5 #
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 from collections import namedtuple
10
11 from ..common.exceptions import DWARFError
12 from ..common.utils import struct_parse, dwarf_assert
13 from .structs import DWARFStructs
14 from .compileunit import CompileUnit
15 from .abbrevtable import AbbrevTable
16
17
18 # Describes a debug section in a stream: offset and size
19 #
20 DebugSectionLocator = namedtuple('DebugSectionLocator', 'offset size')
21
22
23 class DWARFInfo(object):
24 """ Acts also as a "context" to other major objects, bridging between
25 various parts of the debug infromation.
26 """
27 def __init__(self,
28 stream,
29 little_endian,
30 debug_info_loc,
31 debug_abbrev_loc,
32 debug_str_loc,
33 debug_line_loc):
34 """ stream:
35 A stream (file-like object) that contains debug sections
36
37 little_endian:
38 Section contents are in little-endian data format
39
40 debug_*_loc:
41 DebugSectionLocator for this section, specifying where it can
42 be found in the stream
43 """
44 self.stream = stream
45 self.debug_info_loc = debug_info_loc
46 self.debug_abbrev_loc = debug_abbrev_loc
47 self.debug_str_loc = debug_str_loc
48 self.debug_line_loc = debug_line_loc
49
50 self.little_endian = little_endian
51 self.dwarf_format = 32
52 self.structs = DWARFStructs(
53 little_endian=self.little_endian,
54 dwarf_format=self.dwarf_format)
55
56 # Populate the list with CUs found in debug_info
57 self._CU = self._parse_CUs()
58
59 # Cache for abbrev tables: a dict keyed by offset
60 self._abbrevtable_cache = {}
61
62 def get_abbrev_table(self, offset):
63 """ Get an AbbrevTable from the given offset in the debug_abbrev
64 section.
65
66 The only verification done on the offset is that it's within the
67 bounds of the section (if not, an exception is raised).
68 It is the caller's responsibility to make sure the offset actually
69 points to a valid abbreviation table.
70
71 AbbrevTable objects are cached internally (two calls for the same
72 offset will return the same object).
73 """
74 dwarf_assert(
75 offset < self.debug_abbrev_loc.size,
76 "Offset '0x%x' to abbrev table out of section bounds" % offset)
77 if offset not in self._abbrevtable_cache:
78 self._abbrevtable_cache[offset] = AbbrevTable(
79 structs=self.structs,
80 stream=self.stream,
81 offset=offset + self.debug_abbrev_loc.offset)
82 return self._abbrevtable_cache[offset]
83
84 def _parse_CUs(self):
85 """ Parse CU entries from debug_info.
86 """
87 offset = self.debug_info_loc.offset
88 section_boundary = self.debug_info_loc.offset + self.debug_info_loc.size
89 CUlist = []
90 while offset < section_boundary:
91 # Section 7.4 (32-bit and 64-bit DWARF Formats) of the DWARF spec v3
92 # states that the first 32-bit word of the CU header determines
93 # whether the CU is represented with 32-bit or 64-bit DWARF format.
94 #
95 # So we peek at the first word in the CU header to determine its
96 # dwarf format. Based on it, we then create a new DWARFStructs
97 # instance suitable for this CU and use it to parse the rest.
98 #
99 initial_length = struct_parse(
100 self.structs.Dwarf_uint32(''), self.stream, offset)
101 if initial_length == 0xFFFFFFFF:
102 self.dwarf_format = 64
103 cu_structs = DWARFStructs(
104 little_endian=self.little_endian,
105 dwarf_format=self.dwarf_format)
106
107 cu_header = struct_parse(
108 cu_structs.Dwarf_CU_header, self.stream, offset)
109 dwarf_assert(
110 self._is_supported_version(cu_header['version']),
111 "Expected supported DWARF version. Got '%s'" % cu_header['version'])
112 CUlist.append(CompileUnit(
113 header=cu_header,
114 dwarfinfo=self,
115 structs=cu_structs))
116 # Compute the offset of the next CU in the section. The unit_length
117 # field of the CU header contains its size not including the length
118 # field itself.
119 offset = ( offset +
120 cu_header['unit_length'] +
121 cu_structs.initial_lenght_field_size())
122 return CUlist
123
124 def _is_supported_version(self, version):
125 """ DWARF version supported by this parser
126 """
127 return 2 <= version <= 3
128