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, sizeof(unw_word_t)))
    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       /* ANDROID support update. */
    183 #ifdef CONSERVATIVE_CHECKS
    184       if (unlikely (validate_mem (addr)))
    185         return -1;
    186 #endif
    187       /* End of ANDROID update. */
    188 
    189       /* ANDROID support update. */
    190 #ifdef UNW_LOCAL_ONLY
    191       if (map_local_is_readable (addr, sizeof(unw_word_t)))
    192         {
    193 #endif
    194           *val = *(unw_word_t *) addr;
    195           Debug (16, "mem[%016lx] -> %lx\n", addr, *val);
    196 #ifdef UNW_LOCAL_ONLY
    197         }
    198       else
    199         {
    200           Debug (16, "Unreadable memory mem[%016lx] -> XXX\n", addr);
    201           return -1;
    202         }
    203 #endif
    204       /* End of ANDROID update. */
    205     }
    206   return 0;
    207 }
    208 
    209 static int
    210 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
    211 	    void *arg)
    212 {
    213   unw_word_t *addr;
    214   ucontext_t *uc = ((struct cursor *)arg)->uc;
    215 
    216   if (unw_is_fpreg (reg))
    217     goto badreg;
    218 
    219   if (!(addr = x86_64_r_uc_addr (uc, reg)))
    220     goto badreg;
    221 
    222   if (write)
    223     {
    224       *(unw_word_t *) addr = *val;
    225       Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val);
    226     }
    227   else
    228     {
    229       *val = *(unw_word_t *) addr;
    230       Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val);
    231     }
    232   return 0;
    233 
    234  badreg:
    235   Debug (1, "bad register number %u\n", reg);
    236   return -UNW_EBADREG;
    237 }
    238 
    239 static int
    240 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
    241 	      int write, void *arg)
    242 {
    243   ucontext_t *uc = ((struct cursor *)arg)->uc;
    244   unw_fpreg_t *addr;
    245 
    246   if (!unw_is_fpreg (reg))
    247     goto badreg;
    248 
    249   if (!(addr = x86_64_r_uc_addr (uc, reg)))
    250     goto badreg;
    251 
    252   if (write)
    253     {
    254       Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
    255 	     ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
    256       *(unw_fpreg_t *) addr = *val;
    257     }
    258   else
    259     {
    260       *val = *(unw_fpreg_t *) addr;
    261       Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
    262 	     ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
    263     }
    264   return 0;
    265 
    266  badreg:
    267   Debug (1, "bad register number %u\n", reg);
    268   /* attempt to access a non-preserved register */
    269   return -UNW_EBADREG;
    270 }
    271 
    272 static int
    273 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
    274 		      char *buf, size_t buf_len, unw_word_t *offp,
    275 		      void *arg)
    276 {
    277   return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
    278 }
    279 
    280 static int
    281 access_mem_unrestricted (unw_addr_space_t as, unw_word_t addr, unw_word_t *val,
    282                          int write, void *arg)
    283 {
    284   if (write)
    285     return -1;
    286 
    287   *val = *(unw_word_t *) addr;
    288   Debug (16, "mem[%016lx] -> %lx\n", addr, *val);
    289   return 0;
    290 }
    291 
    292 // This initializes just enough of the address space to call the
    293 // access memory function.
    294 PROTECTED void
    295 unw_local_access_addr_space_init (unw_addr_space_t as)
    296 {
    297   memset (as, 0, sizeof (*as));
    298   as->acc.access_mem = access_mem_unrestricted;
    299 }
    300 
    301 HIDDEN void
    302 x86_64_local_addr_space_init (void)
    303 {
    304   memset (&local_addr_space, 0, sizeof (local_addr_space));
    305   local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
    306   local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
    307   local_addr_space.acc.put_unwind_info = put_unwind_info;
    308   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
    309   local_addr_space.acc.access_mem = access_mem;
    310   local_addr_space.acc.access_reg = access_reg;
    311   local_addr_space.acc.access_fpreg = access_fpreg;
    312   local_addr_space.acc.resume = x86_64_local_resume;
    313   local_addr_space.acc.get_proc_name = get_static_proc_name;
    314   unw_flush_cache (&local_addr_space, 0, 0);
    315 
    316   memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
    317   lga_victim = 0;
    318 
    319   map_local_init ();
    320 }
    321 
    322 #endif /* !UNW_REMOTE_ONLY */
    323