Home | History | Annotate | Download | only in ia64
      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);
    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