• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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