Home | History | Annotate | Download | only in x86_64
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2002 Hewlett-Packard Co
      3    Copyright (C) 2007 David Mosberger-Tang
      4 	Contributed by David Mosberger-Tang <dmosberger (at) gmail.com>
      5 
      6    Modified for x86_64 by Max Asbock <masbock (at) us.ibm.com>
      7 
      8 This file is part of libunwind.
      9 
     10 Permission is hereby granted, free of charge, to any person obtaining
     11 a copy of this software and associated documentation files (the
     12 "Software"), to deal in the Software without restriction, including
     13 without limitation the rights to use, copy, modify, merge, publish,
     14 distribute, sublicense, and/or sell copies of the Software, and to
     15 permit persons to whom the Software is furnished to do so, subject to
     16 the following conditions:
     17 
     18 The above copyright notice and this permission notice shall be
     19 included in all copies or substantial portions of the Software.
     20 
     21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     25 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     26 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 #include <config.h>
     31 #endif
     32 
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <sys/mman.h>
     36 
     37 #include "unwind_i.h"
     38 
     39 #ifdef UNW_REMOTE_ONLY
     40 
     41 /* unw_local_addr_space is a NULL pointer in this case.  */
     42 PROTECTED unw_addr_space_t unw_local_addr_space;
     43 
     44 #else /* !UNW_REMOTE_ONLY */
     45 
     46 static struct unw_addr_space local_addr_space;
     47 
     48 PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
     49 
     50 HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
     51 
     52 /* XXX fix me: there is currently no way to locate the dyn-info list
     53        by a remote unwinder.  On ia64, this is done via a special
     54        unwind-table entry.  Perhaps something similar can be done with
     55        DWARF2 unwind info.  */
     56 
     57 static void
     58 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
     59 {
     60   /* it's a no-op */
     61 }
     62 
     63 static int
     64 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
     65 			void *arg)
     66 {
     67   *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
     68   return 0;
     69 }
     70 
     71 #define PAGE_SIZE 4096
     72 #define PAGE_START(a)	((a) & ~(PAGE_SIZE-1))
     73 
     74 static int (*mem_validate_func) (void *addr, size_t len);
     75 static int msync_validate (void *addr, size_t len)
     76 {
     77   return msync (addr, len, MS_ASYNC);
     78 }
     79 
     80 #ifdef HAVE_MINCORE
     81 static int mincore_validate (void *addr, size_t len)
     82 {
     83   unsigned char mvec[2]; /* Unaligned access may cross page boundary */
     84   return mincore (addr, len, mvec);
     85 }
     86 #endif
     87 
     88 /* Initialise memory validation method. On linux kernels <2.6.21,
     89    mincore() returns incorrect value for MAP_PRIVATE mappings,
     90    such as stacks. If mincore() was available at compile time,
     91    check if we can actually use it. If not, use msync() instead. */
     92 HIDDEN void
     93 tdep_init_mem_validate (void)
     94 {
     95 #ifdef HAVE_MINCORE
     96   unsigned char present = 1;
     97   if (mincore (&present, 1, &present) == 0)
     98     {
     99       Debug(1, "using mincore to validate memory\n");
    100       mem_validate_func = mincore_validate;
    101     }
    102   else
    103 #endif
    104     {
    105       Debug(1, "using msync to validate memory\n");
    106       mem_validate_func = msync_validate;
    107     }
    108 }
    109 
    110 /* Cache of already validated addresses */
    111 #define NLGA 4
    112 static unw_word_t last_good_addr[NLGA];
    113 static int lga_victim;
    114 
    115 static int
    116 validate_mem (unw_word_t addr)
    117 {
    118   int i, victim;
    119   size_t len;
    120 
    121   if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
    122     len = PAGE_SIZE;
    123   else
    124     len = PAGE_SIZE * 2;
    125 
    126   addr = PAGE_START(addr);
    127 
    128   if (addr == 0)
    129     return -1;
    130 
    131   for (i = 0; i < NLGA; i++)
    132     {
    133       if (last_good_addr[i] && (addr == last_good_addr[i]))
    134 	return 0;
    135     }
    136 
    137   if (mem_validate_func ((void *) addr, len) == -1)
    138     return -1;
    139 
    140   victim = lga_victim;
    141   for (i = 0; i < NLGA; i++) {
    142     if (!last_good_addr[victim]) {
    143       last_good_addr[victim++] = addr;
    144       return 0;
    145     }
    146     victim = (victim + 1) % NLGA;
    147   }
    148 
    149   /* All slots full. Evict the victim. */
    150   last_good_addr[victim] = addr;
    151   victim = (victim + 1) % NLGA;
    152   lga_victim = victim;
    153 
    154   return 0;
    155 }
    156 
    157 static int
    158 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
    159 	    void *arg)
    160 {
    161   if (unlikely (write))
    162     {
    163       /* ANDROID support update. */
    164 #ifdef UNW_LOCAL_ONLY
    165       if (map_local_is_writable (addr))
    166         {
    167 #endif
    168           Debug (16, "mem[%016lx] <- %lx\n", addr, *val);
    169           *(unw_word_t *) addr = *val;
    170 #ifdef UNW_LOCAL_ONLY
    171         }
    172       else
    173         {
    174           Debug (16, "Unwritable memory mem[%016lx] <- %lx\n", addr, *val);
    175           return -1;
    176         }
    177 #endif
    178       /* End of ANDROID update. */
    179     }
    180   else
    181     {
    182       /* validate address */
    183       const struct cursor *c = (const struct cursor *)arg;
    184       if (likely (c != NULL) && unlikely (c->validate)
    185           && unlikely (validate_mem (addr)))
    186         return -1;
    187 
    188       /* ANDROID support update. */
    189 #ifdef UNW_LOCAL_ONLY
    190       if (map_local_is_readable (addr))
    191         {
    192 #endif
    193           *val = *(unw_word_t *) addr;
    194           Debug (16, "mem[%016lx] -> %lx\n", addr, *val);
    195 #ifdef UNW_LOCAL_ONLY
    196         }
    197       else
    198         {
    199           Debug (16, "Unreadable memory mem[%016lx] -> XXX\n", addr);
    200           return -1;
    201         }
    202 #endif
    203       /* End of ANDROID update. */
    204     }
    205   return 0;
    206 }
    207 
    208 static int
    209 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
    210 	    void *arg)
    211 {
    212   unw_word_t *addr;
    213   ucontext_t *uc = ((struct cursor *)arg)->uc;
    214 
    215   if (unw_is_fpreg (reg))
    216     goto badreg;
    217 
    218   if (!(addr = x86_64_r_uc_addr (uc, reg)))
    219     goto badreg;
    220 
    221   if (write)
    222     {
    223       *(unw_word_t *) addr = *val;
    224       Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val);
    225     }
    226   else
    227     {
    228       *val = *(unw_word_t *) addr;
    229       Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val);
    230     }
    231   return 0;
    232 
    233  badreg:
    234   Debug (1, "bad register number %u\n", reg);
    235   return -UNW_EBADREG;
    236 }
    237 
    238 static int
    239 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
    240 	      int write, void *arg)
    241 {
    242   ucontext_t *uc = ((struct cursor *)arg)->uc;
    243   unw_fpreg_t *addr;
    244 
    245   if (!unw_is_fpreg (reg))
    246     goto badreg;
    247 
    248   if (!(addr = x86_64_r_uc_addr (uc, reg)))
    249     goto badreg;
    250 
    251   if (write)
    252     {
    253       Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
    254 	     ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
    255       *(unw_fpreg_t *) addr = *val;
    256     }
    257   else
    258     {
    259       *val = *(unw_fpreg_t *) addr;
    260       Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
    261 	     ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
    262     }
    263   return 0;
    264 
    265  badreg:
    266   Debug (1, "bad register number %u\n", reg);
    267   /* attempt to access a non-preserved register */
    268   return -UNW_EBADREG;
    269 }
    270 
    271 static int
    272 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
    273 		      char *buf, size_t buf_len, unw_word_t *offp,
    274 		      void *arg)
    275 {
    276   return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
    277 }
    278 
    279 HIDDEN void
    280 x86_64_local_addr_space_init (void)
    281 {
    282   memset (&local_addr_space, 0, sizeof (local_addr_space));
    283   local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
    284   local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
    285   local_addr_space.acc.put_unwind_info = put_unwind_info;
    286   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
    287   local_addr_space.acc.access_mem = access_mem;
    288   local_addr_space.acc.access_reg = access_reg;
    289   local_addr_space.acc.access_fpreg = access_fpreg;
    290   local_addr_space.acc.resume = x86_64_local_resume;
    291   local_addr_space.acc.get_proc_name = get_static_proc_name;
    292   unw_flush_cache (&local_addr_space, 0, 0);
    293 
    294   memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
    295   lga_victim = 0;
    296 
    297   map_local_init ();
    298 }
    299 
    300 #endif /* !UNW_REMOTE_ONLY */
    301