blob: b8890a3c6e85a23e321f41115573b4d0a95e40f6 [file] [log] [blame]
homeip.net!davidm588192d2004-08-17 15:34:28 +00001/* libunwind - a platform-independent unwind library
2 Copyright (C) 2002 Hewlett-Packard Co
David Mosberger-Tange6b9f352007-08-22 13:02:09 -06003 Copyright (C) 2007 David Mosberger-Tang
4 Contributed by David Mosberger-Tang <dmosberger@gmail.com>
homeip.net!davidm588192d2004-08-17 15:34:28 +00005
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
26
Konstantin Belousovee99dbe2010-04-20 17:45:18 +030027#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
homeip.net!davidm588192d2004-08-17 15:34:28 +000031#include <stdlib.h>
32#include <string.h>
33
34#include "unwind_i.h"
35
36#ifdef UNW_REMOTE_ONLY
37
38/* unw_local_addr_space is a NULL pointer in this case. */
39PROTECTED unw_addr_space_t unw_local_addr_space;
40
41#else /* !UNW_REMOTE_ONLY */
42
43static struct unw_addr_space local_addr_space;
44
45PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
46
homeip.net!davidm588192d2004-08-17 15:34:28 +000047# ifdef UNW_LOCAL_ONLY
48
49HIDDEN void *
50tdep_uc_addr (ucontext_t *uc, int reg)
51{
Konstantin Belousov0dbeeeb2010-04-05 22:42:23 +030052 return x86_r_uc_addr (uc, reg);
homeip.net!davidm588192d2004-08-17 15:34:28 +000053}
54
55# endif /* UNW_LOCAL_ONLY */
56
57HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
58
59/* XXX fix me: there is currently no way to locate the dyn-info list
60 by a remote unwinder. On ia64, this is done via a special
61 unwind-table entry. Perhaps something similar can be done with
62 DWARF2 unwind info. */
63
64static void
65put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
66{
67 /* it's a no-op */
68}
69
70static int
71get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
72 void *arg)
73{
74 *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
75 return 0;
76}
77
Arun Sharmaff0ae702009-03-16 11:06:26 -070078#define PAGE_SIZE 4096
79#define PAGE_START(a) ((a) & ~(PAGE_SIZE-1))
80
81/* Cache of already validated addresses */
82#define NLGA 4
83static unw_word_t last_good_addr[NLGA];
84static int lga_victim;
85
86static int
87validate_mem (unw_word_t addr)
88{
89 int i, victim;
Konstantin Belousovee99dbe2010-04-20 17:45:18 +030090#ifdef HAVE_MINCORE
Arun Sharma99e60be2010-06-08 14:44:07 -070091 unsigned char mvec[2]; /* Unaligned access may cross page boundary */
Konstantin Belousovee99dbe2010-04-20 17:45:18 +030092#endif
Arun Sharma99e60be2010-06-08 14:44:07 -070093 size_t len;
94
95 if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
96 len = PAGE_SIZE;
97 else
98 len = PAGE_SIZE * 2;
Arun Sharmaff0ae702009-03-16 11:06:26 -070099
100 addr = PAGE_START(addr);
101
Paul Pluzhnikov0cf76ed2009-12-01 13:59:45 -0800102 if (addr == 0)
103 return -1;
104
Arun Sharmaff0ae702009-03-16 11:06:26 -0700105 for (i = 0; i < NLGA; i++)
106 {
107 if (last_good_addr[i] && (addr == last_good_addr[i]))
108 return 0;
109 }
110
Konstantin Belousovee99dbe2010-04-20 17:45:18 +0300111#ifdef HAVE_MINCORE
Arun Sharma99e60be2010-06-08 14:44:07 -0700112 if (mincore ((void *) addr, len, mvec) == -1)
Konstantin Belousovee99dbe2010-04-20 17:45:18 +0300113#else
Arun Sharma99e60be2010-06-08 14:44:07 -0700114 if (msync ((void *) addr, len, MS_ASYNC) == -1)
Konstantin Belousovee99dbe2010-04-20 17:45:18 +0300115#endif
Arun Sharmaff0ae702009-03-16 11:06:26 -0700116 return -1;
117
118 victim = lga_victim;
119 for (i = 0; i < NLGA; i++) {
120 if (!last_good_addr[victim]) {
121 last_good_addr[victim++] = addr;
122 return 0;
123 }
124 victim = (victim + 1) % NLGA;
125 }
126
127 /* All slots full. Evict the victim. */
128 last_good_addr[victim] = addr;
129 victim = (victim + 1) % NLGA;
130 lga_victim = victim;
131
132 return 0;
133}
134
homeip.net!davidm588192d2004-08-17 15:34:28 +0000135static int
136access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
137 void *arg)
138{
139 if (write)
140 {
141 Debug (16, "mem[%x] <- %x\n", addr, *val);
142 *(unw_word_t *) addr = *val;
143 }
144 else
145 {
Arun Sharmaff0ae702009-03-16 11:06:26 -0700146 /* validate address */
147 const struct cursor *c = (const struct cursor *)arg;
148 if (c && c->validate && validate_mem(addr))
149 return -1;
homeip.net!davidm588192d2004-08-17 15:34:28 +0000150 *val = *(unw_word_t *) addr;
151 Debug (16, "mem[%x] -> %x\n", addr, *val);
152 }
153 return 0;
154}
155
156static int
157access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
158 void *arg)
159{
160 unw_word_t *addr;
Arun Sharmaff0ae702009-03-16 11:06:26 -0700161 ucontext_t *uc = ((struct cursor *)arg)->uc;
homeip.net!davidm588192d2004-08-17 15:34:28 +0000162
163 if (unw_is_fpreg (reg))
164 goto badreg;
165
Konstantin Belousov0dbeeeb2010-04-05 22:42:23 +0300166 if (!(addr = x86_r_uc_addr (uc, reg)))
homeip.net!davidm588192d2004-08-17 15:34:28 +0000167 goto badreg;
168
169 if (write)
170 {
171 *(unw_word_t *) addr = *val;
172 Debug (12, "%s <- %x\n", unw_regname (reg), *val);
173 }
174 else
175 {
176 *val = *(unw_word_t *) addr;
177 Debug (12, "%s -> %x\n", unw_regname (reg), *val);
178 }
179 return 0;
180
181 badreg:
182 Debug (1, "bad register number %u\n", reg);
183 return -UNW_EBADREG;
184}
185
186static int
187access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
188 int write, void *arg)
189{
Arun Sharmaff0ae702009-03-16 11:06:26 -0700190 ucontext_t *uc = ((struct cursor *)arg)->uc;
homeip.net!davidm588192d2004-08-17 15:34:28 +0000191 unw_fpreg_t *addr;
192
193 if (!unw_is_fpreg (reg))
194 goto badreg;
195
Konstantin Belousov0dbeeeb2010-04-05 22:42:23 +0300196 if (!(addr = x86_r_uc_addr (uc, reg)))
homeip.net!davidm588192d2004-08-17 15:34:28 +0000197 goto badreg;
198
199 if (write)
200 {
201 Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
202 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
203 *(unw_fpreg_t *) addr = *val;
204 }
205 else
206 {
207 *val = *(unw_fpreg_t *) addr;
208 Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
209 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
210 }
211 return 0;
212
213 badreg:
214 Debug (1, "bad register number %u\n", reg);
215 /* attempt to access a non-preserved register */
216 return -UNW_EBADREG;
217}
218
219static int
220get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
221 char *buf, size_t buf_len, unw_word_t *offp,
222 void *arg)
223{
David Mosberger-Tange6b9f352007-08-22 13:02:09 -0600224 return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
homeip.net!davidm588192d2004-08-17 15:34:28 +0000225}
226
227HIDDEN void
228x86_local_addr_space_init (void)
229{
230 memset (&local_addr_space, 0, sizeof (local_addr_space));
231 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
232 local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
233 local_addr_space.acc.put_unwind_info = put_unwind_info;
234 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
235 local_addr_space.acc.access_mem = access_mem;
236 local_addr_space.acc.access_reg = access_reg;
237 local_addr_space.acc.access_fpreg = access_fpreg;
238 local_addr_space.acc.resume = x86_local_resume;
239 local_addr_space.acc.get_proc_name = get_static_proc_name;
240 unw_flush_cache (&local_addr_space, 0, 0);
241}
242
243#endif /* !UNW_REMOTE_ONLY */