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