1 /* libunwind - a platform-independent unwind library 2 Copyright (C) 2001-2005 Hewlett-Packard Co 3 Copyright (C) 2007 David Mosberger-Tang 4 Contributed by David Mosberger-Tang <dmosberger (at) gmail.com> 5 6 This file is part of libunwind. 7 8 Permission is hereby granted, free of charge, to any person obtaining 9 a copy of this software and associated documentation files (the 10 "Software"), to deal in the Software without restriction, including 11 without limitation the rights to use, copy, modify, merge, publish, 12 distribute, sublicense, and/or sell copies of the Software, and to 13 permit persons to whom the Software is furnished to do so, subject to 14 the following conditions: 15 16 The above copyright notice and this permission notice shall be 17 included in all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 26 27 #include "unwind_i.h" 28 29 #ifdef HAVE_SYS_UC_ACCESS_H 30 # include <sys/uc_access.h> 31 #endif 32 33 #ifdef UNW_REMOTE_ONLY 34 35 /* unw_local_addr_space is a NULL pointer in this case. */ 36 PROTECTED unw_addr_space_t unw_local_addr_space; 37 38 #else /* !UNW_REMOTE_ONLY */ 39 40 static struct unw_addr_space local_addr_space; 41 42 PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space; 43 44 #ifdef HAVE_SYS_UC_ACCESS_H 45 46 #else /* !HAVE_SYS_UC_ACCESS_H */ 47 48 HIDDEN void * 49 tdep_uc_addr (ucontext_t *uc, int reg, uint8_t *nat_bitnr) 50 { 51 return inlined_uc_addr (uc, reg, nat_bitnr); 52 } 53 54 #endif /* !HAVE_SYS_UC_ACCESS_H */ 55 56 static void 57 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) 58 { 59 /* it's a no-op */ 60 } 61 62 static int 63 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, 64 void *arg) 65 { 66 #ifndef UNW_LOCAL_ONLY 67 # pragma weak _U_dyn_info_list_addr 68 if (!_U_dyn_info_list_addr) 69 return -UNW_ENOINFO; 70 #endif 71 *dyn_info_list_addr = _U_dyn_info_list_addr (); 72 return 0; 73 } 74 75 static int 76 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, 77 void *arg) 78 { 79 if (write) 80 { 81 /* ANDROID support update. */ 82 #ifdef UNW_LOCAL_ONLY 83 if (map_local_is_writable (addr)) 84 { 85 #endif 86 Debug (12, "mem[%lx] <- %lx\n", addr, *val); 87 *(unw_word_t *) addr = *val; 88 #ifdef UNW_LOCAL_ONLY 89 } 90 else 91 { 92 Debug (12, "Unwritable memory mem[%lx] <- %lx\n", addr, *val); 93 return -1; 94 } 95 #endif 96 /* End of ANDROID update. */ 97 } 98 else 99 { 100 /* ANDROID support update. */ 101 #ifdef UNW_LOCAL_ONLY 102 if (map_local_is_readable (addr)) 103 { 104 #endif 105 *val = *(unw_word_t *) addr; 106 Debug (12, "mem[%lx] -> %lx\n", addr, *val); 107 #ifdef UNW_LOCAL_ONLY 108 } 109 else 110 { 111 Debug (12, "Unreadable memory mem[%lx] -> XXX\n", addr); 112 return -1; 113 } 114 #endif 115 /* End of ANDROID update. */ 116 } 117 return 0; 118 } 119 120 #ifdef HAVE_SYS_UC_ACCESS_H 121 122 #define SYSCALL_CFM_SAVE_REG 11 /* on a syscall, ar.pfs is saved in r11 */ 123 #define REASON_SYSCALL 0 124 125 static int 126 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, 127 void *arg) 128 { 129 ucontext_t *uc = arg; 130 unsigned int nat, mask; 131 uint64_t value; 132 uint16_t reason; 133 int ret; 134 135 __uc_get_reason (uc, &reason); 136 137 switch (reg) 138 { 139 case UNW_IA64_GR ... UNW_IA64_GR + 31: 140 if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat))) 141 break; 142 143 if (write) 144 ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, val, nat); 145 else 146 *val = value; 147 break; 148 149 case UNW_IA64_NAT ... UNW_IA64_NAT + 31: 150 if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat))) 151 break; 152 153 mask = 1 << (reg - UNW_IA64_GR); 154 155 if (write) 156 { 157 if (*val) 158 nat |= mask; 159 else 160 nat &= ~mask; 161 ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, &value, nat); 162 } 163 else 164 *val = (nat & mask) != 0; 165 break; 166 167 case UNW_IA64_AR ... UNW_IA64_AR + 127: 168 if (reg == UNW_IA64_AR_BSP) 169 { 170 if (write) 171 ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val); 172 else 173 ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val); 174 } 175 else if (reg == UNW_IA64_AR_PFS && reason == REASON_SYSCALL) 176 { 177 /* As of HP-UX 11.22, getcontext() does not have unwind info 178 and because of that, we need to hack thins manually here. 179 Hopefully, this is OK because the HP-UX kernel also needs 180 to know where AR.PFS has been saved, so the use of 181 register r11 for this purpose is pretty much nailed 182 down. */ 183 if (write) 184 ret = __uc_set_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, 0); 185 else 186 ret = __uc_get_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, &nat); 187 } 188 else 189 { 190 if (write) 191 ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val); 192 else 193 ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val); 194 } 195 break; 196 197 case UNW_IA64_BR ... UNW_IA64_BR + 7: 198 if (write) 199 ret = __uc_set_brs (uc, (reg - UNW_IA64_BR), 1, val); 200 else 201 ret = __uc_get_brs (uc, (reg - UNW_IA64_BR), 1, val); 202 break; 203 204 case UNW_IA64_PR: 205 if (write) 206 ret = __uc_set_prs (uc, *val); 207 else 208 ret = __uc_get_prs (uc, val); 209 break; 210 211 case UNW_IA64_IP: 212 if (write) 213 ret = __uc_set_ip (uc, *val); 214 else 215 ret = __uc_get_ip (uc, val); 216 break; 217 218 case UNW_IA64_CFM: 219 if (write) 220 ret = __uc_set_cfm (uc, *val); 221 else 222 ret = __uc_get_cfm (uc, val); 223 break; 224 225 case UNW_IA64_FR ... UNW_IA64_FR + 127: 226 default: 227 ret = EINVAL; 228 break; 229 } 230 231 if (ret != 0) 232 { 233 Debug (1, "failed to %s %s (ret = %d)\n", 234 write ? "write" : "read", unw_regname (reg), ret); 235 return -UNW_EBADREG; 236 } 237 238 if (write) 239 Debug (12, "%s <- %lx\n", unw_regname (reg), *val); 240 else 241 Debug (12, "%s -> %lx\n", unw_regname (reg), *val); 242 return 0; 243 } 244 245 static int 246 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, 247 int write, void *arg) 248 { 249 ucontext_t *uc = arg; 250 fp_regval_t fp_regval; 251 int ret; 252 253 switch (reg) 254 { 255 case UNW_IA64_FR ... UNW_IA64_FR + 127: 256 if (write) 257 { 258 memcpy (&fp_regval, val, sizeof (fp_regval)); 259 ret = __uc_set_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval); 260 } 261 else 262 { 263 ret = __uc_get_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval); 264 memcpy (val, &fp_regval, sizeof (*val)); 265 } 266 break; 267 268 default: 269 ret = EINVAL; 270 break; 271 } 272 if (ret != 0) 273 return -UNW_EBADREG; 274 275 return 0; 276 } 277 278 #else /* !HAVE_SYS_UC_ACCESS_H */ 279 280 static int 281 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, 282 void *arg) 283 { 284 unw_word_t *addr, mask; 285 ucontext_t *uc = arg; 286 287 if (reg >= UNW_IA64_NAT + 4 && reg <= UNW_IA64_NAT + 7) 288 { 289 mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT); 290 if (write) 291 { 292 if (*val) 293 uc->uc_mcontext.sc_nat |= mask; 294 else 295 uc->uc_mcontext.sc_nat &= ~mask; 296 } 297 else 298 *val = (uc->uc_mcontext.sc_nat & mask) != 0; 299 300 if (write) 301 Debug (12, "%s <- %lx\n", unw_regname (reg), *val); 302 else 303 Debug (12, "%s -> %lx\n", unw_regname (reg), *val); 304 return 0; 305 } 306 307 addr = tdep_uc_addr (uc, reg, NULL); 308 if (!addr) 309 goto badreg; 310 311 if (write) 312 { 313 if (ia64_read_only_reg (addr)) 314 { 315 Debug (16, "attempt to write read-only register\n"); 316 return -UNW_EREADONLYREG; 317 } 318 *addr = *val; 319 Debug (12, "%s <- %lx\n", unw_regname (reg), *val); 320 } 321 else 322 { 323 *val = *(unw_word_t *) addr; 324 Debug (12, "%s -> %lx\n", unw_regname (reg), *val); 325 } 326 return 0; 327 328 badreg: 329 Debug (1, "bad register number %u\n", reg); 330 return -UNW_EBADREG; 331 } 332 333 static int 334 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, 335 int write, void *arg) 336 { 337 ucontext_t *uc = arg; 338 unw_fpreg_t *addr; 339 340 if (reg < UNW_IA64_FR || reg >= UNW_IA64_FR + 128) 341 goto badreg; 342 343 addr = tdep_uc_addr (uc, reg, NULL); 344 if (!addr) 345 goto badreg; 346 347 if (write) 348 { 349 if (ia64_read_only_reg (addr)) 350 { 351 Debug (16, "attempt to write read-only register\n"); 352 return -UNW_EREADONLYREG; 353 } 354 *addr = *val; 355 Debug (12, "%s <- %016lx.%016lx\n", 356 unw_regname (reg), val->raw.bits[1], val->raw.bits[0]); 357 } 358 else 359 { 360 *val = *(unw_fpreg_t *) addr; 361 Debug (12, "%s -> %016lx.%016lx\n", 362 unw_regname (reg), val->raw.bits[1], val->raw.bits[0]); 363 } 364 return 0; 365 366 badreg: 367 Debug (1, "bad register number %u\n", reg); 368 /* attempt to access a non-preserved register */ 369 return -UNW_EBADREG; 370 } 371 372 #endif /* !HAVE_SYS_UC_ACCESS_H */ 373 374 static int 375 get_static_proc_name (unw_addr_space_t as, unw_word_t ip, 376 char *buf, size_t buf_len, unw_word_t *offp, 377 void *arg) 378 { 379 return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg); 380 } 381 382 HIDDEN void 383 ia64_local_addr_space_init (void) 384 { 385 memset (&local_addr_space, 0, sizeof (local_addr_space)); 386 local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN); 387 #if defined(__linux) 388 local_addr_space.abi = ABI_LINUX; 389 #elif defined(__hpux) 390 local_addr_space.abi = ABI_HPUX; 391 #endif 392 local_addr_space.caching_policy = UNW_CACHE_GLOBAL; 393 local_addr_space.acc.find_proc_info = tdep_find_proc_info; 394 local_addr_space.acc.put_unwind_info = put_unwind_info; 395 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr; 396 local_addr_space.acc.access_mem = access_mem; 397 local_addr_space.acc.access_reg = access_reg; 398 local_addr_space.acc.access_fpreg = access_fpreg; 399 local_addr_space.acc.resume = ia64_local_resume; 400 local_addr_space.acc.get_proc_name = get_static_proc_name; 401 unw_flush_cache (&local_addr_space, 0, 0); 402 403 map_local_init (); 404 } 405 406 #endif /* !UNW_REMOTE_ONLY */ 407 408 #ifndef UNW_LOCAL_ONLY 409 410 HIDDEN int 411 ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc, unw_word_t *valp, 412 int write) 413 { 414 #ifdef HAVE_SYS_UC_ACCESS_H 415 unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc); 416 ucontext_t *ucp; 417 int ret; 418 419 Debug (16, "%s location %s\n", 420 write ? "writing" : "reading", ia64_strloc (loc)); 421 422 if (c->as == unw_local_addr_space) 423 ucp = (ucontext_t *) uc_addr; 424 else 425 { 426 unw_word_t *dst, src; 427 428 /* Need to copy-in ucontext_t first. */ 429 ucp = alloca (sizeof (ucontext_t)); 430 if (!ucp) 431 return -UNW_ENOMEM; 432 433 /* For now, there is no non-HP-UX implementation of the 434 uc_access(3) interface. Because of that, we cannot, e.g., 435 unwind an HP-UX program from a Linux program. Should that 436 become possible at some point in the future, the 437 copy-in/copy-out needs to be adjusted to do byte-swapping if 438 necessary. */ 439 assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN)); 440 441 dst = (unw_word_t *) ucp; 442 for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8) 443 if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg)) 444 < 0) 445 return ret; 446 } 447 448 if (IA64_IS_REG_LOC (loc)) 449 ret = access_reg (unw_local_addr_space, IA64_GET_REG (loc), valp, write, 450 ucp); 451 else 452 { 453 /* Must be an access to the RSE backing store in ucontext_t. */ 454 unw_word_t addr = IA64_GET_ADDR (loc); 455 456 if (write) 457 ret = __uc_set_rsebs (ucp, (uint64_t *) addr, 1, valp); 458 else 459 ret = __uc_get_rsebs (ucp, (uint64_t *) addr, 1, valp); 460 if (ret != 0) 461 ret = -UNW_EBADREG; 462 } 463 if (ret < 0) 464 return ret; 465 466 if (write && c->as != unw_local_addr_space) 467 { 468 /* need to copy-out ucontext_t: */ 469 unw_word_t dst, *src = (unw_word_t *) ucp; 470 for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8) 471 if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg)) 472 < 0) 473 return ret; 474 } 475 return 0; 476 #else /* !HAVE_SYS_UC_ACCESS_H */ 477 return -UNW_EINVAL; 478 #endif /* !HAVE_SYS_UC_ACCESS_H */ 479 } 480 481 HIDDEN int 482 ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *valp, 483 int write) 484 { 485 #ifdef HAVE_SYS_UC_ACCESS_H 486 unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc); 487 ucontext_t *ucp; 488 int ret; 489 490 if (c->as == unw_local_addr_space) 491 ucp = (ucontext_t *) uc_addr; 492 else 493 { 494 unw_word_t *dst, src; 495 496 /* Need to copy-in ucontext_t first. */ 497 ucp = alloca (sizeof (ucontext_t)); 498 if (!ucp) 499 return -UNW_ENOMEM; 500 501 /* For now, there is no non-HP-UX implementation of the 502 uc_access(3) interface. Because of that, we cannot, e.g., 503 unwind an HP-UX program from a Linux program. Should that 504 become possible at some point in the future, the 505 copy-in/copy-out needs to be adjusted to do byte-swapping if 506 necessary. */ 507 assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN)); 508 509 dst = (unw_word_t *) ucp; 510 for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8) 511 if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg)) 512 < 0) 513 return ret; 514 } 515 516 if ((ret = access_fpreg (unw_local_addr_space, IA64_GET_REG (loc), valp, 517 write, ucp)) < 0) 518 return ret; 519 520 if (write && c->as != unw_local_addr_space) 521 { 522 /* need to copy-out ucontext_t: */ 523 unw_word_t dst, *src = (unw_word_t *) ucp; 524 for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8) 525 if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg)) 526 < 0) 527 return ret; 528 } 529 return 0; 530 #else /* !HAVE_SYS_UC_ACCESS_H */ 531 return -UNW_EINVAL; 532 #endif /* !HAVE_SYS_UC_ACCESS_H */ 533 } 534 535 #endif /* UNW_LOCAL_ONLY */ 536