1 #-------------------------------------------------------------------------------
4 # Eli Bendersky (eliben@gmail.com)
5 # This code is in the public domain
6 #-------------------------------------------------------------------------------
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
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
)
27 def test_spec_sample_d6(self
):
28 # D.6 sample in DWARFv3
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
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
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' +
61 b
'\x0d\x07' + b
'\x41' +
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()
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])
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],
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)
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)
132 def test_describe_CFI_instructions(self
):
133 # The data here represents a single CIE
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
140 b
'\x10\x02\x07\x03\x01\x02\x00\x00\x06\x06')
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()
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'))
152 def test_CFIEntry_get_decoded(self
):
153 oracle_decoded
= DecodedCallFrameTable(
155 {'pc': 0, 'cfa': CFARule(reg
= 29, offset
= 0, expr
= None)}
160 test_dir
= join('test', 'testfiles_for_unittests')
161 with
open(join(test_dir
, 'simple_mipsel.elf'), 'rb') as 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
169 self
.assertEqual(oracle_decoded
.table
[0]['cfa'].offset
,
170 decoded
.table
[0]['cfa'].offset
)
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
177 b
'\x1c\x00\x00\x00' + # length
178 b
'\x00\x00\x00\x00' + # ID
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
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' +
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()
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)
222 if __name__
== '__main__':