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