1/* libunwind - a platform-independent unwind library 2 Copyright 2011 Linaro Limited 3 4This file is part of libunwind. 5 6Permission is hereby granted, free of charge, to any person obtaining 7a copy of this software and associated documentation files (the 8"Software"), to deal in the Software without restriction, including 9without limitation the rights to use, copy, modify, merge, publish, 10distribute, sublicense, and/or sell copies of the Software, and to 11permit persons to whom the Software is furnished to do so, subject to 12the following conditions: 13 14The above copyright notice and this permission notice shall be 15included in all copies or substantial portions of the Software. 16 17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 24 25/* This file contains functionality for parsing and interpreting the ARM 26specific unwind information. Documentation about the exception handling 27ABI for the ARM architecture can be found at: 28http://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 42enum arm_exbuf_cmd_flags { 43 ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, 44 ARM_EXIDX_VFP_DOUBLE = 1 << 17, 45}; 46 47struct 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 56static inline int 57prel31_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 */ 74HIDDEN int 75arm_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 */ 145HIDDEN int 146arm_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 */ 283HIDDEN int 284arm_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 378PROTECTED int 379tdep_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 */ 454static int 455arm_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 496HIDDEN int 497arm_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 540HIDDEN void 541arm_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