# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
-class ELFError(Exception):
+class ELFError(Exception):
pass
class ELFRelocationError(ELFError):
pass
-
+
class ELFParseError(ELFError):
pass
A probe: dumps the context, stack frames, and stream content to the screen
to aid the debugging process.
See also Debugger.
-
+
Parameters:
* name - the display name
- * show_stream - whether or not to show stream contents. default is True.
+ * show_stream - whether or not to show stream contents. default is True.
the stream must be seekable.
* show_context - whether or not to show the context. default is True.
- * show_stack - whether or not to show the upper stack frames. default
+ * show_stack - whether or not to show the upper stack frames. default
is True.
* stream_lookahead - the number of bytes to dump when show_stack is set.
default is 100.
-
+
Example:
Struct("foo",
UBInt8("a"),
)
"""
__slots__ = [
- "printname", "show_stream", "show_context", "show_stack",
+ "printname", "show_stream", "show_context", "show_stack",
"stream_lookahead"
]
counter = 0
-
- def __init__(self, name = None, show_stream = True,
- show_context = True, show_stack = True,
+
+ def __init__(self, name = None, show_stream = True,
+ show_context = True, show_stack = True,
stream_lookahead = 100):
Construct.__init__(self, None)
if name is None:
self.printout(stream, context)
def _sizeof(self, context):
return 0
-
+
def printout(self, stream, context):
obj = Container()
if self.show_stream:
stream.seek(-len(follows), 1)
obj.following_stream_data = HexString(follows)
print
-
+
if self.show_context:
obj.context = context
-
+
if self.show_stack:
obj.stack = ListContainer()
frames = [s[0] for s in inspect.stack()][1:-1]
a = Container()
a.__update__(f.f_locals)
obj.stack.append(a)
-
+
print("=" * 80)
print("Probe", self.printname)
print(obj)
"""
A pdb-based debugger. When an exception occurs in the subcon, a debugger
will appear and allow you to debug the error (and even fix on-the-fly).
-
+
Parameters:
* subcon - the subcon to debug
-
+
Example:
Debugger(
Enum(UBInt8("foo"),
print(msg)
pdb.post_mortem(sys.exc_info()[2])
print("=" * 80)
-
_bit_values = {
- 0: 0,
- 1: 1,
+ 0: 0,
+ 1: 1,
48: 0, # '0'
49: 1, # '1'
def encode_bin(data):
- """
+ """
Create a binary representation of the given b'' object. Assume 8-bit
ASCII. Example:
def decode_bin(data):
- """
+ """
Locical opposite of decode_bin.
"""
if len(data) & 7:
i += 8
j += 1
return b"".join(chars)
-
def __new__(cls, data, *args, **kwargs):
return bytes.__new__(cls, data)
-
+
def __str__(self):
if not self:
return "''"
sep = "\n"
return sep + sep.join(
hexdump(self, self.linesize))
-
return bytes(b, encoding)
advance_iterator = next
-
+
else:
import cStringIO
StringIO = BytesIO = cStringIO.StringIO
def advance_iterator(it):
return it.next()
-
from bisect import bisect_right
import math
-# An entry in the aranges table;
+# An entry in the aranges table;
# begin_addr: The beginning address in the CU
# length: The length of the address range in this entry
# info_offset: The CU's offset into .debug_info
# see 6.1.2 in DWARF4 docs for explanation of the remaining fields
-ARangeEntry = namedtuple('ARangeEntry',
+ARangeEntry = namedtuple('ARangeEntry',
'begin_addr length info_offset unit_length version address_size segment_size')
class ARanges(object):
""" ARanges table in DWARF
- stream, size:
+ stream, size:
A stream holding the .debug_aranges section, and its size
- structs:
+ structs:
A DWARFStructs instance for parsing the data
"""
def __init__(self, stream, size, structs):
"""
tup = self.entries[bisect_right(self.keys, addr) - 1]
return tup.info_offset
-
+
#------ PRIVATE ------#
def _get_entries(self):
# one loop == one "set" == one CU
while offset < self.size :
- aranges_header = struct_parse(self.structs.Dwarf_aranges_header,
+ aranges_header = struct_parse(self.structs.Dwarf_aranges_header,
self.stream, offset)
addr_size = self._get_addr_size_struct(aranges_header["address_size"])
# No segmentation
if aranges_header["segment_size"] == 0:
# pad to nearest multiple of tuple size
- tuple_size = aranges_header["address_size"] * 2
+ tuple_size = aranges_header["address_size"] * 2
fp = self.stream.tell()
seek_to = int(math.ceil(fp/float(tuple_size)) * tuple_size)
self.stream.seek(seek_to)
while addr != 0 or length != 0:
# 'begin_addr length info_offset version address_size segment_size'
entries.append(
- ARangeEntry(begin_addr=addr,
- length=length,
+ ARangeEntry(begin_addr=addr,
+ length=length,
info_offset=aranges_header["debug_info_offset"],
unit_length=aranges_header["unit_length"],
version=aranges_header["version"],
elif aranges_header["segment_size"] != 0:
raise NotImplementedError("Segmentation not implemented")
- offset = (offset
- + aranges_header.unit_length
+ offset = (offset
+ + aranges_header.unit_length
+ self.structs.initial_length_field_size())
return entries
def _get_addr_size_struct(self, addr_header_value):
- """ Given this set's header value (int) for the address size,
+ """ Given this set's header value (int) for the address size,
get the Construct representation of that size
"""
if addr_header_value == 4:
return self.structs.Dwarf_uint32
- else:
+ else:
assert addr_header_value == 8
return self.structs.Dwarf_uint64
def _add_to_order(regnum):
# DW_CFA_restore and others remove registers from cur_line,
# but they stay in reg_order. Avoid duplicates.
- if regnum not in reg_order:
+ if regnum not in reg_order:
reg_order.append(regnum)
for instr in self.instructions:
def parse_arg_struct2(arg1_struct, arg2_struct):
return lambda stream: [struct_parse(arg1_struct, stream),
struct_parse(arg2_struct, stream)]
-
+
# ULEB128, then an expression of that length
def parse_nestedexpr():
def parse(stream):
add('DW_OP_call2', parse_arg_struct(structs.Dwarf_uint16('')))
add('DW_OP_call4', parse_arg_struct(structs.Dwarf_uint32('')))
add('DW_OP_call_ref', parse_arg_struct(structs.Dwarf_offset('')))
- add('DW_OP_implicit_value', parse_blob())
+ add('DW_OP_implicit_value', parse_blob())
add('DW_OP_GNU_entry_value', parse_nestedexpr())
add('DW_OP_GNU_const_type', parse_typedblob())
add('DW_OP_GNU_regval_type', parse_arg_struct2(structs.Dwarf_uleb128(''),
"""
def __init__(self, header, stream, structs,
program_start_offset, program_end_offset):
- """
+ """
header:
The header of this line program. Note: LineProgram may modify
its header by appending file entries if DW_LNE_define_file
return self._decoded_entries
#------ PRIVATE ------#
-
+
def __getitem__(self, name):
""" Implement dict-like access to header entries
"""
offset = self.program_start_offset
while offset < self.program_end_offset:
opcode = struct_parse(
- self.structs.Dwarf_uint8(''),
+ self.structs.Dwarf_uint8(''),
self.stream,
offset)
adjusted_opcode = opcode - self['opcode_base']
operation_advance = adjusted_opcode // self['line_range']
address_addend = (
- self['minimum_instruction_length'] *
+ self['minimum_instruction_length'] *
((state.op_index + operation_advance) //
maximum_operations_per_instruction))
state.address += address_addend
state.end_sequence = True
add_entry_new_state(ex_opcode, [], is_extended=True)
# reset state
- state = LineState(self.header['default_is_stmt'])
+ state = LineState(self.header['default_is_stmt'])
elif ex_opcode == DW_LNE_set_address:
operand = struct_parse(self.structs.Dwarf_target_addr(''),
self.stream)
opcode,))
offset = self.stream.tell()
return entries
-
self.Dwarf_nameLUT_header = Struct("Dwarf_nameLUT_header",
self.Dwarf_initial_length('unit_length'),
self.Dwarf_uint16('version'),
- self.Dwarf_offset('debug_info_offset'),
+ self.Dwarf_offset('debug_info_offset'),
self.Dwarf_length('debug_info_length')
)
class VER_FLAGS(object):
VER_FLG_BASE=0x1
VER_FLG_WEAK=0x2
- VER_FLG_INFO=0x4
+ VER_FLG_INFO=0x4
elif tag == 'TAG_NODEFAULTS':
return _DESCR_ATTR_TAG_ARM[tag] + 'True'
-
+
s = _DESCR_ATTR_TAG_ARM[tag]
s += '"%s"' % val if val else ''
return s
"""
def __init__(self, stream, elffile, stringtable, position, empty):
"""
- stream:
+ stream:
The file-like object from which to load data
- elffile:
+ elffile:
The parent elffile object
- stringtable:
+ stringtable:
A stringtable reference to use for parsing string references in
entries
- position:
+ position:
The file offset of the dynamic segment/section
- empty:
+ empty:
Whether this is a degenerate case with zero entries. Normally, every
dynamic table will have at least one entry, the DT_NULL terminator.
"""
return self._tag['tag']
def __repr__(self):
- s = '<ARMAttribute (%s): %r>' % (self.tag, self.value)
+ s = '<ARMAttribute (%s): %r>' % (self.tag, self.value)
s += ' %s' % self.extra if self.extra is not None else ''
return s
yield ARMAttribute(self.structs, self.stream)
def __repr__(self):
- s = "<ARMAttributesSubsubsection (%s): %d bytes>"
+ s = "<ARMAttributesSubsubsection (%s): %d bytes>"
return s % (self.header.tag[4:], self.header.value)
print('ERROR: No .debug_pubnames section found in ELF.')
else:
print('%d entries found in .debug_pubnames' % len(pubnames))
-
+
# try getting information on a global symbol.
print('Trying pubnames example ...')
sym_name = 'main'
if cu.cu_offset == entry.cu_ofs:
for die in cu.iter_DIEs():
if die.offset == entry.die_ofs:
- print('Die Name: %s' %
+ print('Die Name: %s' %
bytes2str(die.attributes['DW_AT_name'].value))
-
+
# dump all entries in .debug_pubnames section.
print('Dumping .debug_pubnames table ...')
print('-' * 66)
if cu.cu_offset == entry.cu_ofs:
for die in cu.iter_DIEs():
if die.offset == entry.die_ofs:
- print('Die Name: %s' %
+ print('Die Name: %s' %
bytes2str(die.attributes['DW_AT_name'].value))
-
+
# dump all entries in .debug_pubtypes section.
print('Dumping .debug_pubtypes table ...')
print('-' * 66)
)
from elftools.dwarf.constants import (
DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
-from elftools.dwarf.locationlists import LocationParser, LocationEntry
+from elftools.dwarf.locationlists import LocationParser, LocationEntry
from elftools.dwarf.callframe import CIE, FDE, ZERO
section = self._dwarfinfo.debug_pubtypes_sec
# readelf prints nothing if the section is not present.
- if namelut is None or len(namelut) == 0:
+ if namelut is None or len(namelut) == 0:
return
-
+
self._emitline('Contents of the %s section:' % section.name)
self._emitline()
-
+
cu_headers = namelut.get_cu_headers()
# go over CU-by-CU first and item-by-item next.
self._emitline(' Length: %d' % cu_hdr.unit_length)
self._emitline(' Version: %d' % cu_hdr.version)
self._emitline(' Offset into .debug_info section: 0x%x' % cu_hdr.debug_info_offset)
- self._emitline(' Size of area in .debug_info section: %d' % cu_hdr.debug_info_length)
+ self._emitline(' Size of area in .debug_info section: %d' % cu_hdr.debug_info_length)
self._emitline()
self._emitline(' Offset Name')
for item in items:
elif 'DW_AT_entry_pc' in attr:
return attr['DW_AT_entry_pc'].value
else:
- raise ValueError("Can't find the base IP (low_pc) for a CU")
+ raise ValueError("Can't find the base IP (low_pc) for a CU")
di = self._dwarfinfo
loc_lists = di.location_lists()
# To dump a location list, one needs to know the CU.
# Scroll through DIEs once, list the known location list offsets
- cu_map = dict() # Loc list offset => CU
+ cu_map = dict() # Loc list offset => CU
for cu in di.iter_CUs():
for die in cu.iter_DIEs():
for key in die.attributes:
addr_size = di.config.default_address_size # In bytes, 4 or 8
addr_width = addr_size * 2 # In hex digits, 8 or 16
- line_template = " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width, addr_width)
+ line_template = " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width, addr_width)
self._emitline('Contents of the %s section:\n' % di.debug_loc_sec.name)
self._emitline(' Offset Begin End Expression')