Imported Upstream version 1.3.1
[platform/upstream/libunwind.git] / src / x86_64 / Ginit.c
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 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/mman.h>
37 #include <sys/syscall.h>
38
39 #include "unwind_i.h"
40
41 #ifdef UNW_REMOTE_ONLY
42
43 /* unw_local_addr_space is a NULL pointer in this case.  */
44 unw_addr_space_t unw_local_addr_space;
45
46 #else /* !UNW_REMOTE_ONLY */
47
48 static struct unw_addr_space local_addr_space;
49
50 unw_addr_space_t unw_local_addr_space = &local_addr_space;
51
52 HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
53
54 /* XXX fix me: there is currently no way to locate the dyn-info list
55        by a remote unwinder.  On ia64, this is done via a special
56        unwind-table entry.  Perhaps something similar can be done with
57        DWARF2 unwind info.  */
58
59 static void
60 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
61 {
62   /* it's a no-op */
63 }
64
65 static int
66 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
67                         void *arg)
68 {
69   *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
70   return 0;
71 }
72
73 #define PAGE_SIZE 4096
74 #define PAGE_START(a)   ((a) & ~(PAGE_SIZE-1))
75
76 static int mem_validate_pipe[2] = {-1, -1};
77
78 static inline void
79 open_pipe (void)
80 {
81   /* ignore errors for closing invalid fd's */
82   close (mem_validate_pipe[0]);
83   close (mem_validate_pipe[1]);
84
85   pipe2 (mem_validate_pipe, O_CLOEXEC | O_NONBLOCK);
86 }
87
88 ALWAYS_INLINE
89 static int
90 write_validate (void *addr)
91 {
92   int ret = -1;
93   ssize_t bytes = 0;
94
95   do
96     {
97       char buf;
98       bytes = read (mem_validate_pipe[0], &buf, 1);
99     }
100   while ( errno == EINTR );
101
102   int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK);
103   if (!valid_read)
104     {
105       // re-open closed pipe
106       open_pipe ();
107     }
108
109   do
110     {
111       /* use syscall insteadof write() so that ASAN does not complain */
112       ret = syscall (SYS_write, mem_validate_pipe[1], addr, 1);
113     }
114   while ( errno == EINTR );
115
116   return ret;
117 }
118
119 static int (*mem_validate_func) (void *addr, size_t len);
120 static int msync_validate (void *addr, size_t len)
121 {
122   if (msync (addr, len, MS_ASYNC) != 0)
123     {
124       return -1;
125     }
126
127   return write_validate (addr);
128 }
129
130 #ifdef HAVE_MINCORE
131 static int mincore_validate (void *addr, size_t len)
132 {
133   unsigned char mvec[2]; /* Unaligned access may cross page boundary */
134   size_t i;
135
136   /* mincore could fail with EAGAIN but we conservatively return -1
137      instead of looping. */
138   if (mincore (addr, len, mvec) != 0)
139     {
140       return -1;
141     }
142
143   for (i = 0; i < (len + PAGE_SIZE - 1) / PAGE_SIZE; i++)
144     {
145       if (!(mvec[i] & 1)) return -1;
146     }
147
148   return write_validate (addr);
149 }
150 #endif
151
152 /* Initialise memory validation method. On linux kernels <2.6.21,
153    mincore() returns incorrect value for MAP_PRIVATE mappings,
154    such as stacks. If mincore() was available at compile time,
155    check if we can actually use it. If not, use msync() instead. */
156 HIDDEN void
157 tdep_init_mem_validate (void)
158 {
159   open_pipe ();
160
161 #ifdef HAVE_MINCORE
162   unsigned char present = 1;
163   unw_word_t addr = PAGE_START((unw_word_t)&present);
164   unsigned char mvec[1];
165   int ret;
166   while ((ret = mincore ((void*)addr, PAGE_SIZE, mvec)) == -1 &&
167          errno == EAGAIN) {}
168   if (ret == 0 && (mvec[0] & 1))
169     {
170       Debug(1, "using mincore to validate memory\n");
171       mem_validate_func = mincore_validate;
172     }
173   else
174 #endif
175     {
176       Debug(1, "using msync to validate memory\n");
177       mem_validate_func = msync_validate;
178     }
179 }
180
181 /* Cache of already validated addresses */
182 #define NLGA 4
183 static unw_word_t last_good_addr[NLGA];
184 static int lga_victim;
185
186 static int
187 validate_mem (unw_word_t addr)
188 {
189   int i, victim;
190   size_t len;
191
192   if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
193     len = PAGE_SIZE;
194   else
195     len = PAGE_SIZE * 2;
196
197   addr = PAGE_START(addr);
198
199   if (addr == 0)
200     return -1;
201
202   for (i = 0; i < NLGA; i++)
203     {
204       if (last_good_addr[i] && (addr == last_good_addr[i]))
205         return 0;
206     }
207
208   if (mem_validate_func ((void *) addr, len) == -1)
209     return -1;
210
211   victim = lga_victim;
212   for (i = 0; i < NLGA; i++) {
213     if (!last_good_addr[victim]) {
214       last_good_addr[victim++] = addr;
215       return 0;
216     }
217     victim = (victim + 1) % NLGA;
218   }
219
220   /* All slots full. Evict the victim. */
221   last_good_addr[victim] = addr;
222   victim = (victim + 1) % NLGA;
223   lga_victim = victim;
224
225   return 0;
226 }
227
228 static int
229 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
230             void *arg)
231 {
232   if (unlikely (write))
233     {
234       Debug (16, "mem[%016lx] <- %lx\n", addr, *val);
235       *(unw_word_t *) addr = *val;
236     }
237   else
238     {
239       /* validate address */
240       const struct cursor *c = (const struct cursor *)arg;
241       if (likely (c != NULL) && unlikely (c->validate)
242           && unlikely (validate_mem (addr))) {
243         Debug (16, "mem[%016lx] -> invalid\n", addr);
244         return -1;
245       }
246       *val = *(unw_word_t *) addr;
247       Debug (16, "mem[%016lx] -> %lx\n", addr, *val);
248     }
249   return 0;
250 }
251
252 static int
253 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
254             void *arg)
255 {
256   unw_word_t *addr;
257   ucontext_t *uc = ((struct cursor *)arg)->uc;
258
259   if (unw_is_fpreg (reg))
260     goto badreg;
261
262   if (!(addr = x86_64_r_uc_addr (uc, reg)))
263     goto badreg;
264
265   if (write)
266     {
267       *(unw_word_t *) addr = *val;
268       Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val);
269     }
270   else
271     {
272       *val = *(unw_word_t *) addr;
273       Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val);
274     }
275   return 0;
276
277  badreg:
278   Debug (1, "bad register number %u\n", reg);
279   return -UNW_EBADREG;
280 }
281
282 static int
283 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
284               int write, void *arg)
285 {
286   ucontext_t *uc = ((struct cursor *)arg)->uc;
287   unw_fpreg_t *addr;
288
289   if (!unw_is_fpreg (reg))
290     goto badreg;
291
292   if (!(addr = x86_64_r_uc_addr (uc, reg)))
293     goto badreg;
294
295   if (write)
296     {
297       Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
298              ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
299       *(unw_fpreg_t *) addr = *val;
300     }
301   else
302     {
303       *val = *(unw_fpreg_t *) addr;
304       Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
305              ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
306     }
307   return 0;
308
309  badreg:
310   Debug (1, "bad register number %u\n", reg);
311   /* attempt to access a non-preserved register */
312   return -UNW_EBADREG;
313 }
314
315 static int
316 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
317                       char *buf, size_t buf_len, unw_word_t *offp,
318                       void *arg)
319 {
320   return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
321 }
322
323 HIDDEN void
324 x86_64_local_addr_space_init (void)
325 {
326   memset (&local_addr_space, 0, sizeof (local_addr_space));
327   local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY;
328   local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
329   local_addr_space.acc.put_unwind_info = put_unwind_info;
330   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
331   local_addr_space.acc.access_mem = access_mem;
332   local_addr_space.acc.access_reg = access_reg;
333   local_addr_space.acc.access_fpreg = access_fpreg;
334   local_addr_space.acc.resume = x86_64_local_resume;
335   local_addr_space.acc.get_proc_name = get_static_proc_name;
336   unw_flush_cache (&local_addr_space, 0, 0);
337
338   memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
339   lga_victim = 0;
340 }
341
342 #endif /* !UNW_REMOTE_ONLY */