static int is_flag_set (unw_word_t addr, int flag, size_t bytes) { struct map_info *map; int ret = 0; intrmask_t saved_mask; lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask); map = map_find_from_addr (local_map_list, addr); if (map != NULL) { if (map->flags & MAP_FLAGS_DEVICE_MEM) { lock_rdwr_release (&local_rdwr_lock, saved_mask); return 0; } /* Do not bother checking if the next map is readable and right at * the end of this map. All of the reads/writes are of small values * that should never span a map. */ if (map->end - addr < bytes) ret = 0; else ret = map->flags & flag; } lock_rdwr_release (&local_rdwr_lock, saved_mask); if (!ret && rebuild_if_necessary (addr, flag, bytes) == 0) { return 1; } return ret; }
PROTECTED int local_get_elf_image (unw_addr_space_t as, struct elf_image *ei, unw_word_t ip, unsigned long *segbase, unsigned long *mapoff, char **path, void *as_arg) { struct map_info *map; intrmask_t saved_mask; int return_value = -UNW_ENOINFO; lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask); map = map_find_from_addr (local_map_list, ip); if (!map) { lock_rdwr_release (&local_rdwr_lock, saved_mask); if (rebuild_if_necessary (ip, 0, sizeof(unw_word_t)) < 0) return -UNW_ENOINFO; lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask); map = map_find_from_addr (local_map_list, ip); } if (map && elf_map_cached_image (as, as_arg, map, ip, true)) { /* It is absolutely necessary that the elf structure is a copy of * the map data. The map could be rebuilt and the old ei pointer * will be modified and thrown away. */ *ei = map->ei; *segbase = map->start; if (ei->mapped) *mapoff = map->offset; else /* Always use zero as the map offset for in memory maps. The * dlopen of a shared library from an APK will result in a * non-zero offset so it won't match the elf data and cause * unwinds to fail. Currently, only in memory unwinds of an APK * are possible, so only modify this path. */ *mapoff = 0; if (path != NULL) { if (map->path) *path = strdup(map->path); else *path = NULL; } return_value = 0; } lock_rdwr_release (&local_rdwr_lock, saved_mask); return return_value; }
PROTECTED int unw_map_local_cursor_get_next (unw_map_cursor_t *map_cursor, unw_map_t *unw_map) { struct map_info *map_info = map_cursor->cur_map; intrmask_t saved_mask; int ret = 1; if (map_info == NULL) return 0; lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask); if (map_cursor->map_list != local_map_list) { map_cursor->map_list = local_map_list; ret = -UNW_EINVAL; } else { unw_map->start = map_info->start; unw_map->end = map_info->end; unw_map->flags = map_info->flags; if (map_info->path) unw_map->path = strdup (map_info->path); else unw_map->path = NULL; map_cursor->cur_map = map_info->next; } lock_rdwr_release (&local_rdwr_lock, saved_mask); return ret; }
PROTECTED void unw_map_local_cursor_get (unw_map_cursor_t *map_cursor) { intrmask_t saved_mask; lock_rdwr_wr_acquire (&local_rdwr_lock, saved_mask); map_cursor->map_list = local_map_list; map_cursor->cur_map = local_map_list; lock_rdwr_release (&local_rdwr_lock, saved_mask); }
PROTECTED void unw_map_local_destroy (void) { intrmask_t saved_mask; lock_rdwr_wr_acquire (&local_rdwr_lock, saved_mask); if (local_map_list != NULL && --local_map_list_refs == 0) { map_destroy_list (local_map_list); local_map_list = NULL; } lock_rdwr_release (&local_rdwr_lock, saved_mask); }
PROTECTED char * map_local_get_image_name (unw_word_t ip) { struct map_info *map; intrmask_t saved_mask; char *image_name = NULL; lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask); map = map_find_from_addr (local_map_list, ip); if (!map) { lock_rdwr_release (&local_rdwr_lock, saved_mask); if (rebuild_if_necessary (ip, 0, sizeof(unw_word_t)) < 0) return NULL; lock_rdwr_rd_acquire (&local_rdwr_lock, saved_mask); map = map_find_from_addr (local_map_list, ip); } if (map) image_name = strdup (map->path); lock_rdwr_release (&local_rdwr_lock, saved_mask); return image_name; }
PROTECTED int unw_map_local_create (void) { intrmask_t saved_mask; int ret_value = 0; lock_rdwr_wr_acquire (&local_rdwr_lock, saved_mask); if (local_map_list_refs == 0) { local_map_list = map_create_list (getpid()); if (local_map_list != NULL) local_map_list_refs = 1; else ret_value = -1; } else local_map_list_refs++; lock_rdwr_release (&local_rdwr_lock, saved_mask); return ret_value; }
/* In order to cache as much as possible while unwinding the local process, we gather a map of the process before starting. If the cache is missing a map, or a map exists but doesn't have the "expected_flags" set, then check if the cache needs to be regenerated. While regenerating the list, grab a write lock to avoid any readers using the list while it's being modified. */ static int rebuild_if_necessary (unw_word_t addr, int expected_flags, size_t bytes) { struct map_info *map; struct map_info *new_list; int ret_value = -1; intrmask_t saved_mask; new_list = map_create_list (UNW_MAP_CREATE_LOCAL, getpid()); map = map_find_from_addr (new_list, addr); if (map && (map->end - addr >= bytes) && (expected_flags == 0 || (map->flags & expected_flags))) { /* Get a write lock on local_map_list since it's going to be modified. */ lock_rdwr_wr_acquire (&local_rdwr_lock, saved_mask); /* Just in case another thread rebuilt the map, check to see if the ip with expected_flags is in local_map_list. If not, the assumption is that new_list is newer than local_map_list because the map only gets new maps with new permissions. If this is not true, then it would be necessary to regenerate the list one more time. */ ret_value = 0; map = map_find_from_addr (local_map_list, addr); if (!map || (map->end - addr < bytes) || (expected_flags != 0 && !(map->flags & expected_flags))) { /* Move any cached items to the new list. */ move_cached_elf_data (local_map_list, new_list); map = local_map_list; local_map_list = new_list; new_list = map; } lock_rdwr_release (&local_rdwr_lock, saved_mask); } map_destroy_list (new_list); return ret_value; }