the relocation manager for dwarf works! sdf
[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 ..construct import CString
12 from ..common.exceptions import DWARFError
13 from ..common.utils import struct_parse, dwarf_assert
14 from .structs import DWARFStructs
15 from .compileunit import CompileUnit
16 from .abbrevtable import AbbrevTable
17 from .dwarfrelocationmanager import DWARFRelocationManager
18
19
20 # Describes a debug section in a stream: offset and size
21 #
22 DebugSectionLocator = namedtuple('DebugSectionLocator', 'offset size')
23
24
25 class DWARFInfo(object):
26 """ Acts also as a "context" to other major objects, bridging between
27 various parts of the debug infromation.
28 """
29 def __init__(self,
30 stream,
31 elffile,
32 debug_info_loc,
33 debug_abbrev_loc,
34 debug_str_loc,
35 debug_line_loc):
36 """ stream:
37 A stream (file-like object) that contains debug sections
38
39 elffile:
40 ELFFile reference
41
42 debug_*_loc:
43 DebugSectionLocator for this section, specifying where it can
44 be found in the stream
45 """
46 self.stream = stream
47 self.debug_info_loc = debug_info_loc
48 self.debug_abbrev_loc = debug_abbrev_loc
49 self.debug_str_loc = debug_str_loc
50 self.debug_line_loc = debug_line_loc
51
52 self.elffile = elffile
53 self.little_endian = self.elffile.little_endian
54
55 self.relocation_manager = {}
56 self.relocation_manager['.debug_info'] = DWARFRelocationManager(
57 elffile=self.elffile,
58 section_name='.debug_info')
59
60 # This is the DWARFStructs the context uses, so it doesn't depend on
61 # DWARF format and address_size (these are determined per CU) - set them
62 # to default values.
63 self.structs = DWARFStructs(
64 little_endian=self.little_endian,
65 dwarf_format=32,
66 address_size=4)
67
68 # Populate the list with CUs found in debug_info. For each CU only its
69 # header is parsed immediately (the abbrev table isn't loaded before
70 # it's being referenced by one of the CU's DIEs).
71 # Since there usually aren't many CUs in a single object, this
72 # shouldn't present a performance problem.
73 #
74 self._CU = self._parse_CUs()
75
76 # Cache for abbrev tables: a dict keyed by offset
77 self._abbrevtable_cache = {}
78
79 def num_CUs(self):
80 """ Number of compile units in the debug info
81 """
82 return len(self._CU)
83
84 def get_CU(self, n):
85 """ Get the compile unit (CompileUnit object) at index #n
86 """
87 return self._CU[n]
88
89 def iter_CUs(self):
90 """ Yield all the compile units (CompileUnit objects) in the debug info
91 """
92 for i in range(self.num_CUs()):
93 yield self.get_CU(i)
94
95 def get_abbrev_table(self, offset):
96 """ Get an AbbrevTable from the given offset in the debug_abbrev
97 section.
98
99 The only verification done on the offset is that it's within the
100 bounds of the section (if not, an exception is raised).
101 It is the caller's responsibility to make sure the offset actually
102 points to a valid abbreviation table.
103
104 AbbrevTable objects are cached internally (two calls for the same
105 offset will return the same object).
106 """
107 dwarf_assert(
108 offset < self.debug_abbrev_loc.size,
109 "Offset '0x%x' to abbrev table out of section bounds" % offset)
110 if offset not in self._abbrevtable_cache:
111 self._abbrevtable_cache[offset] = AbbrevTable(
112 structs=self.structs,
113 stream=self.stream,
114 offset=offset + self.debug_abbrev_loc.offset)
115 return self._abbrevtable_cache[offset]
116
117 def info_offset2absolute(self, offset):
118 """ Given an offset into the debug_info section, translate it to an
119 absolute offset into the stream. Raise an exception if the offset
120 exceeds the section bounds.
121 """
122 dwarf_assert(
123 offset < self.debug_info_loc.size,
124 "Offset '0x%x' to debug_info out of section bounds" % offset)
125 return offset + self.debug_info_loc.offset
126
127 def get_string_from_table(self, offset):
128 """ Obtain a string from the string table section, given an offset
129 relative to the section.
130 """
131 return struct_parse(
132 CString(''),
133 self.stream,
134 stream_pos=self.debug_str_loc.offset + offset)
135
136 #------ PRIVATE ------#
137
138 def _parse_CUs(self):
139 """ Parse CU entries from debug_info.
140 """
141 offset = self.debug_info_loc.offset
142 section_boundary = self.debug_info_loc.offset + self.debug_info_loc.size
143 CUlist = []
144 while offset < section_boundary:
145 # Section 7.4 (32-bit and 64-bit DWARF Formats) of the DWARF spec v3
146 # states that the first 32-bit word of the CU header determines
147 # whether the CU is represented with 32-bit or 64-bit DWARF format.
148 #
149 # So we peek at the first word in the CU header to determine its
150 # dwarf format. Based on it, we then create a new DWARFStructs
151 # instance suitable for this CU and use it to parse the rest.
152 #
153 initial_length = struct_parse(
154 self.structs.Dwarf_uint32(''), self.stream, offset)
155 dwarf_format = 64 if initial_length == 0xFFFFFFFF else 32
156
157 # At this point we still haven't read the whole header, so we don't
158 # know the address_size. Therefore, we're going to create structs
159 # with a default address_size=4. If, after parsing the header, we
160 # find out address_size is actually 8, we just create a new structs
161 # object for this CU.
162 #
163 cu_structs = DWARFStructs(
164 little_endian=self.little_endian,
165 dwarf_format=dwarf_format,
166 address_size=4)
167
168 cu_header = struct_parse(
169 cu_structs.Dwarf_CU_header, self.stream, offset)
170 if cu_header['address_size'] == 8:
171 cu_structs = DWARFStructs(
172 little_endian=self.little_endian,
173 dwarf_format=dwarf_format,
174 address_size=8)
175
176 cu_die_offset = self.stream.tell()
177 dwarf_assert(
178 self._is_supported_version(cu_header['version']),
179 "Expected supported DWARF version. Got '%s'" % cu_header['version'])
180 CUlist.append(CompileUnit(
181 header=cu_header,
182 dwarfinfo=self,
183 structs=cu_structs,
184 cu_offset=offset,
185 cu_die_offset=cu_die_offset))
186 # Compute the offset of the next CU in the section. The unit_length
187 # field of the CU header contains its size not including the length
188 # field itself.
189 offset = ( offset +
190 cu_header['unit_length'] +
191 cu_structs.initial_length_field_size())
192 return CUlist
193
194 def _is_supported_version(self, version):
195 """ DWARF version supported by this parser
196 """
197 return 2 <= version <= 3
198