--- /dev/null
+#-------------------------------------------------------------------------------
+# elftools: dwarf/callframe.py
+#
+# DWARF call frame information
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from ..common.utils import (struct_parse)
+from .structs import DWARFStructs
+
+
+class CallFrameInfo(object):
+ def __init__(self, stream, size, base_structs):
+ self.stream = stream
+ self.size = size
+ self.base_structs = base_structs
+
+ def _parse_entries(self):
+ offset = 0
+ while offset < self.size:
+ entry_length = struct_parse(
+ self.base_structs.Dwarf_uint32(''), self.stream, offset)
+ dwarf_format = 64 if entry_length == 0xFFFFFFFF else 32
+
+ entry_structs = DWARFStructs(
+ little_endian=self.base_structs.little_endian,
+ dwarf_format=dwarf_format,
+ address_size=self.base_structs.address_size)
+
+ # ZZZ: it will be easier to split entry reading:
+ # header: what comes before the instructions
+ # the instructions are parsed separately (their length is computed
+ # from the length and the tell() after parsing the header)
+
#
# machine_arch:
# Machine architecture as a string. For example 'x86' or 'x64'
-#
+#
#
DwarfConfig = namedtuple('DwarfConfig',
'little_endian machine_arch')
config,
debug_info_sec,
debug_abbrev_sec,
+ debug_frame_sec,
debug_str_sec,
debug_line_sec):
""" config:
self.config = config
self.debug_info_sec = debug_info_sec
self.debug_abbrev_sec = debug_abbrev_sec
+ self.debug_frame_sec = debug_frame_sec
self.debug_str_sec = debug_str_sec
self.debug_line_sec = debug_line_sec
little_endian=self.config.little_endian,
dwarf_format=32,
address_size=4)
-
+
# 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 = {}
-
+
def iter_CUs(self):
""" Yield all the compile units (CompileUnit objects) in the debug info
"""
def get_abbrev_table(self, offset):
""" Get an AbbrevTable from the given offset in the debug_abbrev
section.
-
+
The only verification done on the offset is that it's within the
bounds of the section (if not, an exception is raised).
It is the caller's responsibility to make sure the offset actually
points to a valid abbreviation table.
-
+
AbbrevTable objects are cached internally (two calls for the same
offset will return the same object).
"""
stream=self.debug_abbrev_sec.stream,
offset=offset)
return self._abbrevtable_cache[offset]
-
+
def get_string_from_table(self, offset):
""" Obtain a string from the string table section, given an offset
relative to the section.
"""
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.
top_DIE.attributes['DW_AT_stmt_list'].value, CU.structs)
else:
return None
-
+
#------ PRIVATE ------#
-
+
def _parse_CUs(self):
""" Parse CU entries from debug_info.
"""
initial_length = struct_parse(
self.structs.Dwarf_uint32(''), self.debug_info_sec.stream, offset)
dwarf_format = 64 if initial_length == 0xFFFFFFFF else 32
-
+
# At this point we still haven't read the whole header, so we don't
# know the address_size. Therefore, we're going to create structs
# with a default address_size=4. If, after parsing the header, we
Note that this contains more information than absolutely required
for the line table. The line table can be easily extracted from
the list of entries by looking only at entries with non-None
- state.
+ state. The extra information is mainly for the purposes of display
+ with readelf and debugging.
"""
if self._decoded_entries is None:
self._decoded_entries = self._decode_line_program()
Dwarf_lineprog_file_entry (+):
A single file entry in a line program header or instruction
+ Dwarf_CIE_header (+):
+ A call-frame CIE
+
+ Dwarf_FDE_header (+):
+ A call-frame FDE
+
See also the documentation of public methods.
"""
def __init__(self, little_endian, dwarf_format, address_size):
lambda obj, ctx: len(obj.name) == 0,
self.Dwarf_lineprog_file_entry),
)
-
+
+ def _create_callframe_entry_headers(self):
+ self.Dwarf_CIE_header = Struct('Dwarf_CIE_header',
+ self.Dwarf_initial_length('length'),
+ self.Dwarf_offset('CIE_id'),
+ self.Dwarf_uint8('version'),
+ CString('augmentation'),
+ self.Dwarf_uleb128('code_alignment_factor'),
+ self.Dwarf_sleb128('data_alignment_factor'),
+ self.Dwarf_uleb128('return_address_register'))
+
+ self.Dwarf_FDE_header = Struct('Dwarf_FDE_header',
+ self.Dwarf_initial_length('length'),
+ self.Dwarf_offset('CIE_pointer'),
+ self.Dwarf_target_addr('initial_location'),
+ self.Dwarf_target_addr('address_range'))
+
def _make_block_struct(self, length_field):
""" Create a struct for DW_FORM_block<size>
"""
"""
segment_header = self._get_segment_header(n)
return self._make_segment(segment_header)
-
+
def iter_segments(self):
""" Yield all the segments in the file
"""
for i in range(self.num_segments()):
yield self.get_segment(i)
-
+
def has_dwarf_info(self):
""" Check whether this file appears to have debugging information.
We assume that if it has the debug_info section, it has all theother
required sections as well.
"""
return bool(self.get_section_by_name('.debug_info'))
-
+
def get_dwarf_info(self, relocate_dwarf_sections=True):
""" Return a DWARFInfo object representing the debugging information in
this file.
"""
# Expect that has_dwarf_info was called, so at least .debug_info is
# present. Check also the presence of other must-have debug sections.
+ # Sections that aren't found will be passed as None to DWARFInfo.
#
debug_sections = {}
for secname in ('.debug_info', '.debug_abbrev', '.debug_str',
- '.debug_line'):
+ '.debug_line', '.debug_frame'):
section = self.get_section_by_name(secname)
- elf_assert(
- section is not None,
- "Expected to find DWARF section '%s' in the file" % (
- secname))
- debug_sections[secname] = self._read_dwarf_section(
- section,
- relocate_dwarf_sections)
-
+ if section is None:
+ debug_sections[secname] = None
+ else:
+ debug_sections[secname] = self._read_dwarf_section(
+ section,
+ relocate_dwarf_sections)
+
return DWARFInfo(
config=DwarfConfig(
little_endian=self.little_endian,
machine_arch=self.get_machine_arch()),
debug_info_sec=debug_sections['.debug_info'],
debug_abbrev_sec=debug_sections['.debug_abbrev'],
+ debug_frame_sec=debug_sections['.debug_frame'],
debug_str_sec=debug_sections['.debug_str'],
- debug_line_sec=debug_sections['.debug_line'])
+ debug_line_sec=debug_sections['.debug_line'])
def get_machine_arch(self):
if self['e_machine'] == 'EM_X86_64':
return 'x86'
else:
return '<unknown>'
-
+
#-------------------------------- PRIVATE --------------------------------#
-
+
def __getitem__(self, name):
""" Implement dict-like access to header entries
"""
return self.header[name]
-
+
def _identify_file(self):
""" Verify the ELF file and identify its class and endianness.
"""
self.stream.seek(0)
magic = self.stream.read(4)
elf_assert(magic == '\x7fELF', 'Magic number does not match')
-
+
ei_class = self.stream.read(1)
if ei_class == '\x01':
self.elfclass = 32
self.elfclass = 64
else:
raise ELFError('Invalid EI_CLASS %s' % repr(ei_class))
-
+
ei_data = self.stream.read(1)
if ei_data == '\x01':
self.little_endian = True