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 ®s[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 ®s[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