• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2008 CodeSourcery
3    Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
4    Copyright (C) 2013 Linaro Limited
5 
6 This file is part of libunwind.
7 
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15 
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26 
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/syscall.h>
32 #include <stdatomic.h>
33 
34 #include "unwind_i.h"
35 
36 #ifdef UNW_REMOTE_ONLY
37 
38 /* unw_local_addr_space is a NULL pointer in this case.  */
39 unw_addr_space_t unw_local_addr_space;
40 
41 #else /* !UNW_REMOTE_ONLY */
42 
43 #ifndef NO_RESERVE_CACHE
44 static struct unw_addr_space local_addr_space;
45 
46 unw_addr_space_t unw_local_addr_space = &local_addr_space;
47 #else
48 unw_addr_space_t unw_local_addr_space;
49 #endif
50 
51 static inline void *
uc_addr(unw_tdep_context_t * uc,int reg)52 uc_addr (unw_tdep_context_t *uc, int reg)
53 {
54   if (reg >= UNW_AARCH64_X0 && reg <= UNW_AARCH64_X30)
55     return &uc->uc_mcontext.regs[reg];
56   else if (reg == UNW_AARCH64_SP)
57     return &uc->uc_mcontext.sp;
58   else if (reg == UNW_AARCH64_PC)
59     return &uc->uc_mcontext.pc;
60   else if (reg >= UNW_AARCH64_V0 && reg <= UNW_AARCH64_V31)
61     return &GET_FPCTX(uc)->vregs[reg - UNW_AARCH64_V0];
62   else
63     return NULL;
64 }
65 
66 # ifdef UNW_LOCAL_ONLY
67 
68 HIDDEN void *
tdep_uc_addr(unw_tdep_context_t * uc,int reg)69 tdep_uc_addr (unw_tdep_context_t *uc, int reg)
70 {
71   return uc_addr (uc, reg);
72 }
73 
74 # endif /* UNW_LOCAL_ONLY */
75 
76 static void
put_unwind_info(unw_addr_space_t as,unw_proc_info_t * proc_info,void * arg)77 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
78 {
79   /* it's a no-op */
80 }
81 
82 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)83 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
84                         void *arg)
85 {
86 #ifndef UNW_LOCAL_ONLY
87 # pragma weak _U_dyn_info_list_addr
88   if (!_U_dyn_info_list_addr)
89     return -UNW_ENOINFO;
90 #endif
91   // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
92   *dyn_info_list_addr = _U_dyn_info_list_addr ();
93   return 0;
94 }
95 
96 #define PAGE_SIZE 4096
97 #define PAGE_START(a)   ((a) & ~(PAGE_SIZE-1))
98 
99 static int mem_validate_pipe[2] = {-1, -1};
100 
101 #ifdef HAVE_PIPE2
102 static inline void
do_pipe2(int pipefd[2])103 do_pipe2 (int pipefd[2])
104 {
105   pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK);
106 }
107 #else
108 static inline void
set_pipe_flags(int fd)109 set_pipe_flags (int fd)
110 {
111   int fd_flags = fcntl (fd, F_GETFD, 0);
112   int status_flags = fcntl (fd, F_GETFL, 0);
113 
114   fd_flags |= FD_CLOEXEC;
115   fcntl (fd, F_SETFD, fd_flags);
116 
117   status_flags |= O_NONBLOCK;
118   fcntl (fd, F_SETFL, status_flags);
119 }
120 
121 static inline void
do_pipe2(int pipefd[2])122 do_pipe2 (int pipefd[2])
123 {
124   pipe (pipefd);
125   set_pipe_flags(pipefd[0]);
126   set_pipe_flags(pipefd[1]);
127 }
128 #endif
129 
130 static inline void
open_pipe(void)131 open_pipe (void)
132 {
133   if (mem_validate_pipe[0] != -1)
134     close (mem_validate_pipe[0]);
135   if (mem_validate_pipe[1] != -1)
136     close (mem_validate_pipe[1]);
137 
138   do_pipe2 (mem_validate_pipe);
139 }
140 
141 ALWAYS_INLINE
142 static int
143 #if defined(__has_feature)
144 #if __has_feature(address_sanitizer)
145 __attribute__((no_sanitize("address")))
146 #endif
147 #endif
write_validate(void * addr)148 write_validate (void *addr)
149 {
150   int ret = -1;
151   ssize_t bytes = 0;
152 
153   do
154     {
155       char buf;
156       bytes = syscall(SYS_read, mem_validate_pipe[0], &buf, 1);
157     }
158   while ( errno == EINTR );
159 
160   int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK);
161   if (!valid_read)
162     {
163       // re-open closed pipe
164       open_pipe ();
165     }
166 
167   do
168     {
169        ret = syscall(SYS_write, mem_validate_pipe[1], addr, 1);
170     }
171   while ( errno == EINTR );
172 
173   return ret;
174 }
175 
176 static int (*mem_validate_func) (void *addr, size_t len);
msync_validate(void * addr,size_t len)177 static int msync_validate (void *addr, size_t len)
178 {
179   if (msync (addr, len, MS_ASYNC) != 0)
180     {
181       return -1;
182     }
183 
184   return write_validate (addr);
185 }
186 
187 #ifdef HAVE_MINCORE
mincore_validate(void * addr,size_t len)188 static int mincore_validate (void *addr, size_t len)
189 {
190   unsigned char mvec[2]; /* Unaligned access may cross page boundary */
191 
192   /* mincore could fail with EAGAIN but we conservatively return -1
193      instead of looping. */
194   if (mincore (addr, len, (unsigned char *)mvec) != 0)
195     {
196       return -1;
197     }
198 
199   return write_validate (addr);
200 }
201 #endif
202 
203 /* Initialise memory validation method. On linux kernels <2.6.21,
204    mincore() returns incorrect value for MAP_PRIVATE mappings,
205    such as stacks. If mincore() was available at compile time,
206    check if we can actually use it. If not, use msync() instead. */
207 HIDDEN void
tdep_init_mem_validate(void)208 tdep_init_mem_validate (void)
209 {
210   open_pipe ();
211 
212 #ifdef HAVE_MINCORE
213   unsigned char present = 1;
214   unw_word_t addr = PAGE_START((unw_word_t)&present);
215   unsigned char mvec[1];
216   int ret;
217   while ((ret = mincore ((void*)addr, PAGE_SIZE, (unsigned char *)mvec)) == -1 &&
218          errno == EAGAIN) {}
219   if (ret == 0)
220     {
221       Debug(1, "using mincore to validate memory\n");
222       mem_validate_func = mincore_validate;
223     }
224   else
225 #endif
226     {
227       Debug(1, "using msync to validate memory\n");
228       mem_validate_func = msync_validate;
229     }
230 }
231 
232 /* Cache of already validated addresses */
233 #define NLGA 4
234 #if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD
235 // thread-local variant
236 static _Thread_local unw_word_t last_good_addr[NLGA];
237 static _Thread_local int lga_victim;
238 
239 static int
is_cached_valid_mem(unw_word_t addr)240 is_cached_valid_mem(unw_word_t addr)
241 {
242   int i;
243   for (i = 0; i < NLGA; i++)
244     {
245       if (addr == last_good_addr[i])
246         return 1;
247     }
248   return 0;
249 }
250 
251 static void
cache_valid_mem(unw_word_t addr)252 cache_valid_mem(unw_word_t addr)
253 {
254   int i, victim;
255   victim = lga_victim;
256   for (i = 0; i < NLGA; i++) {
257     if (last_good_addr[victim] == 0) {
258       last_good_addr[victim] = addr;
259       return;
260     }
261     victim = (victim + 1) % NLGA;
262   }
263 
264   /* All slots full. Evict the victim. */
265   last_good_addr[victim] = addr;
266   victim = (victim + 1) % NLGA;
267   lga_victim = victim;
268 }
269 
270 #else
271 // global, thread safe variant
272 static _Atomic unw_word_t last_good_addr[NLGA];
273 static _Atomic int lga_victim;
274 
275 static int
is_cached_valid_mem(unw_word_t addr)276 is_cached_valid_mem(unw_word_t addr)
277 {
278   int i;
279   for (i = 0; i < NLGA; i++)
280     {
281       if (addr == atomic_load(&last_good_addr[i]))
282         return 1;
283     }
284   return 0;
285 }
286 
287 static void
cache_valid_mem(unw_word_t addr)288 cache_valid_mem(unw_word_t addr)
289 {
290   int i, victim;
291   victim = atomic_load(&lga_victim);
292   unw_word_t zero = 0;
293   for (i = 0; i < NLGA; i++) {
294     if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) {
295       return;
296     }
297     victim = (victim + 1) % NLGA;
298   }
299 
300   /* All slots full. Evict the victim. */
301   atomic_store(&last_good_addr[victim], addr);
302   victim = (victim + 1) % NLGA;
303   atomic_store(&lga_victim, victim);
304 }
305 #endif
306 
307 static int
validate_mem(unw_word_t addr)308 validate_mem (unw_word_t addr)
309 {
310   size_t len;
311 
312   if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
313     len = PAGE_SIZE;
314   else
315     len = PAGE_SIZE * 2;
316 
317   addr = PAGE_START(addr);
318 
319   if (addr == 0)
320     return -1;
321 
322   if (is_cached_valid_mem(addr))
323     return 0;
324 
325   if (mem_validate_func ((void *) addr, len) == -1)
326     return -1;
327 
328   cache_valid_mem(addr);
329 
330   return 0;
331 }
332 
333 static int
334 #if defined(__has_feature)
335 #if __has_feature(address_sanitizer)
336 __attribute__((no_sanitize("address")))
337 #endif
338 #endif
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)339 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
340             void *arg)
341 {
342   if (unlikely (write))
343     {
344       Debug (16, "mem[%lx] <- %lx\n", addr, *val);
345       *(unw_word_t *) addr = *val;
346     }
347   else
348     {
349       /* validate address */
350       const struct cursor *c = (const struct cursor *)arg;
351       if (likely (c != NULL) && unlikely (c->validate)
352           && unlikely (validate_mem (addr))) {
353         Debug (16, "mem[%016lx] -> invalid\n", addr);
354         return -1;
355       }
356       *val = *(unw_word_t *) addr;
357       Debug (16, "mem[%lx] -> %lx\n", addr, *val);
358     }
359   return 0;
360 }
361 
362 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)363 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
364             void *arg)
365 {
366   unw_word_t *addr;
367   unw_tdep_context_t *uc = ((struct cursor *)arg)->uc;
368 
369   if (unw_is_fpreg (reg))
370     goto badreg;
371 
372   if (!(addr = uc_addr (uc, reg)))
373     goto badreg;
374 
375   if (write)
376     {
377       *(unw_word_t *) addr = *val;
378       Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
379     }
380   else
381     {
382       *val = *(unw_word_t *) addr;
383       Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
384     }
385   return 0;
386 
387  badreg:
388   Debug (1, "bad register number %u\n", reg);
389   return -UNW_EBADREG;
390 }
391 
392 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)393 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
394               int write, void *arg)
395 {
396   unw_tdep_context_t *uc = ((struct cursor *)arg)->uc;
397   unw_fpreg_t *addr;
398 
399   if (!unw_is_fpreg (reg))
400     goto badreg;
401 
402   if (!(addr = uc_addr (uc, reg)))
403     goto badreg;
404 
405   if (write)
406     {
407       Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
408              ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
409       *(unw_fpreg_t *) addr = *val;
410     }
411   else
412     {
413       *val = *(unw_fpreg_t *) addr;
414       Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
415              ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
416     }
417   return 0;
418 
419  badreg:
420   Debug (1, "bad register number %u\n", reg);
421   /* attempt to access a non-preserved register */
422   return -UNW_EBADREG;
423 }
424 
425 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)426 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
427                       char *buf, size_t buf_len, unw_word_t *offp,
428                       void *arg)
429 {
430   return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
431 }
432 
433 HIDDEN void
aarch64_local_addr_space_init(void)434 aarch64_local_addr_space_init (void)
435 {
436 #ifndef NO_RESERVE_CACHE
437   memset (&local_addr_space, 0, sizeof (local_addr_space));
438   local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY;
439   local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
440   local_addr_space.acc.put_unwind_info = put_unwind_info;
441   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
442   local_addr_space.acc.access_mem = access_mem;
443   local_addr_space.acc.access_reg = access_reg;
444   local_addr_space.acc.access_fpreg = access_fpreg;
445   local_addr_space.acc.resume = aarch64_local_resume;
446   local_addr_space.acc.get_proc_name = get_static_proc_name;
447   local_addr_space.big_endian = target_is_big_endian();
448   unw_flush_cache (&local_addr_space, 0, 0);
449 #endif
450 }
451 
452 HIDDEN void
init_local_addr_space(unw_addr_space_t as)453 init_local_addr_space (unw_addr_space_t as)
454 {
455   memset (as, 0, sizeof (struct unw_addr_space));
456   as->caching_policy = UNW_CACHE_NONE;
457   as->acc.find_proc_info = dwarf_find_proc_info;
458   as->acc.put_unwind_info = put_unwind_info;
459   as->acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
460   as->acc.access_mem = access_mem;
461   as->acc.access_reg = access_reg;
462   as->acc.access_fpreg = access_fpreg;
463   as->acc.resume = aarch64_local_resume;
464   as->acc.get_proc_name = get_static_proc_name;
465   as->big_endian = target_is_big_endian();
466   unw_flush_cache (as, 0, 0);
467 }
468 
469 #endif /* !UNW_REMOTE_ONLY */
470