• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2008 CodeSourcery
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 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/mman.h>
29 #include <sys/syscall.h>
30 #include <stdatomic.h>
31 
32 #include "unwind_i.h"
33 
34 #ifdef UNW_REMOTE_ONLY
35 
36 /* unw_local_addr_space is a NULL pointer in this case.  */
37 unw_addr_space_t unw_local_addr_space;
38 
39 #else /* !UNW_REMOTE_ONLY */
40 #ifndef NO_RESERVE_CACHE
41 static struct unw_addr_space local_addr_space;
42 
43 unw_addr_space_t unw_local_addr_space = &local_addr_space;
44 #else
45 unw_addr_space_t unw_local_addr_space;
46 #endif
47 
48 static inline void *
uc_addr(unw_tdep_context_t * uc,int reg)49 uc_addr (unw_tdep_context_t *uc, int reg)
50 {
51   if (reg >= UNW_ARM_R0 && reg < UNW_ARM_R0 + 16)
52     return &uc->regs[reg - UNW_ARM_R0];
53   else
54     return NULL;
55 }
56 
57 # ifdef UNW_LOCAL_ONLY
58 
59 HIDDEN void *
tdep_uc_addr(unw_tdep_context_t * uc,int reg)60 tdep_uc_addr (unw_tdep_context_t *uc, int reg)
61 {
62   return uc_addr (uc, reg);
63 }
64 
65 # endif /* UNW_LOCAL_ONLY */
66 
67 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)68 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
69                         void *arg)
70 {
71 #ifndef UNW_LOCAL_ONLY
72 # pragma weak _U_dyn_info_list_addr
73   if (!_U_dyn_info_list_addr)
74     return -UNW_ENOINFO;
75 #endif
76   // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
77   *dyn_info_list_addr = _U_dyn_info_list_addr ();
78   return 0;
79 }
80 
81 #define PAGE_SIZE 4096
82 #define PAGE_START(a)	((a) & ~(PAGE_SIZE-1))
83 
84 static int mem_validate_pipe[2] = {-1, -1};
85 
86 #ifdef HAVE_PIPE2
87 static inline void
do_pipe2(int pipefd[2])88 do_pipe2 (int pipefd[2])
89 {
90   pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK);
91 }
92 #else
93 static inline void
set_pipe_flags(int fd)94 set_pipe_flags (int fd)
95 {
96   int fd_flags = fcntl (fd, F_GETFD, 0);
97   int status_flags = fcntl (fd, F_GETFL, 0);
98 
99   fd_flags |= FD_CLOEXEC;
100   fcntl (fd, F_SETFD, fd_flags);
101 
102   status_flags |= O_NONBLOCK;
103   fcntl (fd, F_SETFL, status_flags);
104 }
105 
106 static inline void
do_pipe2(int pipefd[2])107 do_pipe2 (int pipefd[2])
108 {
109   pipe (pipefd);
110   set_pipe_flags(pipefd[0]);
111   set_pipe_flags(pipefd[1]);
112 }
113 #endif
114 
115 static inline void
open_pipe(void)116 open_pipe (void)
117 {
118   if (mem_validate_pipe[0] != -1)
119     close (mem_validate_pipe[0]);
120   if (mem_validate_pipe[1] != -1)
121     close (mem_validate_pipe[1]);
122 
123   do_pipe2 (mem_validate_pipe);
124 }
125 
126 ALWAYS_INLINE
127 static int
128 #if defined(__has_feature)
129 #if __has_feature(address_sanitizer)
130 __attribute__((no_sanitize("address")))
131 #endif
132 #endif
write_validate(void * addr)133 write_validate (void *addr)
134 {
135   int ret = -1;
136   ssize_t bytes = 0;
137 
138   do
139     {
140       char buf;
141       bytes = syscall(SYS_read, mem_validate_pipe[0], &buf, 1);
142     }
143   while ( errno == EINTR );
144 
145   int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK);
146   if (!valid_read)
147     {
148       // re-open closed pipe
149       open_pipe ();
150     }
151 
152   do
153     {
154        ret = syscall(SYS_write, mem_validate_pipe[1], addr, 1);
155     }
156   while ( errno == EINTR );
157 
158   return ret;
159 }
160 
161 static int (*mem_validate_func) (void *addr, size_t len);
msync_validate(void * addr,size_t len)162 static int msync_validate (void *addr, size_t len)
163 {
164   if (msync (addr, len, MS_ASYNC) != 0)
165     {
166       return -1;
167     }
168 
169   return write_validate (addr);
170 }
171 
172 #ifdef HAVE_MINCORE
mincore_validate(void * addr,size_t len)173 static int mincore_validate (void *addr, size_t len)
174 {
175   unsigned char mvec[2]; /* Unaligned access may cross page boundary */
176 
177   /* mincore could fail with EAGAIN but we conservatively return -1
178      instead of looping. */
179   if (mincore (addr, len, (unsigned char *)mvec) != 0)
180     {
181     return -1;
182     }
183 
184   return write_validate (addr);
185 }
186 #endif
187 
188 /* Initialise memory validation method. On linux kernels <2.6.21,
189    mincore() returns incorrect value for MAP_PRIVATE mappings,
190    such as stacks. If mincore() was available at compile time,
191    check if we can actually use it. If not, use msync() instead. */
192 HIDDEN void
tdep_init_mem_validate(void)193 tdep_init_mem_validate (void)
194 {
195   open_pipe ();
196 
197 #ifdef HAVE_MINCORE
198   unsigned char present = 1;
199   unw_word_t addr = PAGE_START((unw_word_t)&present);
200   unsigned char mvec[1];
201   int ret;
202   while ((ret = mincore ((void*)addr, PAGE_SIZE, (unsigned char *)mvec)) == -1 &&
203          errno == EAGAIN) {}
204   if (ret == 0)
205     {
206       Debug(1, "using mincore to validate memory\n");
207       mem_validate_func = mincore_validate;
208     }
209   else
210 #endif
211     {
212       Debug(1, "using msync to validate memory\n");
213       mem_validate_func = msync_validate;
214     }
215 }
216 
217 /* Cache of already validated addresses */
218 #define NLGA 4
219 #if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD
220 // thread-local variant
221 static _Thread_local unw_word_t last_good_addr[NLGA];
222 static _Thread_local int lga_victim;
223 
224 static int
is_cached_valid_mem(unw_word_t addr)225 is_cached_valid_mem(unw_word_t addr)
226 {
227   int i;
228   for (i = 0; i < NLGA; i++)
229     {
230       if (addr == last_good_addr[i])
231         return 1;
232     }
233       return 0;
234     }
235 
236 static void
cache_valid_mem(unw_word_t addr)237 cache_valid_mem(unw_word_t addr)
238 {
239   int i, victim;
240   victim = lga_victim;
241   for (i = 0; i < NLGA; i++) {
242     if (last_good_addr[victim] == 0) {
243       last_good_addr[victim] = addr;
244       return;
245     }
246     victim = (victim + 1) % NLGA;
247   }
248 
249   /* All slots full. Evict the victim. */
250   last_good_addr[victim] = addr;
251   victim = (victim + 1) % NLGA;
252   lga_victim = victim;
253 }
254 
255 #else
256 // global, thread safe variant
257 static _Atomic unw_word_t last_good_addr[NLGA];
258 static _Atomic int lga_victim;
259 
260 static int
is_cached_valid_mem(unw_word_t addr)261 is_cached_valid_mem(unw_word_t addr)
262 {
263   int i;
264   for (i = 0; i < NLGA; i++)
265     {
266       if (addr == atomic_load(&last_good_addr[i]))
267         return 1;
268     }
269   return 0;
270 }
271 
272 static void
cache_valid_mem(unw_word_t addr)273 cache_valid_mem(unw_word_t addr)
274 {
275   int i, victim;
276   victim = atomic_load(&lga_victim);
277   unw_word_t zero = 0;
278   for (i = 0; i < NLGA; i++) {
279     if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) {
280       return;
281     }
282     victim = (victim + 1) % NLGA;
283   }
284 
285   /* All slots full. Evict the victim. */
286   atomic_store(&last_good_addr[victim], addr);
287     victim = (victim + 1) % NLGA;
288   atomic_store(&lga_victim, victim);
289 }
290 #endif
291 
292 static int
validate_mem(unw_word_t addr)293 validate_mem (unw_word_t addr)
294 {
295   size_t len;
296 
297   if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
298     len = PAGE_SIZE;
299   else
300     len = PAGE_SIZE * 2;
301 
302   addr = PAGE_START(addr);
303 
304   if (addr == 0)
305       return -1;
306 
307   if (is_cached_valid_mem(addr))
308     return 0;
309 
310   if (mem_validate_func ((void *) addr, len) == -1)
311     return -1;
312 
313   cache_valid_mem(addr);
314 
315   return 0;
316 }
317 
318 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)319 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
320             void *arg)
321 {
322   /* validate address */
323     const struct cursor *c = (const struct cursor *) arg;
324     if (c && validate_mem(addr))
325       return -1;
326 
327   if (write)
328     {
329       Debug (16, "mem[%x] <- %x\n", addr, *val);
330       *(unw_word_t *) addr = *val;
331     }
332   else
333     {
334       *val = *(unw_word_t *) addr;
335       Debug (16, "mem[%x] -> %x\n", addr, *val);
336     }
337   return 0;
338 }
339 
340 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)341 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
342             void *arg)
343 {
344   unw_word_t *addr;
345   unw_tdep_context_t *uc = arg;
346 
347   if (unw_is_fpreg (reg))
348     goto badreg;
349 
350 Debug (16, "reg = %s\n", unw_regname (reg));
351   if (!(addr = uc_addr (uc, reg)))
352     goto badreg;
353 
354   if (write)
355     {
356       *(unw_word_t *) addr = *val;
357       Debug (12, "%s <- %x\n", unw_regname (reg), *val);
358     }
359   else
360     {
361       *val = *(unw_word_t *) addr;
362       Debug (12, "%s -> %x\n", unw_regname (reg), *val);
363     }
364   return 0;
365 
366  badreg:
367   Debug (1, "bad register number %u\n", reg);
368   return -UNW_EBADREG;
369 }
370 
371 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)372 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
373               int write, void *arg)
374 {
375   unw_tdep_context_t *uc = arg;
376   unw_fpreg_t *addr;
377 
378   if (!unw_is_fpreg (reg))
379     goto badreg;
380 
381   if (!(addr = uc_addr (uc, reg)))
382     goto badreg;
383 
384   if (write)
385     {
386       Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
387              ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
388       *(unw_fpreg_t *) addr = *val;
389     }
390   else
391     {
392       *val = *(unw_fpreg_t *) addr;
393       Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
394              ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
395     }
396   return 0;
397 
398  badreg:
399   Debug (1, "bad register number %u\n", reg);
400   /* attempt to access a non-preserved register */
401   return -UNW_EBADREG;
402 }
403 
404 static int
get_static_proc_name(unw_addr_space_t as,unw_word_t ip,char * buf,size_t buf_len,unw_word_t * offp,void * arg)405 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
406                       char *buf, size_t buf_len, unw_word_t *offp,
407                       void *arg)
408 {
409   return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
410 }
411 
412 HIDDEN void
arm_local_addr_space_init(void)413 arm_local_addr_space_init (void)
414 {
415 #ifndef NO_RESERVE_CACHE
416   memset (&local_addr_space, 0, sizeof (local_addr_space));
417   local_addr_space.caching_policy = UNW_CACHE_NONE;
418   local_addr_space.acc.find_proc_info = arm_find_proc_info;
419   local_addr_space.acc.put_unwind_info = arm_put_unwind_info;
420   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
421   local_addr_space.acc.access_mem = access_mem;
422   local_addr_space.acc.access_reg = access_reg;
423   local_addr_space.acc.access_fpreg = access_fpreg;
424   local_addr_space.acc.resume = arm_local_resume;
425   local_addr_space.acc.get_proc_name = get_static_proc_name;
426   unw_flush_cache (&local_addr_space, 0, 0);
427 #endif
428 }
429 
430 HIDDEN void
init_local_addr_space(unw_addr_space_t as)431 init_local_addr_space (unw_addr_space_t as)
432 {
433   memset (as, 0, sizeof (struct unw_addr_space));
434   as->caching_policy = UNW_CACHE_NONE;
435   as->acc.find_proc_info = arm_find_proc_info;
436   as->acc.put_unwind_info = arm_put_unwind_info;
437   as->acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
438   as->acc.access_mem = access_mem;
439   as->acc.access_reg = access_reg;
440   as->acc.access_fpreg = access_fpreg;
441   as->acc.resume = arm_local_resume;
442   as->acc.get_proc_name = get_static_proc_name;
443   unw_flush_cache (as, 0, 0);
444 }
445 
446 #endif /* !UNW_REMOTE_ONLY */
447