class CallFrameInfo(object):
+ """ DWARF CFI (Call Frame Info)
+
+ stream, size:
+ A stream holding the .debug_frame section, and the size of the
+ section in it.
+
+ base_structs:
+ The structs to be used as the base for parsing this section.
+ Eventually, each entry gets its own structs based on the initial
+ length field it starts with. The address_size, however, is taken
+ from base_structs. This appears to be a limitation of the DWARFv3
+ standard, fixed in v4 (where an address_size field exists for each
+ CFI. I had a discussion about this on dwarf-discuss that confirms
+ this.
+ Currently for base_structs I simply use the elfclass of the
+ containing file, but more sophisticated methods are used by
+ libdwarf and others, such as guessing which CU contains which FDEs
+ (based on their address ranges) and taking the address_size from
+ those CUs.
+ """
def __init__(self, stream, size, base_structs):
self.stream = stream
self.size = size
entries.append(new_entry_class(
header=header,
instructions=instructions))
- # ZZZ: for FDE's, I need some offset->CIE mapping cache stored
+ # ZZZ: for FDE's, I need some offset->CIE mapping cache stored
offset = self.stream.tell()
return entries
dwarf_assert(False, 'Unknown CFI opcode: 0x%x' % opcode)
instructions.append(CallFrameInstruction(opcode=opcode, args=args))
- print instructions[-1]
offset = self.stream.tell()
return instructions
--- /dev/null
+import sys, unittest
+from cStringIO import StringIO
+
+sys.path.extend(['.', '..'])
+from elftools.dwarf.callframe import (
+ CallFrameInfo, CIE, FDE, instruction_name, CallFrameInstruction)
+from elftools.dwarf.structs import DWARFStructs
+
+
+class TestCallFrame(unittest.TestCase):
+ def assertInstruction(self, instr, name, args):
+ self.assertIsInstance(instr, CallFrameInstruction)
+ self.assertEqual(instruction_name(instr.opcode), name)
+ self.assertEqual(instr.args, args)
+
+ def test_spec_sample_d6(self):
+ # D.6 sample in DWARFv3
+ s = StringIO()
+ data = ('' +
+ # first comes the CIE
+ '\x20\x00\x00\x00' + # length
+ '\xff\xff\xff\xff' + # CIE_id
+ '\x03\x00\x04\x7c' + # version, augmentation, caf, daf
+ '\x08' + # return address
+ '\x0c\x07\x00' +
+ '\x08\x00' +
+ '\x07\x01' +
+ '\x07\x02' +
+ '\x07\x03' +
+ '\x08\x04' +
+ '\x08\x05' +
+ '\x08\x06' +
+ '\x08\x07' +
+ '\x09\x08\x01' +
+ '\x00' +
+
+ # then comes the FDE
+ '\x28\x00\x00\x00' + # length
+ '\x00\x00\x00\x00' + # CIE_pointer (to CIE at 0)
+ '\x44\x33\x22\x11' + # initial_location
+ '\x54\x00\x00\x00' + # address range
+ '\x41' +
+ '\x0e\x0c' + '\x41' +
+ '\x88\x01' + '\x41' +
+ '\x86\x02' + '\x41' +
+ '\x0d\x06' + '\x41' +
+ '\x84\x03' + '\x4b' +
+ '\xc4' + '\x41' +
+ '\xc6' +
+ '\x0d\x07' + '\x41' +
+ '\xc8' + '\x41' +
+ '\x0e\x00' +
+ '\x00\x00'
+ )
+ s.write(data)
+
+ structs = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4)
+ cfi = CallFrameInfo(s, len(data), structs)
+ entries = cfi.get_entries()
+
+ self.assertEqual(len(entries), 2)
+ self.assertIsInstance(entries[0], CIE)
+ self.assertEqual(entries[0]['length'], 32)
+ self.assertEqual(entries[0]['data_alignment_factor'], -4)
+ self.assertEqual(entries[0]['return_address_register'], 8)
+ self.assertInstruction(entries[0].instructions[0],
+ 'DW_CFA_def_cfa', [7, 0])
+
+ self.assertTrue(isinstance(entries[1], FDE))
+
+if __name__ == '__main__':
+ unittest.main()
+
+