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