Remove BytesIO and StringIO from py3compat
[pyelftools.git] / test / test_callframe.py
1 #-------------------------------------------------------------------------------
2 # elftools tests
3 #
4 # Eli Bendersky (eliben@gmail.com)
5 # This code is in the public domain
6 #-------------------------------------------------------------------------------
7 import unittest
8 from io import BytesIO
9
10 from elftools.dwarf.callframe import (
11 CallFrameInfo, CIE, FDE, instruction_name, CallFrameInstruction,
12 RegisterRule, DecodedCallFrameTable, CFARule)
13 from elftools.dwarf.structs import DWARFStructs
14 from elftools.dwarf.descriptions import (describe_CFI_instructions,
15 set_global_machine_arch)
16 from elftools.dwarf.enums import DW_EH_encoding_flags
17 from elftools.elf.elffile import ELFFile
18 from os.path import join
19
20
21 class TestCallFrame(unittest.TestCase):
22 def assertInstruction(self, instr, name, args):
23 self.assertIsInstance(instr, CallFrameInstruction)
24 self.assertEqual(instruction_name(instr.opcode), name)
25 self.assertEqual(instr.args, args)
26
27 def test_spec_sample_d6(self):
28 # D.6 sample in DWARFv3
29 s = BytesIO()
30 data = (b'' +
31 # first comes the CIE
32 b'\x20\x00\x00\x00' + # length
33 b'\xff\xff\xff\xff' + # CIE_id
34 b'\x03\x00\x04\x7c' + # version, augmentation, caf, daf
35 b'\x08' + # return address
36 b'\x0c\x07\x00' +
37 b'\x08\x00' +
38 b'\x07\x01' +
39 b'\x07\x02' +
40 b'\x07\x03' +
41 b'\x08\x04' +
42 b'\x08\x05' +
43 b'\x08\x06' +
44 b'\x08\x07' +
45 b'\x09\x08\x01' +
46 b'\x00' +
47
48 # then comes the FDE
49 b'\x28\x00\x00\x00' + # length
50 b'\x00\x00\x00\x00' + # CIE_pointer (to CIE at 0)
51 b'\x44\x33\x22\x11' + # initial_location
52 b'\x54\x00\x00\x00' + # address range
53 b'\x41' +
54 b'\x0e\x0c' + b'\x41' +
55 b'\x88\x01' + b'\x41' +
56 b'\x86\x02' + b'\x41' +
57 b'\x0d\x06' + b'\x41' +
58 b'\x84\x03' + b'\x4b' +
59 b'\xc4' + b'\x41' +
60 b'\xc6' +
61 b'\x0d\x07' + b'\x41' +
62 b'\xc8' + b'\x41' +
63 b'\x0e\x00' +
64 b'\x00\x00'
65 )
66 s.write(data)
67
68 structs = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4)
69 cfi = CallFrameInfo(s, len(data), 0, structs)
70 entries = cfi.get_entries()
71
72 self.assertEqual(len(entries), 2)
73 self.assertIsInstance(entries[0], CIE)
74 self.assertEqual(entries[0]['length'], 32)
75 self.assertEqual(entries[0]['data_alignment_factor'], -4)
76 self.assertEqual(entries[0]['return_address_register'], 8)
77 self.assertEqual(len(entries[0].instructions), 11)
78 self.assertInstruction(entries[0].instructions[0],
79 'DW_CFA_def_cfa', [7, 0])
80 self.assertInstruction(entries[0].instructions[8],
81 'DW_CFA_same_value', [7])
82 self.assertInstruction(entries[0].instructions[9],
83 'DW_CFA_register', [8, 1])
84
85 self.assertTrue(isinstance(entries[1], FDE))
86 self.assertEqual(entries[1]['length'], 40)
87 self.assertEqual(entries[1]['CIE_pointer'], 0)
88 self.assertEqual(entries[1]['address_range'], 84)
89 self.assertIsNone(entries[1].lsda_pointer)
90 self.assertIs(entries[1].cie, entries[0])
91 self.assertEqual(len(entries[1].instructions), 21)
92 self.assertInstruction(entries[1].instructions[0],
93 'DW_CFA_advance_loc', [1])
94 self.assertInstruction(entries[1].instructions[1],
95 'DW_CFA_def_cfa_offset', [12])
96 self.assertInstruction(entries[1].instructions[9],
97 'DW_CFA_offset', [4, 3])
98 self.assertInstruction(entries[1].instructions[18],
99 'DW_CFA_def_cfa_offset', [0])
100 self.assertInstruction(entries[1].instructions[20],
101 'DW_CFA_nop', [])
102
103 # Now let's decode it...
104 decoded_CIE = entries[0].get_decoded()
105 self.assertEqual(decoded_CIE.reg_order, list(range(9)))
106 self.assertEqual(len(decoded_CIE.table), 1)
107 self.assertEqual(decoded_CIE.table[0]['cfa'].reg, 7)
108 self.assertEqual(decoded_CIE.table[0]['pc'], 0)
109 self.assertEqual(decoded_CIE.table[0]['cfa'].offset, 0)
110 self.assertEqual(decoded_CIE.table[0][4].type, RegisterRule.SAME_VALUE)
111 self.assertEqual(decoded_CIE.table[0][8].type, RegisterRule.REGISTER)
112 self.assertEqual(decoded_CIE.table[0][8].arg, 1)
113
114 decoded_FDE = entries[1].get_decoded()
115 self.assertEqual(decoded_FDE.reg_order, list(range(9)))
116 self.assertEqual(decoded_FDE.table[0]['cfa'].reg, 7)
117 self.assertEqual(decoded_FDE.table[0]['cfa'].offset, 0)
118 self.assertEqual(decoded_FDE.table[0]['pc'], 0x11223344)
119 self.assertEqual(decoded_FDE.table[0][8].type, RegisterRule.REGISTER)
120 self.assertEqual(decoded_FDE.table[0][8].arg, 1)
121 self.assertEqual(decoded_FDE.table[1]['cfa'].reg, 7)
122 self.assertEqual(decoded_FDE.table[1]['cfa'].offset, 12)
123 self.assertEqual(decoded_FDE.table[2][8].type, RegisterRule.OFFSET)
124 self.assertEqual(decoded_FDE.table[2][8].arg, -4)
125 self.assertEqual(decoded_FDE.table[2][4].type, RegisterRule.SAME_VALUE)
126 self.assertEqual(decoded_FDE.table[5]['pc'], 0x11223344 + 20)
127 self.assertEqual(decoded_FDE.table[5][4].type, RegisterRule.OFFSET)
128 self.assertEqual(decoded_FDE.table[5][4].arg, -12)
129 self.assertEqual(decoded_FDE.table[6]['pc'], 0x11223344 + 64)
130 self.assertEqual(decoded_FDE.table[9]['pc'], 0x11223344 + 76)
131
132 def test_describe_CFI_instructions(self):
133 # The data here represents a single CIE
134 data = (b'' +
135 b'\x16\x00\x00\x00' + # length
136 b'\xff\xff\xff\xff' + # CIE_id
137 b'\x03\x00\x04\x7c' + # version, augmentation, caf, daf
138 b'\x08' + # return address
139 b'\x0c\x07\x02' +
140 b'\x10\x02\x07\x03\x01\x02\x00\x00\x06\x06')
141 s = BytesIO(data)
142
143 structs = DWARFStructs(little_endian=True, dwarf_format=32, address_size=4)
144 cfi = CallFrameInfo(s, len(data), 0, structs)
145 entries = cfi.get_entries()
146
147 set_global_machine_arch('x86')
148 self.assertEqual(describe_CFI_instructions(entries[0]),
149 ( ' DW_CFA_def_cfa: r7 (edi) ofs 2\n' +
150 ' DW_CFA_expression: r2 (edx) (DW_OP_addr: 201; DW_OP_deref; DW_OP_deref)\n'))
151
152 def test_CFIEntry_get_decoded(self):
153 oracle_decoded = DecodedCallFrameTable(
154 table = [
155 {'pc': 0, 'cfa': CFARule(reg = 29, offset = 0, expr = None)}
156 ],
157 reg_order = []
158 )
159
160 test_dir = join('test', 'testfiles_for_unittests')
161 with open(join(test_dir, 'simple_mipsel.elf'), 'rb') as f:
162 elf = ELFFile(f)
163 di = elf.get_dwarf_info()
164 entries = di.CFI_entries()
165 decoded = entries[0].get_decoded()
166 self.assertEqual(oracle_decoded.table[0]['cfa'].reg,
167 decoded.table[0]['cfa'].reg
168 )
169 self.assertEqual(oracle_decoded.table[0]['cfa'].offset,
170 decoded.table[0]['cfa'].offset)
171
172 def test_ehframe_fde_with_lsda_pointer(self):
173 # CIE and FDE dumped from exceptions_0, offset 0xcc0
174 # binary is at https://github.com/angr/binaries/blob/master/tests/x86_64/exceptions_0
175 data = (b'' +
176 # CIE
177 b'\x1c\x00\x00\x00' + # length
178 b'\x00\x00\x00\x00' + # ID
179 b'\x01' + # version
180 b'\x7a\x50\x4c\x52\x00' + # augmentation string
181 b'\x01' + # code alignment
182 b'\x78' + # data alignment
183 b'\x10' + # return address register
184 b'\x07' + # augmentation data length
185 b'\x9b' + # personality function pointer encoding
186 b'\x3d\x13\x20\x00' + # personality function pointer
187 b'\x1b' + # LSDA pointer encoding
188 b'\x1b' + # FDE encoding
189 b'\x0c\x07\x08\x90' + # initial instructions
190 b'\x01\x00\x00' +
191 # FDE
192 b'\x24\x00\x00\x00' + # length
193 b'\x24\x00\x00\x00' + # CIE reference pointer
194 b'\x62\xfd\xff\xff' + # pc begin
195 b'\x89\x00\x00\x00' + # pc range
196 b'\x04' + # augmentation data length
197 b'\xb7\x00\x00\x00' + # LSDA pointer
198 b'\x41\x0e\x10\x86' + # initial instructions
199 b'\x02\x43\x0d\x06' +
200 b'\x45\x83\x03\x02' +
201 b'\x7f\x0c\x07\x08' +
202 b'\x00\x00\x00'
203 )
204 s = BytesIO(data)
205
206 structs = DWARFStructs(little_endian=True, dwarf_format=32, address_size=8)
207 cfi = CallFrameInfo(s, len(data), 0, structs, for_eh_frame=True)
208 entries = cfi.get_entries()
209
210 self.assertEqual(len(entries), 2)
211 self.assertIsInstance(entries[0], CIE)
212 self.assertIn('LSDA_encoding', entries[0].augmentation_dict)
213 # check LSDA encoding
214 lsda_encoding = entries[0].augmentation_dict['LSDA_encoding']
215 basic_encoding = lsda_encoding & 0x0f
216 modifier = lsda_encoding & 0xf0
217 self.assertEqual(basic_encoding, DW_EH_encoding_flags['DW_EH_PE_sdata4'])
218 self.assertEqual(modifier, DW_EH_encoding_flags['DW_EH_PE_pcrel'])
219 self.assertIsInstance(entries[1], FDE)
220 self.assertEqual(entries[1].lsda_pointer, 232)
221
222 if __name__ == '__main__':
223 unittest.main()