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.image == NULL)
     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.size = old_list->ei.size;
     69               new_list->ei.image = old_list->ei.image;
     70               old_list->ei.size = 0;
     71               old_list->ei.image = NULL;
     72               /* Don't bother breaking out of the loop, the next while check
     73                  is guaranteed to fail, causing us to break out of the loop
     74                  after advancing to the next map element. */
     75             }
     76           new_list = new_list->next;
     77         }
     78       old_list = old_list->next;
     79     }
     80 }
     81 
     82 /* In order to cache as much as possible while unwinding the local process,
     83    we gather a map of the process before starting. If the cache is missing
     84    a map, or a map exists but doesn't have the "expected_flags" set, then
     85    check if the cache needs to be regenerated.
     86    While regenerating the list, grab a write lock to avoid any readers using
     87    the list while it's being modified. */
     88 static int
     89 rebuild_if_necessary (unw_word_t addr, int expected_flags)
     90 {
     91   struct map_info *map;
     92   struct map_info *new_list;
     93   int ret_value = -1;
     94   intrmask_t saved_mask;
     95 
     96   new_list = map_create_list (getpid());
     97   map = map_find_from_addr (new_list, addr);
     98   if (map && (expected_flags == 0 || (map->flags & expected_flags)))
     99     {
    100       /* Get a write lock on local_map_list since it's going to be modified. */
    101       lock_rdwr_wr_acquire (&local_rdwr_lock, saved_mask);
    102 
    103       /* Just in case another thread rebuilt the map, check to see if the
    104          ip with expected_flags is in local_map_list. If not, the assumption
    105          is that new_list is newer than local_map_list because the map only
    106          gets new maps with new permissions. If this is not true, then it
    107          would be necessary to regenerate the list one more time. */
    108       ret_value = 0;
    109       map = map_find_from_addr (local_map_list, addr);
    110       if (!map || (expected_flags != 0 && !(map->flags & expected_flags)))
    111         {
    112           /* Move any cached items to the new list. */
    113           move_cached_elf_data (local_map_list, new_list);
    114           map = local_map_list;
    115           local_map_list = new_list;
    116           new_list = map;
    117         }
    118 
    119       lock_rdwr_release (&local_rdwr_lock, saved_mask);
    120     }
    121 
    122   map_destroy_list (new_list);
    123 
    124   return ret_value;
    125 }
    126 
    127 static int
    128 is_flag_set (unw_word_t addr, int flag)
    129 {
    130   struct map_info *map;
    131   int ret = 0;
    132   intrmask_t saved_mask;
    133 
    134   lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    135   map = map_find_from_addr (local_map_list, addr);
    136   if (map != NULL)
    137     {
    138       if (map->flags & MAP_FLAGS_DEVICE_MEM)
    139         {
    140           lock_rdwr_release (&local_rdwr_lock, saved_mask);
    141           return 0;
    142         }
    143       ret = map->flags & flag;
    144     }
    145   lock_rdwr_release (&local_rdwr_lock, saved_mask);
    146 
    147   if (!ret && rebuild_if_necessary (addr, flag) == 0)
    148     {
    149       return 1;
    150     }
    151   return ret;
    152 }
    153 
    154 PROTECTED int
    155 map_local_is_readable (unw_word_t addr)
    156 {
    157   return is_flag_set (addr, PROT_READ);
    158 }
    159 
    160 PROTECTED int
    161 map_local_is_writable (unw_word_t addr)
    162 {
    163   return is_flag_set (addr, PROT_WRITE);
    164 }
    165 
    166 PROTECTED int
    167 local_get_elf_image (struct elf_image *ei, unw_word_t ip,
    168                      unsigned long *segbase, unsigned long *mapoff, char **path)
    169 {
    170   struct map_info *map;
    171   intrmask_t saved_mask;
    172   int return_value = -UNW_ENOINFO;
    173 
    174   lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    175   map = map_find_from_addr (local_map_list, ip);
    176   if (!map)
    177     {
    178       lock_rdwr_release (&local_rdwr_lock, saved_mask);
    179       if (rebuild_if_necessary (ip, 0) < 0)
    180         return -UNW_ENOINFO;
    181 
    182       lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    183       map = map_find_from_addr (local_map_list, ip);
    184     }
    185 
    186   if (map && elf_map_cached_image (map, ip) == 0)
    187     {
    188       *ei = map->ei;
    189       *segbase = map->start;
    190       *mapoff = map->offset;
    191       if (path != NULL)
    192         {
    193           if (map->path)
    194             *path = strdup(map->path);
    195           else
    196             *path = NULL;
    197         }
    198       return_value = 0;
    199     }
    200   lock_rdwr_release (&local_rdwr_lock, saved_mask);
    201 
    202   return return_value;
    203 }
    204 
    205 PROTECTED char *
    206 map_local_get_image_name (unw_word_t ip)
    207 {
    208   struct map_info *map;
    209   intrmask_t saved_mask;
    210   char *image_name = NULL;
    211 
    212   lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    213   map = map_find_from_addr (local_map_list, ip);
    214   if (!map)
    215     {
    216       lock_rdwr_release (&local_rdwr_lock, saved_mask);
    217       if (rebuild_if_necessary (ip, 0) < 0)
    218         return NULL;
    219 
    220       lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask);
    221       map = map_find_from_addr (local_map_list, ip);
    222     }
    223   if (map)
    224     image_name = strdup (map->path);
    225   lock_rdwr_release (&local_rdwr_lock, saved_mask);
    226 
    227   return image_name;
    228 }
    229