1 /* libunwind - a platform-independent unwind library
2 Copyright 2011 Linaro Limited
4 This file is part of libunwind.
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
25 /* This file contains functionality for parsing and interpreting the ARM
26 specific unwind information. Documentation about the exception handling
27 ABI for the ARM architecture can be found at:
28 http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
31 #include "libunwind_i.h"
33 #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
34 #define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
35 #define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
37 #define ARM_EXIDX_CANT_UNWIND 0x00000001
38 #define ARM_EXIDX_COMPACT 0x80000000
40 #define ARM_EXTBL_OP_FINISH 0xb0
42 enum arm_exbuf_cmd_flags
{
43 ARM_EXIDX_VFP_SHIFT_16
= 1 << 16,
44 ARM_EXIDX_VFP_DOUBLE
= 1 << 17,
50 unw_word_t ip
; /* instruction-pointer we're looking for */
51 unw_proc_info_t
*pi
; /* proc-info pointer */
53 unw_dyn_info_t di
; /* info about the ARM exidx segment */
56 static inline uint32_t CONST_ATTR
57 prel31_read (uint32_t prel31
)
59 return ((int32_t)prel31
<< 1) >> 1;
63 prel31_to_addr (unw_addr_space_t as
, void *arg
, unw_word_t prel31
,
68 if ((*as
->acc
.access_mem
)(as
, prel31
, &offset
, 0, arg
) < 0)
71 offset
= ((long)offset
<< 1) >> 1;
72 *val
= prel31
+ offset
;
78 * Applies the given command onto the new state to the given dwarf_cursor.
81 arm_exidx_apply_cmd (struct arm_exbuf_data
*edata
, struct dwarf_cursor
*c
)
88 case ARM_EXIDX_CMD_FINISH
:
89 /* Set LR to PC if not set already. */
90 if (DWARF_IS_NULL_LOC (c
->loc
[UNW_ARM_R15
]))
91 c
->loc
[UNW_ARM_R15
] = c
->loc
[UNW_ARM_R14
];
93 dwarf_get (c
, c
->loc
[UNW_ARM_R15
], &c
->ip
);
95 case ARM_EXIDX_CMD_DATA_PUSH
:
96 Debug (2, "vsp = vsp - %d\n", edata
->data
);
97 c
->cfa
-= edata
->data
;
99 case ARM_EXIDX_CMD_DATA_POP
:
100 Debug (2, "vsp = vsp + %d\n", edata
->data
);
101 c
->cfa
+= edata
->data
;
103 case ARM_EXIDX_CMD_REG_POP
:
104 for (i
= 0; i
< 16; i
++)
105 if (edata
->data
& (1 << i
))
107 Debug (2, "pop {r%d}\n", i
);
108 c
->loc
[UNW_ARM_R0
+ i
] = DWARF_LOC (c
->cfa
, 0);
111 /* Set cfa in case the SP got popped. */
112 if (edata
->data
& (1 << 13))
113 dwarf_get (c
, c
->loc
[UNW_ARM_R13
], &c
->cfa
);
115 case ARM_EXIDX_CMD_REG_TO_SP
:
116 assert (edata
->data
< 16);
117 Debug (2, "vsp = r%d\n", edata
->data
);
118 c
->loc
[UNW_ARM_R13
] = c
->loc
[UNW_ARM_R0
+ edata
->data
];
119 dwarf_get (c
, c
->loc
[UNW_ARM_R13
], &c
->cfa
);
121 case ARM_EXIDX_CMD_VFP_POP
:
122 /* Skip VFP registers, but be sure to adjust stack */
123 for (i
= ARM_EXBUF_START (edata
->data
); i
<= ARM_EXBUF_END (edata
->data
);
126 if (!(edata
->data
& ARM_EXIDX_VFP_DOUBLE
))
129 case ARM_EXIDX_CMD_WREG_POP
:
130 for (i
= ARM_EXBUF_START (edata
->data
); i
<= ARM_EXBUF_END (edata
->data
);
134 case ARM_EXIDX_CMD_WCGR_POP
:
135 for (i
= 0; i
< 4; i
++)
136 if (edata
->data
& (1 << i
))
139 case ARM_EXIDX_CMD_REFUSED
:
140 case ARM_EXIDX_CMD_RESERVED
:
148 * Decodes the given unwind instructions into arm_exbuf_data and calls
149 * arm_exidx_apply_cmd that applies the command onto the dwarf_cursor.
152 arm_exidx_decode (const uint8_t *buf
, uint8_t len
, struct dwarf_cursor
*c
)
154 #define READ_OP() *buf++
155 const uint8_t *end
= buf
+ len
;
157 struct arm_exbuf_data edata
;
164 uint8_t op
= READ_OP ();
165 if ((op
& 0xc0) == 0x00)
167 edata
.cmd
= ARM_EXIDX_CMD_DATA_POP
;
168 edata
.data
= (((int)op
& 0x3f) << 2) + 4;
170 else if ((op
& 0xc0) == 0x40)
172 edata
.cmd
= ARM_EXIDX_CMD_DATA_PUSH
;
173 edata
.data
= (((int)op
& 0x3f) << 2) + 4;
175 else if ((op
& 0xf0) == 0x80)
177 uint8_t op2
= READ_OP ();
178 if (op
== 0x80 && op2
== 0x00)
179 edata
.cmd
= ARM_EXIDX_CMD_REFUSED
;
182 edata
.cmd
= ARM_EXIDX_CMD_REG_POP
;
183 edata
.data
= ((op
& 0xf) << 8) | op2
;
184 edata
.data
= edata
.data
<< 4;
187 else if ((op
& 0xf0) == 0x90)
189 if (op
== 0x9d || op
== 0x9f)
190 edata
.cmd
= ARM_EXIDX_CMD_RESERVED
;
193 edata
.cmd
= ARM_EXIDX_CMD_REG_TO_SP
;
194 edata
.data
= op
& 0x0f;
197 else if ((op
& 0xf0) == 0xa0)
199 unsigned end
= (op
& 0x07);
200 edata
.data
= (1 << (end
+ 1)) - 1;
201 edata
.data
= edata
.data
<< 4;
203 edata
.data
|= 1 << 14;
204 edata
.cmd
= ARM_EXIDX_CMD_REG_POP
;
206 else if (op
== ARM_EXTBL_OP_FINISH
)
208 edata
.cmd
= ARM_EXIDX_CMD_FINISH
;
213 uint8_t op2
= READ_OP ();
214 if (op2
== 0 || (op2
& 0xf0))
215 edata
.cmd
= ARM_EXIDX_CMD_RESERVED
;
218 edata
.cmd
= ARM_EXIDX_CMD_REG_POP
;
219 edata
.data
= op2
& 0x0f;
225 uint8_t byte
, shift
= 0;
229 offset
|= (byte
& 0x7f) << shift
;
233 edata
.data
= offset
* 4 + 0x204;
234 edata
.cmd
= ARM_EXIDX_CMD_DATA_POP
;
236 else if (op
== 0xb3 || op
== 0xc8 || op
== 0xc9)
238 edata
.cmd
= ARM_EXIDX_CMD_VFP_POP
;
239 edata
.data
= READ_OP ();
241 edata
.data
|= ARM_EXIDX_VFP_SHIFT_16
;
243 edata
.data
|= ARM_EXIDX_VFP_DOUBLE
;
245 else if ((op
& 0xf8) == 0xb8 || (op
& 0xf8) == 0xd0)
247 edata
.cmd
= ARM_EXIDX_CMD_VFP_POP
;
248 edata
.data
= 0x80 | (op
& 0x07);
249 if ((op
& 0xf8) == 0xd0)
250 edata
.data
|= ARM_EXIDX_VFP_DOUBLE
;
252 else if (op
>= 0xc0 && op
<= 0xc5)
254 edata
.cmd
= ARM_EXIDX_CMD_WREG_POP
;
255 edata
.data
= 0xa0 | (op
& 0x07);
259 edata
.cmd
= ARM_EXIDX_CMD_WREG_POP
;
260 edata
.data
= READ_OP ();
264 uint8_t op2
= READ_OP ();
265 if (op2
== 0 || (op2
& 0xf0))
266 edata
.cmd
= ARM_EXIDX_CMD_RESERVED
;
269 edata
.cmd
= ARM_EXIDX_CMD_WCGR_POP
;
270 edata
.data
= op2
& 0x0f;
274 edata
.cmd
= ARM_EXIDX_CMD_RESERVED
;
276 ret
= arm_exidx_apply_cmd (&edata
, c
);
284 * Reads the entry from the given cursor and extracts the unwind instructions
285 * into buf. Returns the number of the extracted unwind insns or
286 * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was
290 arm_exidx_extract (struct dwarf_cursor
*c
, uint8_t *buf
)
293 unw_word_t entry
= (unw_word_t
) c
->pi
.unwind_info
;
297 /* An ARM unwind entry consists of a prel31 offset to the start of a
298 function followed by 31bits of data:
299 * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
300 * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT)
301 * if bit 31 is zero: this is a prel31 offset of the start of the
302 table entry for this function */
303 if (prel31_to_addr(c
->as
, c
->as_arg
, entry
, &addr
) < 0)
306 if ((*c
->as
->acc
.access_mem
)(c
->as
, entry
+ 4, &data
, 0, c
->as_arg
) < 0)
309 if (data
== ARM_EXIDX_CANT_UNWIND
)
311 Debug (2, "0x1 [can't unwind]\n");
312 nbuf
= -UNW_ESTOPUNWIND
;
314 else if (data
& ARM_EXIDX_COMPACT
)
316 Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr
,
317 (data
>> 24) & 0x7f, data
);
318 buf
[nbuf
++] = data
>> 16;
319 buf
[nbuf
++] = data
>> 8;
324 unw_word_t extbl_data
;
325 unsigned int n_table_words
= 0;
327 if (prel31_to_addr(c
->as
, c
->as_arg
, entry
+ 4, &extbl_data
) < 0)
330 if ((*c
->as
->acc
.access_mem
)(c
->as
, extbl_data
, &data
, 0, c
->as_arg
) < 0)
333 if (data
& ARM_EXIDX_COMPACT
)
335 int pers
= (data
>> 24) & 0x0f;
336 Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr
, pers
, data
);
337 if (pers
== 1 || pers
== 2)
339 n_table_words
= (data
>> 16) & 0xff;
343 buf
[nbuf
++] = data
>> 16;
344 buf
[nbuf
++] = data
>> 8;
350 if (prel31_to_addr (c
->as
, c
->as_arg
, extbl_data
, &pers
) < 0)
352 Debug (2, "%p Personality routine: %8p\n", (void *)addr
,
354 if ((*c
->as
->acc
.access_mem
)(c
->as
, extbl_data
+ 4, &data
, 0,
357 n_table_words
= data
>> 24;
358 buf
[nbuf
++] = data
>> 16;
359 buf
[nbuf
++] = data
>> 8;
363 assert (n_table_words
<= 5);
365 for (j
= 0; j
< n_table_words
; j
++)
367 if ((*c
->as
->acc
.access_mem
)(c
->as
, extbl_data
, &data
, 0,
371 buf
[nbuf
++] = data
>> 24;
372 buf
[nbuf
++] = data
>> 16;
373 buf
[nbuf
++] = data
>> 8;
374 buf
[nbuf
++] = data
>> 0;
378 if (nbuf
> 0 && buf
[nbuf
- 1] != ARM_EXTBL_OP_FINISH
)
379 buf
[nbuf
++] = ARM_EXTBL_OP_FINISH
;
385 tdep_search_unwind_table (unw_addr_space_t as
, unw_word_t ip
,
386 unw_dyn_info_t
*di
, unw_proc_info_t
*pi
,
387 int need_unwind_info
, void *arg
)
389 if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX
)
390 && di
->format
== UNW_INFO_FORMAT_ARM_EXIDX
)
392 /* The .ARM.exidx section contains a sorted list of key-value pairs -
393 the unwind entries. The 'key' is a prel31 offset to the start of a
394 function. We binary search this section in order to find the
395 appropriate unwind entry. */
396 unw_word_t first
= di
->u
.rti
.table_data
;
397 unw_word_t last
= di
->u
.rti
.table_data
+ di
->u
.rti
.table_len
- 8;
398 unw_word_t entry
, val
;
400 if (prel31_to_addr (as
, arg
, first
, &val
) < 0 || ip
< val
)
403 if (prel31_to_addr (as
, arg
, last
, &val
) < 0)
410 if (prel31_to_addr (as
, arg
, last
, &pi
->start_ip
) < 0)
413 pi
->end_ip
= di
->end_ip
-1;
417 while (first
< last
- 8)
419 entry
= first
+ (((last
- first
) / 8 + 1) >> 1) * 8;
421 if (prel31_to_addr (as
, arg
, entry
, &val
) < 0)
432 if (prel31_to_addr (as
, arg
, entry
, &pi
->start_ip
) < 0)
435 if (prel31_to_addr (as
, arg
, entry
+ 8, &pi
->end_ip
) < 0)
441 if (need_unwind_info
)
443 pi
->unwind_info_size
= 8;
444 pi
->unwind_info
= (void *) entry
;
445 pi
->format
= UNW_INFO_FORMAT_ARM_EXIDX
;
449 else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF
)
450 && di
->format
!= UNW_INFO_FORMAT_ARM_EXIDX
)
451 return dwarf_search_unwind_table (as
, ip
, di
, pi
, need_unwind_info
, arg
);
456 #ifndef UNW_REMOTE_ONLY
458 * Callback to dl_iterate_phdr to find infos about the ARM exidx segment.
461 arm_phdr_cb (struct dl_phdr_info
*info
, size_t size
, void *data
)
463 struct arm_cb_data
*cb_data
= data
;
464 const Elf_W(Phdr
) *p_text
= NULL
;
465 const Elf_W(Phdr
) *p_arm_exidx
= NULL
;
466 const Elf_W(Phdr
) *phdr
= info
->dlpi_phdr
;
469 for (n
= info
->dlpi_phnum
; --n
>= 0; phdr
++)
471 switch (phdr
->p_type
)
474 if (cb_data
->ip
>= phdr
->p_vaddr
+ info
->dlpi_addr
&&
475 cb_data
->ip
< phdr
->p_vaddr
+ info
->dlpi_addr
+ phdr
->p_memsz
)
488 if (p_text
&& p_arm_exidx
)
490 cb_data
->di
.format
= UNW_INFO_FORMAT_ARM_EXIDX
;
491 cb_data
->di
.start_ip
= p_text
->p_vaddr
+ info
->dlpi_addr
;
492 cb_data
->di
.end_ip
= cb_data
->di
.start_ip
+ p_text
->p_memsz
;
493 cb_data
->di
.u
.rti
.name_ptr
= (unw_word_t
) info
->dlpi_name
;
494 cb_data
->di
.u
.rti
.table_data
= p_arm_exidx
->p_vaddr
+ info
->dlpi_addr
;
495 cb_data
->di
.u
.rti
.table_len
= p_arm_exidx
->p_memsz
;
503 arm_find_proc_info (unw_addr_space_t as
, unw_word_t ip
,
504 unw_proc_info_t
*pi
, int need_unwind_info
, void *arg
)
507 intrmask_t saved_mask
;
509 Debug (14, "looking for IP=0x%lx\n", (long) ip
);
511 if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF
))
513 struct dwarf_callback_data cb_data
;
515 memset (&cb_data
, 0, sizeof (cb_data
));
518 cb_data
.need_unwind_info
= need_unwind_info
;
519 cb_data
.di
.format
= -1;
520 cb_data
.di_debug
.format
= -1;
522 SIGPROCMASK (SIG_SETMASK
, &unwi_full_mask
, &saved_mask
);
523 ret
= dl_iterate_phdr (dwarf_callback
, &cb_data
);
524 SIGPROCMASK (SIG_SETMASK
, &saved_mask
, NULL
);
526 if (cb_data
.single_fde
)
527 /* already got the result in *pi */
530 if (cb_data
.di_debug
.format
!= -1)
531 ret
= tdep_search_unwind_table (as
, ip
, &cb_data
.di_debug
, pi
,
532 need_unwind_info
, arg
);
537 if (ret
< 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX
))
539 struct arm_cb_data cb_data
;
541 memset (&cb_data
, 0, sizeof (cb_data
));
544 cb_data
.di
.format
= -1;
546 SIGPROCMASK (SIG_SETMASK
, &unwi_full_mask
, &saved_mask
);
547 ret
= dl_iterate_phdr (arm_phdr_cb
, &cb_data
);
548 SIGPROCMASK (SIG_SETMASK
, &saved_mask
, NULL
);
550 if (cb_data
.di
.format
!= -1)
551 ret
= tdep_search_unwind_table (as
, ip
, &cb_data
.di
, pi
,
552 need_unwind_info
, arg
);
558 Debug (14, "IP=0x%lx not found\n", (long) ip
);
564 arm_put_unwind_info (unw_addr_space_t as
, unw_proc_info_t
*proc_info
, void *arg
)
568 #endif /* !UNW_REMOTE_ONLY */