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