]>
Commit | Line | Data |
---|---|---|
2a75c0b6 | 1 | /* ARM EABI compliant unwinding routines |
748086b7 | 2 | Copyright (C) 2004, 2005, 2009 Free Software Foundation, Inc. |
2a75c0b6 PB |
3 | Contributed by Paul Brook |
4 | ||
5 | This file is free software; you can redistribute it and/or modify it | |
6 | under the terms of the GNU General Public License as published by the | |
748086b7 | 7 | Free Software Foundation; either version 3, or (at your option) any |
2a75c0b6 PB |
8 | later version. |
9 | ||
2a75c0b6 PB |
10 | This file is distributed in the hope that it will be useful, but |
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | General Public License for more details. | |
14 | ||
748086b7 JJ |
15 | Under Section 7 of GPL version 3, you are granted additional |
16 | permissions described in the GCC Runtime Library Exception, version | |
17 | 3.1, as published by the Free Software Foundation. | |
18 | ||
19 | You should have received a copy of the GNU General Public License and | |
20 | a copy of the GCC Runtime Library Exception along with this program; | |
21 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
22 | <http://www.gnu.org/licenses/>. */ | |
23 | ||
2a75c0b6 PB |
24 | #include "unwind.h" |
25 | ||
74d9c39f DJ |
26 | /* We add a prototype for abort here to avoid creating a dependency on |
27 | target headers. */ | |
28 | extern void abort (void); | |
29 | ||
2a75c0b6 PB |
30 | typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ |
31 | ||
32 | /* Misc constants. */ | |
33 | #define R_IP 12 | |
34 | #define R_SP 13 | |
35 | #define R_LR 14 | |
36 | #define R_PC 15 | |
37 | ||
38 | #define uint32_highbit (((_uw) 1) << 31) | |
39 | ||
40 | void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); | |
41 | ||
42 | /* Unwind descriptors. */ | |
43 | ||
44 | typedef struct | |
45 | { | |
46 | _uw16 length; | |
47 | _uw16 offset; | |
48 | } EHT16; | |
49 | ||
50 | typedef struct | |
51 | { | |
52 | _uw length; | |
53 | _uw offset; | |
54 | } EHT32; | |
55 | ||
56 | /* Calculate the address encoded by a 31-bit self-relative offset at address | |
57 | P. Copy of routine in unwind-arm.c. */ | |
58 | ||
59 | static inline _uw | |
60 | selfrel_offset31 (const _uw *p) | |
61 | { | |
62 | _uw offset; | |
63 | ||
64 | offset = *p; | |
65 | /* Sign extend to 32 bits. */ | |
66 | if (offset & (1 << 30)) | |
67 | offset |= 1u << 31; | |
68 | ||
69 | return offset + (_uw) p; | |
70 | } | |
71 | ||
72 | ||
73 | /* Personality routine helper functions. */ | |
74 | ||
75 | #define CODE_FINISH (0xb0) | |
76 | ||
77 | /* Return the next byte of unwinding information, or CODE_FINISH if there is | |
78 | no data remaining. */ | |
79 | static inline _uw8 | |
80 | next_unwind_byte (__gnu_unwind_state * uws) | |
81 | { | |
82 | _uw8 b; | |
83 | ||
84 | if (uws->bytes_left == 0) | |
85 | { | |
86 | /* Load another word */ | |
87 | if (uws->words_left == 0) | |
88 | return CODE_FINISH; /* Nothing left. */ | |
89 | uws->words_left--; | |
90 | uws->data = *(uws->next++); | |
91 | uws->bytes_left = 3; | |
92 | } | |
93 | else | |
94 | uws->bytes_left--; | |
95 | ||
96 | /* Extract the most significant byte. */ | |
97 | b = (uws->data >> 24) & 0xff; | |
98 | uws->data <<= 8; | |
99 | return b; | |
100 | } | |
101 | ||
102 | /* Execute the unwinding instructions described by UWS. */ | |
103 | _Unwind_Reason_Code | |
104 | __gnu_unwind_execute (_Unwind_Context * context, __gnu_unwind_state * uws) | |
105 | { | |
106 | _uw op; | |
107 | int set_pc; | |
108 | _uw reg; | |
109 | ||
110 | set_pc = 0; | |
111 | for (;;) | |
112 | { | |
113 | op = next_unwind_byte (uws); | |
114 | if (op == CODE_FINISH) | |
115 | { | |
116 | /* If we haven't already set pc then copy it from lr. */ | |
117 | if (!set_pc) | |
118 | { | |
119 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, | |
120 | ®); | |
121 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, | |
122 | ®); | |
123 | set_pc = 1; | |
124 | } | |
125 | /* Drop out of the loop. */ | |
126 | break; | |
127 | } | |
128 | if ((op & 0x80) == 0) | |
129 | { | |
130 | /* vsp = vsp +- (imm6 << 2 + 4). */ | |
131 | _uw offset; | |
132 | ||
133 | offset = ((op & 0x3f) << 2) + 4; | |
134 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
135 | if (op & 0x40) | |
136 | reg -= offset; | |
137 | else | |
138 | reg += offset; | |
139 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
140 | continue; | |
141 | } | |
142 | ||
143 | if ((op & 0xf0) == 0x80) | |
144 | { | |
145 | op = (op << 8) | next_unwind_byte (uws); | |
146 | if (op == 0x8000) | |
147 | { | |
148 | /* Refuse to unwind. */ | |
149 | return _URC_FAILURE; | |
150 | } | |
151 | /* Pop r4-r15 under mask. */ | |
152 | op = (op << 4) & 0xfff0; | |
153 | if (_Unwind_VRS_Pop (context, _UVRSC_CORE, op, _UVRSD_UINT32) | |
154 | != _UVRSR_OK) | |
155 | return _URC_FAILURE; | |
156 | if (op & (1 << R_PC)) | |
157 | set_pc = 1; | |
158 | continue; | |
159 | } | |
160 | if ((op & 0xf0) == 0x90) | |
161 | { | |
162 | op &= 0xf; | |
163 | if (op == 13 || op == 15) | |
164 | /* Reserved. */ | |
165 | return _URC_FAILURE; | |
166 | /* vsp = r[nnnn]. */ | |
167 | _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®); | |
168 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
169 | continue; | |
170 | } | |
171 | if ((op & 0xf0) == 0xa0) | |
172 | { | |
173 | /* Pop r4-r[4+nnn], [lr]. */ | |
174 | _uw mask; | |
175 | ||
176 | mask = (0xff0 >> (7 - (op & 7))) & 0xff0; | |
177 | if (op & 8) | |
178 | mask |= (1 << R_LR); | |
179 | if (_Unwind_VRS_Pop (context, _UVRSC_CORE, mask, _UVRSD_UINT32) | |
180 | != _UVRSR_OK) | |
181 | return _URC_FAILURE; | |
182 | continue; | |
183 | } | |
184 | if ((op & 0xf0) == 0xb0) | |
185 | { | |
186 | /* op == 0xb0 already handled. */ | |
187 | if (op == 0xb1) | |
188 | { | |
189 | op = next_unwind_byte (uws); | |
190 | if (op == 0 || ((op & 0xf0) != 0)) | |
191 | /* Spare. */ | |
192 | return _URC_FAILURE; | |
193 | /* Pop r0-r4 under mask. */ | |
194 | if (_Unwind_VRS_Pop (context, _UVRSC_CORE, op, _UVRSD_UINT32) | |
195 | != _UVRSR_OK) | |
196 | return _URC_FAILURE; | |
197 | continue; | |
198 | } | |
199 | if (op == 0xb2) | |
200 | { | |
201 | /* vsp = vsp + 0x204 + (uleb128 << 2). */ | |
202 | int shift; | |
203 | ||
204 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
205 | ®); | |
206 | op = next_unwind_byte (uws); | |
207 | shift = 2; | |
208 | while (op & 0x80) | |
209 | { | |
210 | reg += ((op & 0x7f) << shift); | |
211 | shift += 7; | |
212 | op = next_unwind_byte (uws); | |
213 | } | |
214 | reg += ((op & 0x7f) << shift) + 0x204; | |
215 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
216 | ®); | |
217 | continue; | |
218 | } | |
219 | if (op == 0xb3) | |
220 | { | |
221 | /* Pop VFP registers with fldmx. */ | |
222 | op = next_unwind_byte (uws); | |
08a557f6 | 223 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
2a75c0b6 PB |
224 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_VFPX) |
225 | != _UVRSR_OK) | |
226 | return _URC_FAILURE; | |
227 | continue; | |
228 | } | |
229 | if ((op & 0xfc) == 0xb4) | |
230 | { | |
231 | /* Pop FPA E[4]-E[4+nn]. */ | |
232 | op = 0x40000 | ((op & 3) + 1); | |
233 | if (_Unwind_VRS_Pop (context, _UVRSC_FPA, op, _UVRSD_FPAX) | |
234 | != _UVRSR_OK) | |
235 | return _URC_FAILURE; | |
236 | continue; | |
237 | } | |
238 | /* op & 0xf8 == 0xb8. */ | |
239 | /* Pop VFP D[8]-D[8+nnn] with fldmx. */ | |
240 | op = 0x80000 | ((op & 7) + 1); | |
241 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_VFPX) | |
242 | != _UVRSR_OK) | |
243 | return _URC_FAILURE; | |
244 | continue; | |
245 | } | |
246 | if ((op & 0xf0) == 0xc0) | |
247 | { | |
248 | if (op == 0xc6) | |
249 | { | |
250 | /* Pop iWMMXt D registers. */ | |
251 | op = next_unwind_byte (uws); | |
08a557f6 | 252 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
2a75c0b6 PB |
253 | if (_Unwind_VRS_Pop (context, _UVRSC_WMMXD, op, _UVRSD_UINT64) |
254 | != _UVRSR_OK) | |
255 | return _URC_FAILURE; | |
256 | continue; | |
257 | } | |
258 | if (op == 0xc7) | |
259 | { | |
260 | op = next_unwind_byte (uws); | |
261 | if (op == 0 || (op & 0xf0) != 0) | |
262 | /* Spare. */ | |
263 | return _URC_FAILURE; | |
264 | /* Pop iWMMXt wCGR{3,2,1,0} under mask. */ | |
265 | if (_Unwind_VRS_Pop (context, _UVRSC_WMMXC, op, _UVRSD_UINT32) | |
266 | != _UVRSR_OK) | |
267 | return _URC_FAILURE; | |
268 | continue; | |
269 | } | |
270 | if ((op & 0xf8) == 0xc0) | |
271 | { | |
272 | /* Pop iWMMXt wR[10]-wR[10+nnn]. */ | |
273 | op = 0xa0000 | ((op & 0xf) + 1); | |
274 | if (_Unwind_VRS_Pop (context, _UVRSC_WMMXD, op, _UVRSD_UINT64) | |
275 | != _UVRSR_OK) | |
276 | return _URC_FAILURE; | |
277 | continue; | |
278 | } | |
279 | if (op == 0xc8) | |
280 | { | |
8edfc4cc MS |
281 | #ifndef __VFP_FP__ |
282 | /* Pop FPA registers. */ | |
283 | op = next_unwind_byte (uws); | |
08a557f6 | 284 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
8edfc4cc MS |
285 | if (_Unwind_VRS_Pop (context, _UVRSC_FPA, op, _UVRSD_FPAX) |
286 | != _UVRSR_OK) | |
287 | return _URC_FAILURE; | |
288 | continue; | |
289 | #else | |
290 | /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */ | |
291 | op = next_unwind_byte (uws); | |
292 | op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1); | |
293 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE) | |
294 | != _UVRSR_OK) | |
295 | return _URC_FAILURE; | |
296 | continue; | |
297 | #endif | |
2a75c0b6 PB |
298 | } |
299 | if (op == 0xc9) | |
300 | { | |
301 | /* Pop VFP registers with fldmd. */ | |
302 | op = next_unwind_byte (uws); | |
08a557f6 | 303 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
2a75c0b6 PB |
304 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE) |
305 | != _UVRSR_OK) | |
306 | return _URC_FAILURE; | |
307 | continue; | |
308 | } | |
309 | /* Spare. */ | |
310 | return _URC_FAILURE; | |
311 | } | |
312 | if ((op & 0xf8) == 0xd0) | |
313 | { | |
314 | /* Pop VFP D[8]-D[8+nnn] with fldmd. */ | |
315 | op = 0x80000 | ((op & 7) + 1); | |
316 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE) | |
317 | != _UVRSR_OK) | |
318 | return _URC_FAILURE; | |
319 | continue; | |
320 | } | |
321 | /* Spare. */ | |
322 | return _URC_FAILURE; | |
323 | } | |
324 | return _URC_OK; | |
325 | } | |
326 | ||
327 | ||
328 | /* Execute the unwinding instructions associated with a frame. UCBP and | |
329 | CONTEXT are the current exception object and virtual CPU state | |
330 | respectively. */ | |
331 | ||
332 | _Unwind_Reason_Code | |
333 | __gnu_unwind_frame (_Unwind_Control_Block * ucbp, _Unwind_Context * context) | |
334 | { | |
335 | _uw *ptr; | |
336 | __gnu_unwind_state uws; | |
337 | ||
338 | ptr = (_uw *) ucbp->pr_cache.ehtp; | |
339 | /* Skip over the personality routine address. */ | |
340 | ptr++; | |
341 | /* Setup the unwinder state. */ | |
342 | uws.data = (*ptr) << 8; | |
343 | uws.next = ptr + 1; | |
344 | uws.bytes_left = 3; | |
345 | uws.words_left = ((*ptr) >> 24) & 0xff; | |
346 | ||
347 | return __gnu_unwind_execute (context, &uws); | |
348 | } | |
349 | ||
350 | /* Get the _Unwind_Control_Block from an _Unwind_Context. */ | |
351 | ||
352 | static inline _Unwind_Control_Block * | |
353 | unwind_UCB_from_context (_Unwind_Context * context) | |
354 | { | |
355 | return (_Unwind_Control_Block *) _Unwind_GetGR (context, R_IP); | |
356 | } | |
357 | ||
358 | /* Get the start address of the function being unwound. */ | |
359 | ||
360 | _Unwind_Ptr | |
361 | _Unwind_GetRegionStart (_Unwind_Context * context) | |
362 | { | |
363 | _Unwind_Control_Block *ucbp; | |
364 | ||
365 | ucbp = unwind_UCB_from_context (context); | |
366 | return (_Unwind_Ptr) ucbp->pr_cache.fnstart; | |
367 | } | |
368 | ||
369 | /* Find the Language specific exception data. */ | |
370 | ||
371 | void * | |
372 | _Unwind_GetLanguageSpecificData (_Unwind_Context * context) | |
373 | { | |
374 | _Unwind_Control_Block *ucbp; | |
375 | _uw *ptr; | |
376 | ||
377 | /* Get a pointer to the exception table entry. */ | |
378 | ucbp = unwind_UCB_from_context (context); | |
379 | ptr = (_uw *) ucbp->pr_cache.ehtp; | |
380 | /* Skip the personality routine address. */ | |
381 | ptr++; | |
382 | /* Skip the unwind opcodes. */ | |
383 | ptr += (((*ptr) >> 24) & 0xff) + 1; | |
384 | ||
385 | return ptr; | |
386 | } | |
387 | ||
66403286 PB |
388 | |
389 | /* These two should never be used. */ | |
390 | ||
391 | _Unwind_Ptr | |
392 | _Unwind_GetDataRelBase (_Unwind_Context *context __attribute__ ((unused))) | |
393 | { | |
394 | abort (); | |
395 | } | |
396 | ||
397 | _Unwind_Ptr | |
398 | _Unwind_GetTextRelBase (_Unwind_Context *context __attribute__ ((unused))) | |
399 | { | |
400 | abort (); | |
401 | } |