blob: 0af616a5cc1b5079f15d2de542f436a8c6298d3d [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
Konstantin Belousov44544132010-04-21 15:18:02 +030091 char mvec[2]; /* Unaligned access may cross page boundary */
Konstantin Belousovee99dbe2010-04-20 17:45:18 +030092#endif
Arun Sharmaff0ae702009-03-16 11:06:26 -070093
94 addr = PAGE_START(addr);
95
Paul Pluzhnikov0cf76ed2009-12-01 13:59:45 -080096 if (addr == 0)
97 return -1;
98
Arun Sharmaff0ae702009-03-16 11:06:26 -070099 for (i = 0; i < NLGA; i++)
100 {
101 if (last_good_addr[i] && (addr == last_good_addr[i]))
102 return 0;
103 }
104
Konstantin Belousovee99dbe2010-04-20 17:45:18 +0300105#ifdef HAVE_MINCORE
Konstantin Belousov2709abc2010-04-21 15:33:37 +0300106 if (mincore ((void *) addr, sizeof (unw_word_t), mvec) == -1)
Konstantin Belousovee99dbe2010-04-20 17:45:18 +0300107#else
Konstantin Belousov2709abc2010-04-21 15:33:37 +0300108 if (msync ((void *) addr, sizeof (unw_word_t), MS_ASYNC) == -1)
Konstantin Belousovee99dbe2010-04-20 17:45:18 +0300109#endif
Arun Sharmaff0ae702009-03-16 11:06:26 -0700110 return -1;
111
112 victim = lga_victim;
113 for (i = 0; i < NLGA; i++) {
114 if (!last_good_addr[victim]) {
115 last_good_addr[victim++] = addr;
116 return 0;
117 }
118 victim = (victim + 1) % NLGA;
119 }
120
121 /* All slots full. Evict the victim. */
122 last_good_addr[victim] = addr;
123 victim = (victim + 1) % NLGA;
124 lga_victim = victim;
125
126 return 0;
127}
128
homeip.net!davidm588192d2004-08-17 15:34:28 +0000129static int
130access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
131 void *arg)
132{
133 if (write)
134 {
135 Debug (16, "mem[%x] <- %x\n", addr, *val);
136 *(unw_word_t *) addr = *val;
137 }
138 else
139 {
Arun Sharmaff0ae702009-03-16 11:06:26 -0700140 /* validate address */
141 const struct cursor *c = (const struct cursor *)arg;
142 if (c && c->validate && validate_mem(addr))
143 return -1;
homeip.net!davidm588192d2004-08-17 15:34:28 +0000144 *val = *(unw_word_t *) addr;
145 Debug (16, "mem[%x] -> %x\n", addr, *val);
146 }
147 return 0;
148}
149
150static int
151access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
152 void *arg)
153{
154 unw_word_t *addr;
Arun Sharmaff0ae702009-03-16 11:06:26 -0700155 ucontext_t *uc = ((struct cursor *)arg)->uc;
homeip.net!davidm588192d2004-08-17 15:34:28 +0000156
157 if (unw_is_fpreg (reg))
158 goto badreg;
159
Konstantin Belousov0dbeeeb2010-04-05 22:42:23 +0300160 if (!(addr = x86_r_uc_addr (uc, reg)))
homeip.net!davidm588192d2004-08-17 15:34:28 +0000161 goto badreg;
162
163 if (write)
164 {
165 *(unw_word_t *) addr = *val;
166 Debug (12, "%s <- %x\n", unw_regname (reg), *val);
167 }
168 else
169 {
170 *val = *(unw_word_t *) addr;
171 Debug (12, "%s -> %x\n", unw_regname (reg), *val);
172 }
173 return 0;
174
175 badreg:
176 Debug (1, "bad register number %u\n", reg);
177 return -UNW_EBADREG;
178}
179
180static int
181access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
182 int write, void *arg)
183{
Arun Sharmaff0ae702009-03-16 11:06:26 -0700184 ucontext_t *uc = ((struct cursor *)arg)->uc;
homeip.net!davidm588192d2004-08-17 15:34:28 +0000185 unw_fpreg_t *addr;
186
187 if (!unw_is_fpreg (reg))
188 goto badreg;
189
Konstantin Belousov0dbeeeb2010-04-05 22:42:23 +0300190 if (!(addr = x86_r_uc_addr (uc, reg)))
homeip.net!davidm588192d2004-08-17 15:34:28 +0000191 goto badreg;
192
193 if (write)
194 {
195 Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
196 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
197 *(unw_fpreg_t *) addr = *val;
198 }
199 else
200 {
201 *val = *(unw_fpreg_t *) addr;
202 Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
203 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
204 }
205 return 0;
206
207 badreg:
208 Debug (1, "bad register number %u\n", reg);
209 /* attempt to access a non-preserved register */
210 return -UNW_EBADREG;
211}
212
213static int
214get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
215 char *buf, size_t buf_len, unw_word_t *offp,
216 void *arg)
217{
David Mosberger-Tange6b9f352007-08-22 13:02:09 -0600218 return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
homeip.net!davidm588192d2004-08-17 15:34:28 +0000219}
220
221HIDDEN void
222x86_local_addr_space_init (void)
223{
224 memset (&local_addr_space, 0, sizeof (local_addr_space));
225 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
226 local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
227 local_addr_space.acc.put_unwind_info = put_unwind_info;
228 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
229 local_addr_space.acc.access_mem = access_mem;
230 local_addr_space.acc.access_reg = access_reg;
231 local_addr_space.acc.access_fpreg = access_fpreg;
232 local_addr_space.acc.resume = x86_local_resume;
233 local_addr_space.acc.get_proc_name = get_static_proc_name;
234 unw_flush_cache (&local_addr_space, 0, 0);
235}
236
237#endif /* !UNW_REMOTE_ONLY */