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 Modified for s390x by Michael Munday <mike.munday@ibm.com>
8
9 This file is part of libunwind.
10
11 Permission is hereby granted, free of charge, to any person obtaining
12 a copy of this software and associated documentation files (the
13 "Software"), to deal in the Software without restriction, including
14 without limitation the rights to use, copy, modify, merge, publish,
15 distribute, sublicense, and/or sell copies of the Software, and to
16 permit persons to whom the Software is furnished to do so, subject to
17 the following conditions:
18
19 The above copyright notice and this permission notice shall be
20 included in all copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
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 #include <sys/syscall.h>
39
40 #include "unwind_i.h"
41
42 #ifdef UNW_REMOTE_ONLY
43
44 /* unw_local_addr_space is a NULL pointer in this case. */
45 unw_addr_space_t unw_local_addr_space;
46
47 #else /* !UNW_REMOTE_ONLY */
48
49 static struct unw_addr_space local_addr_space;
50
51 unw_addr_space_t unw_local_addr_space = &local_addr_space;
52
53 static inline void *
uc_addr(ucontext_t * uc,int reg)54 uc_addr (ucontext_t *uc, int reg)
55 {
56 if (reg >= UNW_S390X_R0 && reg <= UNW_S390X_R15)
57 return &uc->uc_mcontext.gregs[reg - UNW_S390X_R0];
58 if (reg >= UNW_S390X_F0 && reg <= UNW_S390X_F15)
59 return &uc->uc_mcontext.fpregs.fprs[reg - UNW_S390X_F0];
60 if (reg == UNW_S390X_IP)
61 return &uc->uc_mcontext.psw.addr;
62
63 return NULL;
64 }
65
66 # ifdef UNW_LOCAL_ONLY
67
68 HIDDEN void *
tdep_uc_addr(ucontext_t * uc,int reg)69 tdep_uc_addr (ucontext_t *uc, int reg)
70 {
71 return uc_addr (uc, reg);
72 }
73
74 # endif /* UNW_LOCAL_ONLY */
75
76 static void
put_unwind_info(unw_addr_space_t as,unw_proc_info_t * proc_info,void * arg)77 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
78 {
79 /* it's a no-op */
80 }
81
82 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)83 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
84 void *arg)
85 {
86 #ifndef UNW_LOCAL_ONLY
87 # pragma weak _U_dyn_info_list_addr
88 if (!_U_dyn_info_list_addr)
89 return -UNW_ENOINFO;
90 #endif
91 // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
92 *dyn_info_list_addr = _U_dyn_info_list_addr ();
93 return 0;
94 }
95
96 #define PAGE_SIZE 4096
97 #define PAGE_START(a) ((a) & ~(PAGE_SIZE-1))
98
99 static int mem_validate_pipe[2] = {-1, -1};
100
101 static inline void
open_pipe(void)102 open_pipe (void)
103 {
104 /* ignore errors for closing invalid fd's */
105 close (mem_validate_pipe[0]);
106 close (mem_validate_pipe[1]);
107
108 pipe2 (mem_validate_pipe, O_CLOEXEC | O_NONBLOCK);
109 }
110
111 ALWAYS_INLINE
112 static int
write_validate(void * addr)113 write_validate (void *addr)
114 {
115 int ret = -1;
116 ssize_t bytes = 0;
117
118 do
119 {
120 char buf;
121 bytes = read (mem_validate_pipe[0], &buf, 1);
122 }
123 while ( errno == EINTR );
124
125 int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK);
126 if (!valid_read)
127 {
128 // re-open closed pipe
129 open_pipe ();
130 }
131
132 do
133 {
134 /* use syscall insteadof write() so that ASAN does not complain */
135 ret = syscall (SYS_write, mem_validate_pipe[1], addr, 1);
136 }
137 while ( errno == EINTR );
138
139 return ret;
140 }
141
142 static int (*mem_validate_func) (void *addr, size_t len);
msync_validate(void * addr,size_t len)143 static int msync_validate (void *addr, size_t len)
144 {
145 if (msync (addr, len, MS_ASYNC) != 0)
146 {
147 return -1;
148 }
149
150 return write_validate (addr);
151 }
152
153 #ifdef HAVE_MINCORE
mincore_validate(void * addr,size_t len)154 static int mincore_validate (void *addr, size_t len)
155 {
156 unsigned char mvec[2]; /* Unaligned access may cross page boundary */
157 size_t i;
158
159 /* mincore could fail with EAGAIN but we conservatively return -1
160 instead of looping. */
161 if (mincore (addr, len, mvec) != 0)
162 {
163 return -1;
164 }
165
166 for (i = 0; i < (len + PAGE_SIZE - 1) / PAGE_SIZE; i++)
167 {
168 if (!(mvec[i] & 1)) return -1;
169 }
170
171 return write_validate (addr);
172 }
173 #endif
174
175 /* Initialise memory validation method. On linux kernels <2.6.21,
176 mincore() returns incorrect value for MAP_PRIVATE mappings,
177 such as stacks. If mincore() was available at compile time,
178 check if we can actually use it. If not, use msync() instead. */
179 HIDDEN void
tdep_init_mem_validate(void)180 tdep_init_mem_validate (void)
181 {
182 open_pipe ();
183
184 #ifdef HAVE_MINCORE
185 unsigned char present = 1;
186 unw_word_t addr = PAGE_START((unw_word_t)&present);
187 unsigned char mvec[1];
188 int ret;
189 while ((ret = mincore ((void*)addr, PAGE_SIZE, mvec)) == -1 &&
190 errno == EAGAIN) {}
191 if (ret == 0 && (mvec[0] & 1))
192 {
193 Debug(1, "using mincore to validate memory\n");
194 mem_validate_func = mincore_validate;
195 }
196 else
197 #endif
198 {
199 Debug(1, "using msync to validate memory\n");
200 mem_validate_func = msync_validate;
201 }
202 }
203
204 /* Cache of already validated addresses */
205 #define NLGA 4
206 static unw_word_t last_good_addr[NLGA];
207 static int lga_victim;
208
209 static int
validate_mem(unw_word_t addr)210 validate_mem (unw_word_t addr)
211 {
212 int i, victim;
213 size_t len;
214
215 if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
216 len = PAGE_SIZE;
217 else
218 len = PAGE_SIZE * 2;
219
220 addr = PAGE_START(addr);
221
222 if (addr == 0)
223 return -1;
224
225 for (i = 0; i < NLGA; i++)
226 {
227 if (last_good_addr[i] && (addr == last_good_addr[i]))
228 return 0;
229 }
230
231 if (mem_validate_func ((void *) addr, len) == -1)
232 return -1;
233
234 victim = lga_victim;
235 for (i = 0; i < NLGA; i++) {
236 if (!last_good_addr[victim]) {
237 last_good_addr[victim++] = addr;
238 return 0;
239 }
240 victim = (victim + 1) % NLGA;
241 }
242
243 /* All slots full. Evict the victim. */
244 last_good_addr[victim] = addr;
245 victim = (victim + 1) % NLGA;
246 lga_victim = victim;
247
248 return 0;
249 }
250
251 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)252 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
253 void *arg)
254 {
255 if (unlikely (write))
256 {
257 Debug (16, "mem[%016lx] <- %lx\n", addr, *val);
258 *(unw_word_t *) addr = *val;
259 }
260 else
261 {
262 /* validate address */
263 const struct cursor *c = (const struct cursor *)arg;
264 if (likely (c != NULL) && unlikely (c->validate)
265 && unlikely (validate_mem (addr))) {
266 Debug (16, "mem[%016lx] -> invalid\n", addr);
267 return -1;
268 }
269 *val = *(unw_word_t *) addr;
270 Debug (16, "mem[%016lx] -> %lx\n", addr, *val);
271 }
272 return 0;
273 }
274
275 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)276 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
277 void *arg)
278 {
279 unw_word_t *addr;
280 ucontext_t *uc = ((struct cursor *)arg)->uc;
281
282 if (unw_is_fpreg (reg))
283 goto badreg;
284
285 if (!(addr = uc_addr (uc, reg)))
286 goto badreg;
287
288 if (write)
289 {
290 *(unw_word_t *) addr = *val;
291 Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val);
292 }
293 else
294 {
295 *val = *(unw_word_t *) addr;
296 Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val);
297 }
298 return 0;
299
300 badreg:
301 Debug (1, "bad register number %u\n", reg);
302 return -UNW_EBADREG;
303 }
304
305 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)306 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
307 int write, void *arg)
308 {
309 ucontext_t *uc = ((struct cursor *)arg)->uc;
310 unw_fpreg_t *addr;
311
312 if (!unw_is_fpreg (reg))
313 goto badreg;
314
315 if (!(addr = uc_addr (uc, reg)))
316 goto badreg;
317
318 if (write)
319 {
320 Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
321 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
322 *(unw_fpreg_t *) addr = *val;
323 }
324 else
325 {
326 *val = *(unw_fpreg_t *) addr;
327 Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
328 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
329 }
330 return 0;
331
332 badreg:
333 Debug (1, "bad register number %u\n", reg);
334 /* attempt to access a non-preserved register */
335 return -UNW_EBADREG;
336 }
337
338 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)339 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
340 char *buf, size_t buf_len, unw_word_t *offp,
341 void *arg)
342 {
343 return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
344 }
345
346 HIDDEN void
s390x_local_addr_space_init(void)347 s390x_local_addr_space_init (void)
348 {
349 memset (&local_addr_space, 0, sizeof (local_addr_space));
350 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
351 local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
352 local_addr_space.acc.put_unwind_info = put_unwind_info;
353 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
354 local_addr_space.acc.access_mem = access_mem;
355 local_addr_space.acc.access_reg = access_reg;
356 local_addr_space.acc.access_fpreg = access_fpreg;
357 local_addr_space.acc.resume = s390x_local_resume;
358 local_addr_space.acc.get_proc_name = get_static_proc_name;
359 unw_flush_cache (&local_addr_space, 0, 0);
360
361 memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
362 lga_victim = 0;
363 }
364
365 #endif /* !UNW_REMOTE_ONLY */
366