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