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