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