1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/callframe.py
4 # DWARF call frame information
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
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 *
19 class CallFrameInfo(object):
20 """ DWARF CFI (Call Frame Info)
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>.
27 A stream holding the .debug_frame section, and the size of the
31 Virtual address for this section. This is used to decode relative
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.
47 def __init__(self
, stream
, size
, address
, base_structs
,
51 self
.address
= address
52 self
.base_structs
= base_structs
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
= {}
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
63 self
.for_eh_frame
= for_eh_frame
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
70 if self
.entries
is None:
71 self
.entries
= self
._parse
_entries
()
74 #-------------------------
76 def _parse_entries(self
):
79 while offset
< self
.size
:
80 entries
.append(self
._parse
_entry
_at
(offset
))
81 offset
= self
.stream
.tell()
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
89 if offset
in self
._entry
_cache
:
90 return self
._entry
_cache
[offset
]
92 entry_length
= struct_parse(
93 self
.base_structs
.Dwarf_uint32(''), self
.stream
, offset
)
95 if self
.for_eh_frame
and entry_length
== 0:
98 dwarf_format
= 64 if entry_length
== 0xFFFFFFFF else 32
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
)
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
)
109 if self
.for_eh_frame
:
113 (dwarf_format
== 32 and CIE_id
== 0xFFFFFFFF) or
114 CIE_id
== 0xFFFFFFFFFFFFFFFF)
116 # Parse the header, which goes up to and excluding the sequence of
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
)
125 header
= self
._parse
_fde
_header
(entry_structs
, offset
)
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
)
136 # If the augmentation string is not empty, hope to find a length field
137 # in order to skip the data specified augmentation.
139 aug_bytes
, aug_dict
= self
._parse
_cie
_augmentation
(
140 header
, entry_structs
)
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']:
147 lsda_pointer
= self
._parse
_lsda
_pointer
(entry_structs
,
148 self
.stream
.tell() - len(aug_bytes
),
153 # For convenience, compute the end offset for this entry
155 offset
+ header
.length
+
156 entry_structs
.initial_length_field_size())
158 # At this point self.stream is at the start of the instruction list
160 instructions
= self
._parse
_instructions
(
161 entry_structs
, self
.stream
.tell(), end_offset
)
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
)
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
,
178 return self
._entry
_cache
[offset
]
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.
186 while offset
< end_offset
:
187 opcode
= struct_parse(structs
.Dwarf_uint8(''), self
.stream
, offset
)
190 primary
= opcode
& _PRIMARY_MASK
191 primary_arg
= opcode
& _PRIMARY_ARG_MASK
192 if primary
== DW_CFA_advance_loc
:
194 elif primary
== DW_CFA_offset
:
197 struct_parse(structs
.Dwarf_uleb128(''), self
.stream
)]
198 elif primary
== DW_CFA_restore
:
200 # primary == 0 and real opcode is extended
201 elif opcode
in (DW_CFA_nop
, DW_CFA_remember_state
,
202 DW_CFA_restore_state
):
204 elif opcode
== DW_CFA_set_loc
:
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
):
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
):
229 struct_parse(structs
.Dwarf_uleb128(''), self
.stream
),
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
):
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
)]
240 dwarf_assert(False, 'Unknown CFI opcode: 0x%x' % opcode
)
242 instructions
.append(CallFrameInstruction(opcode
=opcode
, args
=args
))
243 offset
= self
.stream
.tell()
246 def _parse_cie_for_fde(self
, fde_offset
, fde_header
, entry_structs
):
247 """ Parse the CIE that corresponds to an FDE.
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
258 cie_offset
= fde_header
['CIE_pointer']
261 with
preserve_stream_pos(self
.stream
):
262 return self
._parse
_entry
_at
(cie_offset
)
264 def _parse_cie_augmentation(self
, header
, entry_structs
):
265 """ Parse CIE augmentation data from the annotation string in `header`.
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.
270 augmentation
= header
.get('augmentation')
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
)))
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'),
286 entry_structs
.Dwarf_uint8('encoding'),
287 Switch('function', lambda ctx
: ctx
.encoding
& 0x0f, {
288 enc
: fld_cons('function')
290 in self
._eh
_encoding
_to
_field
(entry_structs
).items()})),
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.
298 for b
in iterbytes(augmentation
):
300 fld
= available_fields
[b
]
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
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
)
321 def _read_augmentation_data(self
, entry_structs
):
322 """ Read augmentation data.
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.
327 if not self
.for_eh_frame
:
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
)
336 def _parse_lsda_pointer(self
, structs
, stream_offset
, encoding
):
337 """ Parse bytes to get an LSDA pointer.
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.
343 Ref: https://www.airs.com/blog/archives/460
345 assert encoding
!= DW_EH_encoding_flags
['DW_EH_PE_omit']
346 basic_encoding
= encoding
& 0x0f
347 modifier
= encoding
& 0xf0
349 formats
= self
._eh
_encoding
_to
_field
(structs
)
352 Struct('Augmentation_Data',
353 formats
[basic_encoding
]('LSDA_pointer')),
354 self
.stream
, stream_pos
=stream_offset
)['LSDA_pointer']
356 if modifier
== DW_EH_encoding_flags
['DW_EH_PE_absptr']:
359 elif modifier
== DW_EH_encoding_flags
['DW_EH_PE_pcrel']:
360 ptr
+= self
.address
+ stream_offset
363 assert False, 'Unsupported encoding modifier for LSDA pointer: {:#x}'.format(modifier
)
367 def _parse_fde_header(self
, entry_structs
, offset
):
368 """ Compute a struct to parse the header of the current FDE.
370 if not self
.for_eh_frame
:
371 return struct_parse(entry_structs
.Dwarf_FDE_header
, self
.stream
,
374 fields
= [entry_structs
.Dwarf_initial_length('length'),
375 entry_structs
.Dwarf_offset('CIE_pointer')]
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()
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
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'))
397 result
= struct_parse(Struct('Dwarf_FDE_header', *fields
),
400 if encoding_modifier
== 0:
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
)
409 assert False, 'Unsupported encoding: {:#x}'.format(encoding
)
414 def _eh_encoding_to_field(entry_structs
):
416 Return a mapping from basic encodings (DW_EH_encoding_flags) the
417 corresponding field constructors (for instance
418 entry_structs.Dwarf_uint32).
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
,
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
,
443 def instruction_name(opcode
):
444 """ Given an opcode, return the instruction name.
446 primary
= opcode
& _PRIMARY_MASK
448 return _OPCODE_NAME_MAP
[opcode
]
450 return _OPCODE_NAME_MAP
[primary
]
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.
459 def __init__(self
, opcode
, args
):
464 return '%s (0x%x): %s' % (
465 instruction_name(self
.opcode
), self
.opcode
, self
.args
)
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.
480 def __init__(self
, header
, structs
, instructions
, offset
,
481 augmentation_dict
=None, augmentation_bytes
=b
'', cie
=None):
483 self
.structs
= structs
484 self
.instructions
= instructions
487 self
._decoded
_table
= None
488 self
.augmentation_dict
= augmentation_dict
if augmentation_dict
else {}
489 self
.augmentation_bytes
= augmentation_bytes
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.
496 if self
._decoded
_table
is None:
497 self
._decoded
_table
= self
._decode
_CFI
_table
()
498 return self
._decoded
_table
500 def __getitem__(self
, name
):
501 """ Implement dict-like access to header entries
503 return self
.header
[name
]
505 def _decode_CFI_table(self
):
506 """ Decode the instructions contained in the given CFI entry and return
507 a DecodedCallFrameTable.
509 if isinstance(self
, CIE
):
510 # For a CIE, initialize cur_line to an "empty" line
512 cur_line
= dict(pc
=0, cfa
=CFARule(reg
=None, offset
=0))
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.
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
)
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
)
530 # Keeps a stack for the use of DW_CFA_{remember|restore}_state
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
)
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.
545 name
= instruction_name(instr
.opcode
)
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(
557 offset
=instr
.args
[1])
558 elif name
== 'DW_CFA_def_cfa_sf':
559 cur_line
['cfa'] = CFARule(
561 offset
=instr
.args
[1] * cie
['code_alignment_factor'])
562 elif name
== 'DW_CFA_def_cfa_register':
563 cur_line
['cfa'] = CFARule(
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(
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
,
594 elif name
== 'DW_CFA_expression':
595 _add_to_order(instr
.args
[0])
596 cur_line
[instr
.args
[0]] = RegisterRule(
597 RegisterRule
.EXPRESSION
,
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
,
604 elif name
in ('DW_CFA_restore', 'DW_CFA_restore_extended'):
605 _add_to_order(instr
.args
[0])
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]]
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':
617 cur_line
= line_stack
.pop()
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
)
625 return DecodedCallFrameTable(table
=table
, reg_order
=reg_order
)
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).
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
644 """ End marker for the sequence of CIE/FDE.
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
650 def __init__(self
, offset
):
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.
659 UNDEFINED
= 'UNDEFINED'
660 SAME_VALUE
= 'SAME_VALUE'
662 VAL_OFFSET
= 'VAL_OFFSET'
663 REGISTER
= 'REGISTER'
664 EXPRESSION
= 'EXPRESSION'
665 VAL_EXPRESSION
= 'VAL_EXPRESSION'
666 ARCHITECTURAL
= 'ARCHITECTURAL'
668 def __init__(self
, type, arg
=None):
673 return 'RegisterRule(%s, %s)' % (self
.type, self
.arg
)
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.
680 def __init__(self
, reg
=None, offset
=None, expr
=None):
686 return 'CFARule(reg=%s, offset=%s, expr=%s)' % (
687 self
.reg
, self
.offset
, self
.expr
)
690 # Represents the decoded CFI for an entry, which is just a large table,
691 # according to DWARFv3 section 6.4.1
693 # DecodedCallFrameTable is a simple named tuple to group together the table
694 # and the register appearance order.
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.
706 # A list of register numbers that are described in the table by the order of
709 DecodedCallFrameTable
= namedtuple(
710 'DecodedCallFrameTable', 'table reg_order')
713 #---------------- PRIVATE ----------------#
715 _PRIMARY_MASK
= 0b11000000
716 _PRIMARY_ARG_MASK
= 0b00111111
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