Home | History | Annotate | Download | only in src
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2014 The Android Open Source Project
      3 
      4 This file is part of libunwind.
      5 
      6 Permission is hereby granted, free of charge, to any person obtaining
      7 a copy of this software and associated documentation files (the
      8 "Software"), to deal in the Software without restriction, including
      9 without limitation the rights to use, copy, modify, merge, publish,
     10 distribute, sublicense, and/or sell copies of the Software, and to
     11 permit persons to whom the Software is furnished to do so, subject to
     12 the following conditions:
     13 
     14 The above copyright notice and this permission notice shall be
     15 included in all copies or substantial portions of the Software.
     16 
     17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     21 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     24 
     25 #define UNW_LOCAL_ONLY
     26 #include <libunwind.h>
     27 #include "libunwind_i.h"
     28 
     29 /* Global to hold the map for all local unwinds. */
     30 extern struct map_info *local_map_list;
     31 extern lock_rdwr_var (local_rdwr_lock);
     32 
     33 static pthread_once_t local_rdwr_lock_init = PTHREAD_ONCE_INIT;
     34 
     35 static void
     36 map_local_init_once (void)
     37 {
     38   lock_rdwr_init (&local_rdwr_lock);
     39 }
     40 
     41 HIDDEN void
     42 map_local_init (void)
     43 {
     44   pthread_once (&local_rdwr_lock_init, map_local_init_once);
     45 }
     46 
     47 static void
     48 move_cached_elf_data (struct map_info *old_list, struct map_info *new_list)
     49 {
     50   while (old_list)
     51     {
     52       if (!old_list->ei.valid)
     53         {
     54           old_list = old_list->next;
     55           continue;
     56         }
     57       /* Both lists are in order, so it's not necessary to scan through
     58          from the beginning of new_list each time looking for a match to
     59          the current map. As we progress, simply start from the last element
     60          in new_list we checked. */
     61       while (new_list && old_list->start <= new_list->start)
     62         {
     63           if (old_list->start == new_list->start
     64               && old_list->end == new_list->end)
     65             {
     66               /* No need to do any lock, the entire local_map_list is locked
     67                  at this point. */
     68               new_list->ei = old_list->ei;
     69               /* If it was mapped before, make sure to mark it unmapped now. */
     70               old_list->ei.mapped = false;
     71               /* Don't bother breaking out of the loop, the next while check
     72                  is guaranteed to fail, causing us to break out of the loop
     73                  after advancing to the next map element. */
     74             }
     75           new_list = new_list->next;
     76         }
     77       old_list = old_list->next;
     78     }
     79 }
     80 
     81 /* In order to cache as much as possible while unwinding the local process,
     82    we gather a map of the process before starting. If the cache is missing
     83    a map, or a map exists but doesn't have the "expected_flags" set, then
     84    check if the cache needs to be regenerated.
     85    While regenerating the list, grab a write lock to avoid any readers using
     86    the list while it's being modified. */
     87 static int
     88 rebuild_if_necessary (unw_word_t addr, int expected_flags)
     89 {
     90   struct map_info *map;
     91   struct map_info *new_list;
     92   int ret_value = -1;
     93   intrmask_t saved_mask;
     94 
     95   new_list = map_create_list (UNW_MAP_CREATE_LOCAL, getpid());
     96   map = map_find_from_addr (new_list, addr);
     97   if (map && (expected_flags == 0 || (map->flags & expected_flags)))
     98     {
     99       /* Get a write lock on local_map_list since it's going to be modified. */
    100       lock_rdwr_wr_acquire (&local_rdwr_lock, saved_mask);
    101 
    102       /* Just in case another thread rebuilt the map, check to see if the
    103          ip with expected_flags is in local_map_list. If not, the assumption
    104          is that new_list is newer than local_map_list because the map only
    105          gets new maps with new permissions. If this is not true, then it
    106          would be necessary to regenerate the list one more time. */
    107       ret_value = 0;
    108       map = map_find_from_addr (local_map_list, addr);
    109       if (!map || (expected_flags != 0 && !(map->flags & expected_flags)))
    110         {
    111           /* Move any cached items to the new list. */
    112           move_cached_elf_data (local_map_list, new_list);
    113           map = local_map_list;
    114           local_map_list = new_list;
    115           new_list = map;
    116         }
    117 
    118       lock_rdwr_release (&local_rdwr_lock, saved_mask);
    119     }
    120 
    121   map_destroy_list (new_list);
    122 
    123   return ret_value;
    124 }
    125 
    126 static int
    127 is_flag_set (unw_word_t addr, int flag)
    128 {
    129   struct map_info *map;
    130   int ret = 0;
    131   intrmask_t saved_mask;
    132 
    133   lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    134   map = map_find_from_addr (local_map_list, addr);
    135   if (map != NULL)
    136     {
    137       if (map->flags & MAP_FLAGS_DEVICE_MEM)
    138         {
    139           lock_rdwr_release (&local_rdwr_lock, saved_mask);
    140           return 0;
    141         }
    142       ret = map->flags & flag;
    143     }
    144   lock_rdwr_release (&local_rdwr_lock, saved_mask);
    145 
    146   if (!ret && rebuild_if_necessary (addr, flag) == 0)
    147     {
    148       return 1;
    149     }
    150   return ret;
    151 }
    152 
    153 PROTECTED int
    154 map_local_is_readable (unw_word_t addr)
    155 {
    156   return is_flag_set (addr, PROT_READ);
    157 }
    158 
    159 PROTECTED int
    160 map_local_is_writable (unw_word_t addr)
    161 {
    162   return is_flag_set (addr, PROT_WRITE);
    163 }
    164 
    165 PROTECTED int
    166 local_get_elf_image (unw_addr_space_t as, struct elf_image *ei, unw_word_t ip,
    167                      unsigned long *segbase, unsigned long *mapoff, char **path, void *as_arg)
    168 {
    169   struct map_info *map;
    170   intrmask_t saved_mask;
    171   int return_value = -UNW_ENOINFO;
    172 
    173   lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    174   map = map_find_from_addr (local_map_list, ip);
    175   if (!map)
    176     {
    177       lock_rdwr_release (&local_rdwr_lock, saved_mask);
    178       if (rebuild_if_necessary (ip, 0) < 0)
    179         return -UNW_ENOINFO;
    180 
    181       lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    182       map = map_find_from_addr (local_map_list, ip);
    183     }
    184 
    185   if (map && elf_map_cached_image (as, as_arg, map, ip))
    186     {
    187       *ei = map->ei;
    188       *segbase = map->start;
    189       if (ei->mapped)
    190         *mapoff = map->offset;
    191       else
    192         /* Always use zero as the map offset for in memory maps. The
    193          * dlopen of a shared library from an APK will result in a
    194          * non-zero offset so it won't match the elf data and cause
    195          * unwinds to fail. Currently, only in memory unwinds of an APK
    196          * are possible, so only modify this path.
    197          */
    198         *mapoff = 0;
    199       if (path != NULL)
    200         {
    201           if (map->path)
    202             *path = strdup(map->path);
    203           else
    204             *path = NULL;
    205         }
    206       return_value = 0;
    207     }
    208   lock_rdwr_release (&local_rdwr_lock, saved_mask);
    209 
    210   return return_value;
    211 }
    212 
    213 PROTECTED char *
    214 map_local_get_image_name (unw_word_t ip)
    215 {
    216   struct map_info *map;
    217   intrmask_t saved_mask;
    218   char *image_name = NULL;
    219 
    220   lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    221   map = map_find_from_addr (local_map_list, ip);
    222   if (!map)
    223     {
    224       lock_rdwr_release (&local_rdwr_lock, saved_mask);
    225       if (rebuild_if_necessary (ip, 0) < 0)
    226         return NULL;
    227 
    228       lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    229       map = map_find_from_addr (local_map_list, ip);
    230     }
    231   if (map)
    232     image_name = strdup (map->path);
    233   lock_rdwr_release (&local_rdwr_lock, saved_mask);
    234 
    235   return image_name;
    236 }
    237