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