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