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