Fix for mixed version loclists, tests (#521)
[pyelftools.git] / elftools / dwarf / callframe.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/callframe.py
3 #
4 # DWARF call frame information
5 #
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 import copy
10 from collections import namedtuple
11 from ..common.utils import (
12 struct_parse, dwarf_assert, preserve_stream_pos, iterbytes)
13 from ..construct import Struct, Switch
14 from .enums import DW_EH_encoding_flags
15 from .structs import DWARFStructs
16 from .constants import *
17
18
19 class CallFrameInfo(object):
20 """ DWARF CFI (Call Frame Info)
21
22 Note that this also supports unwinding information as found in .eh_frame
23 sections: its format differs slightly from the one in .debug_frame. See
24 <http://www.airs.com/blog/archives/460>.
25
26 stream, size:
27 A stream holding the .debug_frame section, and the size of the
28 section in it.
29
30 address:
31 Virtual address for this section. This is used to decode relative
32 addresses.
33
34 base_structs:
35 The structs to be used as the base for parsing this section.
36 Eventually, each entry gets its own structs based on the initial
37 length field it starts with. The address_size, however, is taken
38 from base_structs. This appears to be a limitation of the DWARFv3
39 standard, fixed in v4.
40 A discussion I had on dwarf-discuss confirms this.
41 So for DWARFv4 we'll take the address size from the CIE header,
42 but for earlier versions will use the elfclass of the containing
43 file; more sophisticated methods are used by libdwarf and others,
44 such as guessing which CU contains which FDEs (based on their
45 address ranges) and taking the address_size from those CUs.
46 """
47 def __init__(self, stream, size, address, base_structs,
48 for_eh_frame=False):
49 self.stream = stream
50 self.size = size
51 self.address = address
52 self.base_structs = base_structs
53 self.entries = None
54
55 # Map between an offset in the stream and the entry object found at this
56 # offset. Useful for assigning CIE to FDEs according to the CIE_pointer
57 # header field which contains a stream offset.
58 self._entry_cache = {}
59
60 # The .eh_frame and .debug_frame section use almost the same CFI
61 # encoding, but there are tiny variations we need to handle during
62 # parsing.
63 self.for_eh_frame = for_eh_frame
64
65 def get_entries(self):
66 """ Get a list of entries that constitute this CFI. The list consists
67 of CIE or FDE objects, in the order of their appearance in the
68 section.
69 """
70 if self.entries is None:
71 self.entries = self._parse_entries()
72 return self.entries
73
74 #-------------------------
75
76 def _parse_entries(self):
77 entries = []
78 offset = 0
79 while offset < self.size:
80 entries.append(self._parse_entry_at(offset))
81 offset = self.stream.tell()
82 return entries
83
84 def _parse_entry_at(self, offset):
85 """ Parse an entry from self.stream starting with the given offset.
86 Return the entry object. self.stream will point right after the
87 entry.
88 """
89 if offset in self._entry_cache:
90 return self._entry_cache[offset]
91
92 entry_length = struct_parse(
93 self.base_structs.Dwarf_uint32(''), self.stream, offset)
94
95 if self.for_eh_frame and entry_length == 0:
96 return ZERO(offset)
97
98 dwarf_format = 64 if entry_length == 0xFFFFFFFF else 32
99
100 entry_structs = DWARFStructs(
101 little_endian=self.base_structs.little_endian,
102 dwarf_format=dwarf_format,
103 address_size=self.base_structs.address_size)
104
105 # Read the next field to see whether this is a CIE or FDE
106 CIE_id = struct_parse(
107 entry_structs.Dwarf_offset(''), self.stream)
108
109 if self.for_eh_frame:
110 is_CIE = CIE_id == 0
111 else:
112 is_CIE = (
113 (dwarf_format == 32 and CIE_id == 0xFFFFFFFF) or
114 CIE_id == 0xFFFFFFFFFFFFFFFF)
115
116 # Parse the header, which goes up to and excluding the sequence of
117 # instructions.
118 if is_CIE:
119 header_struct = (entry_structs.EH_CIE_header
120 if self.for_eh_frame else
121 entry_structs.Dwarf_CIE_header)
122 header = struct_parse(
123 header_struct, self.stream, offset)
124 else:
125 header = self._parse_fde_header(entry_structs, offset)
126
127
128 # If this is DWARF version 4 or later, we can have a more precise
129 # address size, read from the CIE header.
130 if not self.for_eh_frame and entry_structs.dwarf_version >= 4:
131 entry_structs = DWARFStructs(
132 little_endian=entry_structs.little_endian,
133 dwarf_format=entry_structs.dwarf_format,
134 address_size=header.address_size)
135
136 # If the augmentation string is not empty, hope to find a length field
137 # in order to skip the data specified augmentation.
138 if is_CIE:
139 aug_bytes, aug_dict = self._parse_cie_augmentation(
140 header, entry_structs)
141 else:
142 cie = self._parse_cie_for_fde(offset, header, entry_structs)
143 aug_bytes = self._read_augmentation_data(entry_structs)
144 lsda_encoding = cie.augmentation_dict.get('LSDA_encoding', DW_EH_encoding_flags['DW_EH_PE_omit'])
145 if lsda_encoding != DW_EH_encoding_flags['DW_EH_PE_omit']:
146 # parse LSDA pointer
147 lsda_pointer = self._parse_lsda_pointer(entry_structs,
148 self.stream.tell() - len(aug_bytes),
149 lsda_encoding)
150 else:
151 lsda_pointer = None
152
153 # For convenience, compute the end offset for this entry
154 end_offset = (
155 offset + header.length +
156 entry_structs.initial_length_field_size())
157
158 # At this point self.stream is at the start of the instruction list
159 # for this entry
160 instructions = self._parse_instructions(
161 entry_structs, self.stream.tell(), end_offset)
162
163 if is_CIE:
164 self._entry_cache[offset] = CIE(
165 header=header, instructions=instructions, offset=offset,
166 augmentation_dict=aug_dict,
167 augmentation_bytes=aug_bytes,
168 structs=entry_structs)
169
170 else: # FDE
171 cie = self._parse_cie_for_fde(offset, header, entry_structs)
172 self._entry_cache[offset] = FDE(
173 header=header, instructions=instructions, offset=offset,
174 structs=entry_structs, cie=cie,
175 augmentation_bytes=aug_bytes,
176 lsda_pointer=lsda_pointer,
177 )
178 return self._entry_cache[offset]
179
180 def _parse_instructions(self, structs, offset, end_offset):
181 """ Parse a list of CFI instructions from self.stream, starting with
182 the offset and until (not including) end_offset.
183 Return a list of CallFrameInstruction objects.
184 """
185 instructions = []
186 while offset < end_offset:
187 opcode = struct_parse(structs.Dwarf_uint8(''), self.stream, offset)
188 args = []
189
190 primary = opcode & _PRIMARY_MASK
191 primary_arg = opcode & _PRIMARY_ARG_MASK
192 if primary == DW_CFA_advance_loc:
193 args = [primary_arg]
194 elif primary == DW_CFA_offset:
195 args = [
196 primary_arg,
197 struct_parse(structs.Dwarf_uleb128(''), self.stream)]
198 elif primary == DW_CFA_restore:
199 args = [primary_arg]
200 # primary == 0 and real opcode is extended
201 elif opcode in (DW_CFA_nop, DW_CFA_remember_state,
202 DW_CFA_restore_state):
203 args = []
204 elif opcode == DW_CFA_set_loc:
205 args = [
206 struct_parse(structs.Dwarf_target_addr(''), self.stream)]
207 elif opcode == DW_CFA_advance_loc1:
208 args = [struct_parse(structs.Dwarf_uint8(''), self.stream)]
209 elif opcode == DW_CFA_advance_loc2:
210 args = [struct_parse(structs.Dwarf_uint16(''), self.stream)]
211 elif opcode == DW_CFA_advance_loc4:
212 args = [struct_parse(structs.Dwarf_uint32(''), self.stream)]
213 elif opcode in (DW_CFA_offset_extended, DW_CFA_register,
214 DW_CFA_def_cfa, DW_CFA_val_offset):
215 args = [
216 struct_parse(structs.Dwarf_uleb128(''), self.stream),
217 struct_parse(structs.Dwarf_uleb128(''), self.stream)]
218 elif opcode in (DW_CFA_restore_extended, DW_CFA_undefined,
219 DW_CFA_same_value, DW_CFA_def_cfa_register,
220 DW_CFA_def_cfa_offset):
221 args = [struct_parse(structs.Dwarf_uleb128(''), self.stream)]
222 elif opcode == DW_CFA_def_cfa_offset_sf:
223 args = [struct_parse(structs.Dwarf_sleb128(''), self.stream)]
224 elif opcode == DW_CFA_def_cfa_expression:
225 args = [struct_parse(
226 structs.Dwarf_dw_form['DW_FORM_block'], self.stream)]
227 elif opcode in (DW_CFA_expression, DW_CFA_val_expression):
228 args = [
229 struct_parse(structs.Dwarf_uleb128(''), self.stream),
230 struct_parse(
231 structs.Dwarf_dw_form['DW_FORM_block'], self.stream)]
232 elif opcode in (DW_CFA_offset_extended_sf,
233 DW_CFA_def_cfa_sf, DW_CFA_val_offset_sf):
234 args = [
235 struct_parse(structs.Dwarf_uleb128(''), self.stream),
236 struct_parse(structs.Dwarf_sleb128(''), self.stream)]
237 elif opcode == DW_CFA_GNU_args_size:
238 args = [struct_parse(structs.Dwarf_uleb128(''), self.stream)]
239 else:
240 dwarf_assert(False, 'Unknown CFI opcode: 0x%x' % opcode)
241
242 instructions.append(CallFrameInstruction(opcode=opcode, args=args))
243 offset = self.stream.tell()
244 return instructions
245
246 def _parse_cie_for_fde(self, fde_offset, fde_header, entry_structs):
247 """ Parse the CIE that corresponds to an FDE.
248 """
249 # Determine the offset of the CIE that corresponds to this FDE
250 if self.for_eh_frame:
251 # CIE_pointer contains the offset for a reverse displacement from
252 # the section offset of the CIE_pointer field itself (not from the
253 # FDE header offset).
254 cie_displacement = fde_header['CIE_pointer']
255 cie_offset = (fde_offset + entry_structs.dwarf_format // 8
256 - cie_displacement)
257 else:
258 cie_offset = fde_header['CIE_pointer']
259
260 # Then read it
261 with preserve_stream_pos(self.stream):
262 return self._parse_entry_at(cie_offset)
263
264 def _parse_cie_augmentation(self, header, entry_structs):
265 """ Parse CIE augmentation data from the annotation string in `header`.
266
267 Return a tuple that contains 1) the augmentation data as a string
268 (without the length field) and 2) the augmentation data as a dict.
269 """
270 augmentation = header.get('augmentation')
271 if not augmentation:
272 return ('', {})
273
274 # Augmentation parsing works in minimal mode here: we need the length
275 # field to be able to skip unhandled augmentation fields.
276 assert augmentation.startswith(b'z'), (
277 'Unhandled augmentation string: {}'.format(repr(augmentation)))
278
279 available_fields = {
280 b'z': entry_structs.Dwarf_uleb128('length'),
281 b'L': entry_structs.Dwarf_uint8('LSDA_encoding'),
282 b'R': entry_structs.Dwarf_uint8('FDE_encoding'),
283 b'S': True,
284 b'P': Struct(
285 'personality',
286 entry_structs.Dwarf_uint8('encoding'),
287 Switch('function', lambda ctx: ctx.encoding & 0x0f, {
288 enc: fld_cons('function')
289 for enc, fld_cons
290 in self._eh_encoding_to_field(entry_structs).items()})),
291 }
292
293 # Build the Struct we will be using to parse the augmentation data.
294 # Stop as soon as we are not able to match the augmentation string.
295 fields = []
296 aug_dict = {}
297
298 for b in iterbytes(augmentation):
299 try:
300 fld = available_fields[b]
301 except KeyError:
302 break
303
304 if fld is True:
305 aug_dict[fld] = True
306 else:
307 fields.append(fld)
308
309 # Read the augmentation twice: once with the Struct, once for the raw
310 # bytes. Read the raw bytes last so we are sure we leave the stream
311 # pointing right after the augmentation: the Struct may be incomplete
312 # (missing trailing fields) due to an unknown char: see the KeyError
313 # above.
314 offset = self.stream.tell()
315 struct = Struct('Augmentation_Data', *fields)
316 aug_dict.update(struct_parse(struct, self.stream, offset))
317 self.stream.seek(offset)
318 aug_bytes = self._read_augmentation_data(entry_structs)
319 return (aug_bytes, aug_dict)
320
321 def _read_augmentation_data(self, entry_structs):
322 """ Read augmentation data.
323
324 This assumes that the augmentation string starts with 'z', i.e. that
325 augmentation data is prefixed by a length field, which is not returned.
326 """
327 if not self.for_eh_frame:
328 return b''
329
330 augmentation_data_length = struct_parse(
331 Struct('Dummy_Augmentation_Data',
332 entry_structs.Dwarf_uleb128('length')),
333 self.stream)['length']
334 return self.stream.read(augmentation_data_length)
335
336 def _parse_lsda_pointer(self, structs, stream_offset, encoding):
337 """ Parse bytes to get an LSDA pointer.
338
339 The basic encoding (lower four bits of the encoding) describes how the values are encoded in a CIE or an FDE.
340 The modifier (upper four bits of the encoding) describes how the raw values, after decoded using a basic
341 encoding, should be modified before using.
342
343 Ref: https://www.airs.com/blog/archives/460
344 """
345 assert encoding != DW_EH_encoding_flags['DW_EH_PE_omit']
346 basic_encoding = encoding & 0x0f
347 modifier = encoding & 0xf0
348
349 formats = self._eh_encoding_to_field(structs)
350
351 ptr = struct_parse(
352 Struct('Augmentation_Data',
353 formats[basic_encoding]('LSDA_pointer')),
354 self.stream, stream_pos=stream_offset)['LSDA_pointer']
355
356 if modifier == DW_EH_encoding_flags['DW_EH_PE_absptr']:
357 pass
358
359 elif modifier == DW_EH_encoding_flags['DW_EH_PE_pcrel']:
360 ptr += self.address + stream_offset
361
362 else:
363 assert False, 'Unsupported encoding modifier for LSDA pointer: {:#x}'.format(modifier)
364
365 return ptr
366
367 def _parse_fde_header(self, entry_structs, offset):
368 """ Compute a struct to parse the header of the current FDE.
369 """
370 if not self.for_eh_frame:
371 return struct_parse(entry_structs.Dwarf_FDE_header, self.stream,
372 offset)
373
374 fields = [entry_structs.Dwarf_initial_length('length'),
375 entry_structs.Dwarf_offset('CIE_pointer')]
376
377 # Parse the couple of header fields that are always here so we can
378 # fetch the corresponding CIE.
379 minimal_header = struct_parse(Struct('eh_frame_minimal_header',
380 *fields), self.stream, offset)
381 cie = self._parse_cie_for_fde(offset, minimal_header, entry_structs)
382 initial_location_offset = self.stream.tell()
383
384 # Try to parse the initial location. We need the initial location in
385 # order to create a meaningful FDE, so assume it's there. Omission does
386 # not seem to happen in practice.
387 encoding = cie.augmentation_dict['FDE_encoding']
388 assert encoding != DW_EH_encoding_flags['DW_EH_PE_omit']
389 basic_encoding = encoding & 0x0f
390 encoding_modifier = encoding & 0xf0
391
392 # Depending on the specified encoding, complete the header Struct
393 formats = self._eh_encoding_to_field(entry_structs)
394 fields.append(formats[basic_encoding]('initial_location'))
395 fields.append(formats[basic_encoding]('address_range'))
396
397 result = struct_parse(Struct('Dwarf_FDE_header', *fields),
398 self.stream, offset)
399
400 if encoding_modifier == 0:
401 pass
402
403 elif encoding_modifier == DW_EH_encoding_flags['DW_EH_PE_pcrel']:
404 # Start address is relative to the address of the
405 # "initial_location" field.
406 result['initial_location'] += (
407 self.address + initial_location_offset)
408 else:
409 assert False, 'Unsupported encoding: {:#x}'.format(encoding)
410
411 return result
412
413 @staticmethod
414 def _eh_encoding_to_field(entry_structs):
415 """
416 Return a mapping from basic encodings (DW_EH_encoding_flags) the
417 corresponding field constructors (for instance
418 entry_structs.Dwarf_uint32).
419 """
420 return {
421 DW_EH_encoding_flags['DW_EH_PE_absptr']:
422 entry_structs.Dwarf_target_addr,
423 DW_EH_encoding_flags['DW_EH_PE_uleb128']:
424 entry_structs.Dwarf_uleb128,
425 DW_EH_encoding_flags['DW_EH_PE_udata2']:
426 entry_structs.Dwarf_uint16,
427 DW_EH_encoding_flags['DW_EH_PE_udata4']:
428 entry_structs.Dwarf_uint32,
429 DW_EH_encoding_flags['DW_EH_PE_udata8']:
430 entry_structs.Dwarf_uint64,
431
432 DW_EH_encoding_flags['DW_EH_PE_sleb128']:
433 entry_structs.Dwarf_sleb128,
434 DW_EH_encoding_flags['DW_EH_PE_sdata2']:
435 entry_structs.Dwarf_int16,
436 DW_EH_encoding_flags['DW_EH_PE_sdata4']:
437 entry_structs.Dwarf_int32,
438 DW_EH_encoding_flags['DW_EH_PE_sdata8']:
439 entry_structs.Dwarf_int64,
440 }
441
442
443 def instruction_name(opcode):
444 """ Given an opcode, return the instruction name.
445 """
446 primary = opcode & _PRIMARY_MASK
447 if primary == 0:
448 return _OPCODE_NAME_MAP[opcode]
449 else:
450 return _OPCODE_NAME_MAP[primary]
451
452
453 class CallFrameInstruction(object):
454 """ An instruction in the CFI section. opcode is the instruction
455 opcode, numeric - as it appears in the section. args is a list of
456 arguments (including arguments embedded in the low bits of some
457 instructions, when applicable), decoded from the stream.
458 """
459 def __init__(self, opcode, args):
460 self.opcode = opcode
461 self.args = args
462
463 def __repr__(self):
464 return '%s (0x%x): %s' % (
465 instruction_name(self.opcode), self.opcode, self.args)
466
467
468 class CFIEntry(object):
469 """ A common base class for CFI entries.
470 Contains a header and a list of instructions (CallFrameInstruction).
471 offset: the offset of this entry from the beginning of the section
472 cie: for FDEs, a CIE pointer is required
473 augmentation_dict: Augmentation data as a parsed struct (dict): see
474 CallFrameInfo._parse_cie_augmentation and
475 http://www.airs.com/blog/archives/460.
476 augmentation_bytes: Augmentation data as a chain of bytes: see
477 CallFrameInfo._parse_cie_augmentation and
478 http://www.airs.com/blog/archives/460.
479 """
480 def __init__(self, header, structs, instructions, offset,
481 augmentation_dict=None, augmentation_bytes=b'', cie=None):
482 self.header = header
483 self.structs = structs
484 self.instructions = instructions
485 self.offset = offset
486 self.cie = cie
487 self._decoded_table = None
488 self.augmentation_dict = augmentation_dict if augmentation_dict else {}
489 self.augmentation_bytes = augmentation_bytes
490
491 def get_decoded(self):
492 """ Decode the CFI contained in this entry and return a
493 DecodedCallFrameTable object representing it. See the documentation
494 of that class to understand how to interpret the decoded table.
495 """
496 if self._decoded_table is None:
497 self._decoded_table = self._decode_CFI_table()
498 return self._decoded_table
499
500 def __getitem__(self, name):
501 """ Implement dict-like access to header entries
502 """
503 return self.header[name]
504
505 def _decode_CFI_table(self):
506 """ Decode the instructions contained in the given CFI entry and return
507 a DecodedCallFrameTable.
508 """
509 if isinstance(self, CIE):
510 # For a CIE, initialize cur_line to an "empty" line
511 cie = self
512 cur_line = dict(pc=0, cfa=CFARule(reg=None, offset=0))
513 reg_order = []
514 else: # FDE
515 # For a FDE, we need to decode the attached CIE first, because its
516 # decoded table is needed. Its "initial instructions" describe a
517 # line that serves as the base (first) line in the FDE's table.
518 cie = self.cie
519 cie_decoded_table = cie.get_decoded()
520 if len(cie_decoded_table.table) > 0:
521 last_line_in_CIE = copy.copy(cie_decoded_table.table[-1])
522 cur_line = copy.copy(last_line_in_CIE)
523 else:
524 cur_line = dict(cfa=CFARule(reg=None, offset=0))
525 cur_line['pc'] = self['initial_location']
526 reg_order = copy.copy(cie_decoded_table.reg_order)
527
528 table = []
529
530 # Keeps a stack for the use of DW_CFA_{remember|restore}_state
531 # instructions.
532 line_stack = []
533
534 def _add_to_order(regnum):
535 # DW_CFA_restore and others remove registers from cur_line,
536 # but they stay in reg_order. Avoid duplicates.
537 if regnum not in reg_order:
538 reg_order.append(regnum)
539
540 for instr in self.instructions:
541 # Throughout this loop, cur_line is the current line. Some
542 # instructions add it to the table, but most instructions just
543 # update it without adding it to the table.
544
545 name = instruction_name(instr.opcode)
546
547 if name == 'DW_CFA_set_loc':
548 table.append(copy.copy(cur_line))
549 cur_line['pc'] = instr.args[0]
550 elif name in ( 'DW_CFA_advance_loc1', 'DW_CFA_advance_loc2',
551 'DW_CFA_advance_loc4', 'DW_CFA_advance_loc'):
552 table.append(copy.copy(cur_line))
553 cur_line['pc'] += instr.args[0] * cie['code_alignment_factor']
554 elif name == 'DW_CFA_def_cfa':
555 cur_line['cfa'] = CFARule(
556 reg=instr.args[0],
557 offset=instr.args[1])
558 elif name == 'DW_CFA_def_cfa_sf':
559 cur_line['cfa'] = CFARule(
560 reg=instr.args[0],
561 offset=instr.args[1] * cie['code_alignment_factor'])
562 elif name == 'DW_CFA_def_cfa_register':
563 cur_line['cfa'] = CFARule(
564 reg=instr.args[0],
565 offset=cur_line['cfa'].offset)
566 elif name == 'DW_CFA_def_cfa_offset':
567 cur_line['cfa'] = CFARule(
568 reg=cur_line['cfa'].reg,
569 offset=instr.args[0])
570 elif name == 'DW_CFA_def_cfa_expression':
571 cur_line['cfa'] = CFARule(expr=instr.args[0])
572 elif name == 'DW_CFA_undefined':
573 _add_to_order(instr.args[0])
574 cur_line[instr.args[0]] = RegisterRule(RegisterRule.UNDEFINED)
575 elif name == 'DW_CFA_same_value':
576 _add_to_order(instr.args[0])
577 cur_line[instr.args[0]] = RegisterRule(RegisterRule.SAME_VALUE)
578 elif name in ( 'DW_CFA_offset', 'DW_CFA_offset_extended',
579 'DW_CFA_offset_extended_sf'):
580 _add_to_order(instr.args[0])
581 cur_line[instr.args[0]] = RegisterRule(
582 RegisterRule.OFFSET,
583 instr.args[1] * cie['data_alignment_factor'])
584 elif name in ('DW_CFA_val_offset', 'DW_CFA_val_offset_sf'):
585 _add_to_order(instr.args[0])
586 cur_line[instr.args[0]] = RegisterRule(
587 RegisterRule.VAL_OFFSET,
588 instr.args[1] * cie['data_alignment_factor'])
589 elif name == 'DW_CFA_register':
590 _add_to_order(instr.args[0])
591 cur_line[instr.args[0]] = RegisterRule(
592 RegisterRule.REGISTER,
593 instr.args[1])
594 elif name == 'DW_CFA_expression':
595 _add_to_order(instr.args[0])
596 cur_line[instr.args[0]] = RegisterRule(
597 RegisterRule.EXPRESSION,
598 instr.args[1])
599 elif name == 'DW_CFA_val_expression':
600 _add_to_order(instr.args[0])
601 cur_line[instr.args[0]] = RegisterRule(
602 RegisterRule.VAL_EXPRESSION,
603 instr.args[1])
604 elif name in ('DW_CFA_restore', 'DW_CFA_restore_extended'):
605 _add_to_order(instr.args[0])
606 dwarf_assert(
607 isinstance(self, FDE),
608 '%s instruction must be in a FDE' % name)
609 if instr.args[0] in last_line_in_CIE:
610 cur_line[instr.args[0]] = last_line_in_CIE[instr.args[0]]
611 else:
612 cur_line.pop(instr.args[0], None)
613 elif name == 'DW_CFA_remember_state':
614 line_stack.append(copy.deepcopy(cur_line))
615 elif name == 'DW_CFA_restore_state':
616 pc = cur_line['pc']
617 cur_line = line_stack.pop()
618 cur_line['pc'] = pc
619
620 # The current line is appended to the table after all instructions
621 # have ended, if there were instructions.
622 if cur_line['cfa'].reg is not None or len(cur_line) > 2:
623 table.append(cur_line)
624
625 return DecodedCallFrameTable(table=table, reg_order=reg_order)
626
627
628 # A CIE and FDE have exactly the same functionality, except that a FDE has
629 # a pointer to its CIE. The functionality was wholly encapsulated in CFIEntry,
630 # so the CIE and FDE classes exists separately for identification (instead
631 # of having an explicit "entry_type" field in CFIEntry).
632 #
633 class CIE(CFIEntry):
634 pass
635
636
637 class FDE(CFIEntry):
638 def __init__(self, header, structs, instructions, offset, augmentation_bytes=None, cie=None, lsda_pointer=None):
639 super(FDE, self).__init__(header, structs, instructions, offset, augmentation_bytes=augmentation_bytes, cie=cie)
640 self.lsda_pointer = lsda_pointer
641
642
643 class ZERO(object):
644 """ End marker for the sequence of CIE/FDE.
645
646 This is specific to `.eh_frame` sections: this kind of entry does not exist
647 in pure DWARF. `readelf` displays these as "ZERO terminator", hence the
648 class name.
649 """
650 def __init__(self, offset):
651 self.offset = offset
652
653
654 class RegisterRule(object):
655 """ Register rules are used to find registers in call frames. Each rule
656 consists of a type (enumeration following DWARFv3 section 6.4.1)
657 and an optional argument to augment the type.
658 """
659 UNDEFINED = 'UNDEFINED'
660 SAME_VALUE = 'SAME_VALUE'
661 OFFSET = 'OFFSET'
662 VAL_OFFSET = 'VAL_OFFSET'
663 REGISTER = 'REGISTER'
664 EXPRESSION = 'EXPRESSION'
665 VAL_EXPRESSION = 'VAL_EXPRESSION'
666 ARCHITECTURAL = 'ARCHITECTURAL'
667
668 def __init__(self, type, arg=None):
669 self.type = type
670 self.arg = arg
671
672 def __repr__(self):
673 return 'RegisterRule(%s, %s)' % (self.type, self.arg)
674
675
676 class CFARule(object):
677 """ A CFA rule is used to compute the CFA for each location. It either
678 consists of a register+offset, or a DWARF expression.
679 """
680 def __init__(self, reg=None, offset=None, expr=None):
681 self.reg = reg
682 self.offset = offset
683 self.expr = expr
684
685 def __repr__(self):
686 return 'CFARule(reg=%s, offset=%s, expr=%s)' % (
687 self.reg, self.offset, self.expr)
688
689
690 # Represents the decoded CFI for an entry, which is just a large table,
691 # according to DWARFv3 section 6.4.1
692 #
693 # DecodedCallFrameTable is a simple named tuple to group together the table
694 # and the register appearance order.
695 #
696 # table:
697 #
698 # A list of dicts that represent "lines" in the decoded table. Each line has
699 # some special dict entries: 'pc' for the location/program counter (LOC),
700 # and 'cfa' for the CFARule to locate the CFA on that line.
701 # The other entries are keyed by register numbers with RegisterRule values,
702 # and describe the rules for these registers.
703 #
704 # reg_order:
705 #
706 # A list of register numbers that are described in the table by the order of
707 # their appearance.
708 #
709 DecodedCallFrameTable = namedtuple(
710 'DecodedCallFrameTable', 'table reg_order')
711
712
713 #---------------- PRIVATE ----------------#
714
715 _PRIMARY_MASK = 0b11000000
716 _PRIMARY_ARG_MASK = 0b00111111
717
718 # This dictionary is filled by automatically scanning the constants module
719 # for DW_CFA_* instructions, and mapping their values to names. Since all
720 # names were imported from constants with `import *`, we look in globals()
721 _OPCODE_NAME_MAP = {}
722 for name in list(globals().keys()):
723 if name.startswith('DW_CFA'):
724 _OPCODE_NAME_MAP[globals()[name]] = name