1 /* libunwind - a platform-independent unwind library
2 Copyright 2011 Linaro Limited
3
4 This file is part of libunwind.
5
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:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
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. */
24
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
29 */
30
31 #include "libunwind_i.h"
32
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))
36
37 #define ARM_EXIDX_CANT_UNWIND 0x00000001
38 #define ARM_EXIDX_COMPACT 0x80000000
39
40 #define ARM_EXTBL_OP_FINISH 0xb0
41
42 enum arm_exbuf_cmd_flags {
43 ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
44 ARM_EXIDX_VFP_DOUBLE = 1 << 17,
45 };
46
47 struct arm_cb_data
48 {
49 /* in: */
50 unw_word_t ip; /* instruction-pointer we're looking for */
51 unw_proc_info_t *pi; /* proc-info pointer */
52 /* out: */
53 unw_dyn_info_t di; /* info about the ARM exidx segment */
54 };
55
56 static inline int
prel31_to_addr(unw_addr_space_t as,void * arg,unw_word_t prel31,unw_word_t * val)57 prel31_to_addr (unw_addr_space_t as, void *arg, unw_word_t prel31,
58 unw_word_t *val)
59 {
60 unw_word_t offset;
61
62 if ((*as->acc.access_mem)(as, prel31, &offset, 0, arg) < 0)
63 return -UNW_EINVAL;
64
65 offset = ((long)offset << 1) >> 1;
66 *val = prel31 + offset;
67
68 return 0;
69 }
70
71 /**
72 * Applies the given command onto the new state to the given dwarf_cursor.
73 */
74 HIDDEN int
arm_exidx_apply_cmd(struct arm_exbuf_data * edata,struct dwarf_cursor * c)75 arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c)
76 {
77 int ret = 0;
78 unsigned i;
79
80 switch (edata->cmd)
81 {
82 case ARM_EXIDX_CMD_FINISH:
83 /* Set LR to PC if not set already. */
84 if (DWARF_IS_NULL_LOC (c->loc[UNW_ARM_R15]))
85 c->loc[UNW_ARM_R15] = c->loc[UNW_ARM_R14];
86 /* Set IP. */
87 dwarf_get (c, c->loc[UNW_ARM_R15], &c->ip);
88 break;
89 case ARM_EXIDX_CMD_DATA_PUSH:
90 Debug (2, "vsp = vsp - %d\n", edata->data);
91 c->cfa -= edata->data;
92 break;
93 case ARM_EXIDX_CMD_DATA_POP:
94 Debug (2, "vsp = vsp + %d\n", edata->data);
95 c->cfa += edata->data;
96 break;
97 case ARM_EXIDX_CMD_REG_POP:
98 for (i = 0; i < 16; i++)
99 if (edata->data & (1 << i))
100 {
101 Debug (2, "pop {r%d}\n", i);
102 c->loc[UNW_ARM_R0 + i] = DWARF_LOC (c->cfa, 0);
103 c->cfa += 4;
104 }
105 /* Set cfa in case the SP got popped. */
106 if (edata->data & (1 << 13))
107 dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
108 break;
109 case ARM_EXIDX_CMD_REG_TO_SP:
110 assert (edata->data < 16);
111 Debug (2, "vsp = r%d\n", edata->data);
112 c->loc[UNW_ARM_R13] = c->loc[UNW_ARM_R0 + edata->data];
113 dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
114 break;
115 case ARM_EXIDX_CMD_VFP_POP:
116 /* Skip VFP registers, but be sure to adjust stack */
117 for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data);
118 i++)
119 c->cfa += 8;
120 if (!(edata->data & ARM_EXIDX_VFP_DOUBLE))
121 c->cfa += 4;
122 break;
123 case ARM_EXIDX_CMD_WREG_POP:
124 for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data);
125 i++)
126 c->cfa += 8;
127 break;
128 case ARM_EXIDX_CMD_WCGR_POP:
129 for (i = 0; i < 4; i++)
130 if (edata->data & (1 << i))
131 c->cfa += 4;
132 break;
133 case ARM_EXIDX_CMD_REFUSED:
134 case ARM_EXIDX_CMD_RESERVED:
135 ret = -1;
136 break;
137 }
138 return ret;
139 }
140
141 /**
142 * Decodes the given unwind instructions into arm_exbuf_data and calls
143 * arm_exidx_apply_cmd that applies the command onto the dwarf_cursor.
144 */
145 HIDDEN int
arm_exidx_decode(const uint8_t * buf,uint8_t len,struct dwarf_cursor * c)146 arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
147 {
148 #define READ_OP() *buf++
149 const uint8_t *end = buf + len;
150 int ret;
151 struct arm_exbuf_data edata;
152
153 assert(buf != NULL);
154 assert(len > 0);
155
156 while (buf < end)
157 {
158 uint8_t op = READ_OP ();
159 if ((op & 0xc0) == 0x00)
160 {
161 edata.cmd = ARM_EXIDX_CMD_DATA_POP;
162 edata.data = (((int)op & 0x3f) << 2) + 4;
163 }
164 else if ((op & 0xc0) == 0x40)
165 {
166 edata.cmd = ARM_EXIDX_CMD_DATA_PUSH;
167 edata.data = (((int)op & 0x3f) << 2) + 4;
168 }
169 else if ((op & 0xf0) == 0x80)
170 {
171 uint8_t op2 = READ_OP ();
172 if (op == 0x80 && op2 == 0x00)
173 edata.cmd = ARM_EXIDX_CMD_REFUSED;
174 else
175 {
176 edata.cmd = ARM_EXIDX_CMD_REG_POP;
177 edata.data = ((op & 0xf) << 8) | op2;
178 edata.data = edata.data << 4;
179 }
180 }
181 else if ((op & 0xf0) == 0x90)
182 {
183 if (op == 0x9d || op == 0x9f)
184 edata.cmd = ARM_EXIDX_CMD_RESERVED;
185 else
186 {
187 edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
188 edata.data = op & 0x0f;
189 }
190 }
191 else if ((op & 0xf0) == 0xa0)
192 {
193 unsigned end = (op & 0x07);
194 edata.data = (1 << (end + 1)) - 1;
195 edata.data = edata.data << 4;
196 if (op & 0x08)
197 edata.data |= 1 << 14;
198 edata.cmd = ARM_EXIDX_CMD_REG_POP;
199 }
200 else if (op == ARM_EXTBL_OP_FINISH)
201 {
202 edata.cmd = ARM_EXIDX_CMD_FINISH;
203 buf = end;
204 }
205 else if (op == 0xb1)
206 {
207 uint8_t op2 = READ_OP ();
208 if (op2 == 0 || (op2 & 0xf0))
209 edata.cmd = ARM_EXIDX_CMD_RESERVED;
210 else
211 {
212 edata.cmd = ARM_EXIDX_CMD_REG_POP;
213 edata.data = op2 & 0x0f;
214 }
215 }
216 else if (op == 0xb2)
217 {
218 uint32_t offset = 0;
219 uint8_t byte, shift = 0;
220 do
221 {
222 byte = READ_OP ();
223 offset |= (byte & 0x7f) << shift;
224 shift += 7;
225 }
226 while (byte & 0x80);
227 edata.data = offset * 4 + 0x204;
228 edata.cmd = ARM_EXIDX_CMD_DATA_POP;
229 }
230 else if (op == 0xb3 || op == 0xc8 || op == 0xc9)
231 {
232 edata.cmd = ARM_EXIDX_CMD_VFP_POP;
233 edata.data = READ_OP ();
234 if (op == 0xc8)
235 edata.data |= ARM_EXIDX_VFP_SHIFT_16;
236 if (op != 0xb3)
237 edata.data |= ARM_EXIDX_VFP_DOUBLE;
238 }
239 else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0)
240 {
241 edata.cmd = ARM_EXIDX_CMD_VFP_POP;
242 edata.data = 0x80 | (op & 0x07);
243 if ((op & 0xf8) == 0xd0)
244 edata.data |= ARM_EXIDX_VFP_DOUBLE;
245 }
246 else if (op >= 0xc0 && op <= 0xc5)
247 {
248 edata.cmd = ARM_EXIDX_CMD_WREG_POP;
249 edata.data = 0xa0 | (op & 0x07);
250 }
251 else if (op == 0xc6)
252 {
253 edata.cmd = ARM_EXIDX_CMD_WREG_POP;
254 edata.data = READ_OP ();
255 }
256 else if (op == 0xc7)
257 {
258 uint8_t op2 = READ_OP ();
259 if (op2 == 0 || (op2 & 0xf0))
260 edata.cmd = ARM_EXIDX_CMD_RESERVED;
261 else
262 {
263 edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
264 edata.data = op2 & 0x0f;
265 }
266 }
267 else
268 edata.cmd = ARM_EXIDX_CMD_RESERVED;
269
270 ret = arm_exidx_apply_cmd (&edata, c);
271 if (ret < 0)
272 return ret;
273 }
274 return 0;
275 }
276
277 /**
278 * Reads the entry from the given cursor and extracts the unwind instructions
279 * into buf. Returns the number of the extracted unwind insns or
280 * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was
281 * found.
282 */
283 HIDDEN int
arm_exidx_extract(struct dwarf_cursor * c,uint8_t * buf)284 arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf)
285 {
286 int nbuf = 0;
287 unw_word_t entry = (unw_word_t) c->pi.unwind_info;
288 unw_word_t addr;
289 uint32_t data;
290
291 /* An ARM unwind entry consists of a prel31 offset to the start of a
292 function followed by 31bits of data:
293 * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
294 * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT)
295 * if bit 31 is zero: this is a prel31 offset of the start of the
296 table entry for this function */
297 if (prel31_to_addr(c->as, c->as_arg, entry, &addr) < 0)
298 return -UNW_EINVAL;
299
300 if ((*c->as->acc.access_mem)(c->as, entry + 4, &data, 0, c->as_arg) < 0)
301 return -UNW_EINVAL;
302
303 if (data == ARM_EXIDX_CANT_UNWIND)
304 {
305 Debug (2, "0x1 [can't unwind]\n");
306 nbuf = -UNW_ESTOPUNWIND;
307 }
308 else if (data & ARM_EXIDX_COMPACT)
309 {
310 Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr,
311 (data >> 24) & 0x7f, data);
312 buf[nbuf++] = data >> 16;
313 buf[nbuf++] = data >> 8;
314 buf[nbuf++] = data;
315 }
316 else
317 {
318 unw_word_t extbl_data;
319 unsigned int n_table_words = 0;
320
321 if (prel31_to_addr(c->as, c->as_arg, entry + 4, &extbl_data) < 0)
322 return -UNW_EINVAL;
323
324 if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0, c->as_arg) < 0)
325 return -UNW_EINVAL;
326
327 if (data & ARM_EXIDX_COMPACT)
328 {
329 int pers = (data >> 24) & 0x0f;
330 Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, pers, data);
331 if (pers == 1 || pers == 2)
332 {
333 n_table_words = (data >> 16) & 0xff;
334 extbl_data += 4;
335 }
336 else
337 buf[nbuf++] = data >> 16;
338 buf[nbuf++] = data >> 8;
339 buf[nbuf++] = data;
340 }
341 else
342 {
343 unw_word_t pers;
344 if (prel31_to_addr (c->as, c->as_arg, extbl_data, &pers) < 0)
345 return -UNW_EINVAL;
346 Debug (2, "%p Personality routine: %8p\n", (void *)addr,
347 (void *)pers);
348 if ((*c->as->acc.access_mem)(c->as, extbl_data + 4, &data, 0,
349 c->as_arg) < 0)
350 return -UNW_EINVAL;
351 n_table_words = data >> 24;
352 buf[nbuf++] = data >> 16;
353 buf[nbuf++] = data >> 8;
354 buf[nbuf++] = data;
355 extbl_data += 8;
356 }
357 assert (n_table_words <= 5);
358 unsigned j;
359 for (j = 0; j < n_table_words; j++)
360 {
361 if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0,
362 c->as_arg) < 0)
363 return -UNW_EINVAL;
364 extbl_data += 4;
365 buf[nbuf++] = data >> 24;
366 buf[nbuf++] = data >> 16;
367 buf[nbuf++] = data >> 8;
368 buf[nbuf++] = data >> 0;
369 }
370 }
371
372 if (nbuf > 0 && buf[nbuf - 1] != ARM_EXTBL_OP_FINISH)
373 buf[nbuf++] = ARM_EXTBL_OP_FINISH;
374
375 return nbuf;
376 }
377
378 PROTECTED int
tdep_search_unwind_table(unw_addr_space_t as,unw_word_t ip,unw_dyn_info_t * di,unw_proc_info_t * pi,int need_unwind_info,void * arg)379 tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
380 unw_dyn_info_t *di, unw_proc_info_t *pi,
381 int need_unwind_info, void *arg)
382 {
383 if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)
384 && di->format == UNW_INFO_FORMAT_ARM_EXIDX)
385 {
386 /* The .ARM.exidx section contains a sorted list of key-value pairs -
387 the unwind entries. The 'key' is a prel31 offset to the start of a
388 function. We binary search this section in order to find the
389 appropriate unwind entry. */
390 unw_word_t first = di->u.rti.table_data;
391 unw_word_t last = di->u.rti.table_data + di->u.rti.table_len - 8;
392 unw_word_t entry, val;
393
394 if (prel31_to_addr (as, arg, first, &val) < 0 || ip < val)
395 return -UNW_ENOINFO;
396
397 if (prel31_to_addr (as, arg, last, &val) < 0)
398 return -UNW_EINVAL;
399
400 if (ip >= val)
401 {
402 entry = last;
403
404 if (prel31_to_addr (as, arg, last, &pi->start_ip) < 0)
405 return -UNW_EINVAL;
406
407 pi->end_ip = di->end_ip -1;
408 }
409 else
410 {
411 while (first < last - 8)
412 {
413 entry = first + (((last - first) / 8 + 1) >> 1) * 8;
414
415 if (prel31_to_addr (as, arg, entry, &val) < 0)
416 return -UNW_EINVAL;
417
418 if (ip < val)
419 last = entry;
420 else
421 first = entry;
422 }
423
424 entry = first;
425
426 if (prel31_to_addr (as, arg, entry, &pi->start_ip) < 0)
427 return -UNW_EINVAL;
428
429 if (prel31_to_addr (as, arg, entry + 8, &pi->end_ip) < 0)
430 return -UNW_EINVAL;
431
432 pi->end_ip--;
433 }
434
435 if (need_unwind_info)
436 {
437 pi->unwind_info_size = 8;
438 pi->unwind_info = (void *) entry;
439 pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
440 }
441 return 0;
442 }
443 else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)
444 && di->format != UNW_INFO_FORMAT_ARM_EXIDX)
445 return dwarf_search_unwind_table (as, ip, di, pi, need_unwind_info, arg);
446
447 return -UNW_ENOINFO;
448 }
449
450 #ifndef UNW_REMOTE_ONLY
451 /**
452 * Callback to dl_iterate_phdr to find infos about the ARM exidx segment.
453 */
454 static int
arm_phdr_cb(struct dl_phdr_info * info,size_t size,void * data)455 arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data)
456 {
457 struct arm_cb_data *cb_data = data;
458 const Elf_W(Phdr) *p_text = NULL;
459 const Elf_W(Phdr) *p_arm_exidx = NULL;
460 const Elf_W(Phdr) *phdr = info->dlpi_phdr;
461 long n;
462
463 for (n = info->dlpi_phnum; --n >= 0; phdr++)
464 {
465 switch (phdr->p_type)
466 {
467 case PT_LOAD:
468 if (cb_data->ip >= phdr->p_vaddr + info->dlpi_addr &&
469 cb_data->ip < phdr->p_vaddr + info->dlpi_addr + phdr->p_memsz)
470 p_text = phdr;
471 break;
472
473 case PT_ARM_EXIDX:
474 p_arm_exidx = phdr;
475 break;
476
477 default:
478 break;
479 }
480 }
481
482 if (p_text && p_arm_exidx)
483 {
484 cb_data->di.format = UNW_INFO_FORMAT_ARM_EXIDX;
485 cb_data->di.start_ip = p_text->p_vaddr + info->dlpi_addr;
486 cb_data->di.end_ip = cb_data->di.start_ip + p_text->p_memsz;
487 cb_data->di.u.rti.name_ptr = (unw_word_t) info->dlpi_name;
488 cb_data->di.u.rti.table_data = p_arm_exidx->p_vaddr + info->dlpi_addr;
489 cb_data->di.u.rti.table_len = p_arm_exidx->p_memsz;
490 return 1;
491 }
492
493 return 0;
494 }
495
496 HIDDEN int
arm_find_proc_info(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)497 arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
498 unw_proc_info_t *pi, int need_unwind_info, void *arg)
499 {
500 int ret = -1;
501 intrmask_t saved_mask;
502
503 Debug (14, "looking for IP=0x%lx\n", (long) ip);
504
505 if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
506 {
507 ret = dwarf_find_proc_info (as, ip, pi, need_unwind_info, arg);
508 }
509
510 if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
511 {
512 struct arm_cb_data cb_data;
513
514 memset (&cb_data, 0, sizeof (cb_data));
515 cb_data.ip = ip;
516 cb_data.pi = pi;
517 cb_data.di.format = -1;
518
519 SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
520 ret = dl_iterate_phdr (arm_phdr_cb, &cb_data);
521 SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
522
523 if (cb_data.di.format != -1)
524 ret = tdep_search_unwind_table (as, ip, &cb_data.di, pi,
525 need_unwind_info, arg);
526 else
527 ret = -UNW_ENOINFO;
528 }
529
530 if (ret < 0)
531 /* ANDROID support update. */
532 {
533 Debug (14, "IP=0x%lx not found\n", (long) ip);
534 }
535 /* End of ANDROID update. */
536
537 return ret;
538 }
539
540 HIDDEN void
arm_put_unwind_info(unw_addr_space_t as,unw_proc_info_t * proc_info,void * arg)541 arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
542 {
543 /* it's a no-op */
544 }
545 #endif /* !UNW_REMOTE_ONLY */
546
547