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