1 /* libunwind - a platform-independent unwind library
2 Copyright (C) 2008 CodeSourcery
3
4 This file is part of libunwind.
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "unwind_i.h"
29
30 #ifdef UNW_REMOTE_ONLY
31
32 /* unw_local_addr_space is a NULL pointer in this case. */
33 unw_addr_space_t unw_local_addr_space;
34
35 #else /* !UNW_REMOTE_ONLY */
36
37 static struct unw_addr_space local_addr_space;
38
39 unw_addr_space_t unw_local_addr_space = &local_addr_space;
40
41 static inline void *
uc_addr(unw_tdep_context_t * uc,int reg)42 uc_addr (unw_tdep_context_t *uc, int reg)
43 {
44 if (reg >= UNW_ARM_R0 && reg < UNW_ARM_R0 + 16)
45 return &uc->regs[reg - UNW_ARM_R0];
46 else
47 return NULL;
48 }
49
50 # ifdef UNW_LOCAL_ONLY
51
52 HIDDEN void *
tdep_uc_addr(unw_tdep_context_t * uc,int reg)53 tdep_uc_addr (unw_tdep_context_t *uc, int reg)
54 {
55 return uc_addr (uc, reg);
56 }
57
58 # endif /* UNW_LOCAL_ONLY */
59
60 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)61 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
62 void *arg)
63 {
64 #ifndef UNW_LOCAL_ONLY
65 # pragma weak _U_dyn_info_list_addr
66 if (!_U_dyn_info_list_addr)
67 return -UNW_ENOINFO;
68 #endif
69 // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
70 *dyn_info_list_addr = _U_dyn_info_list_addr ();
71 return 0;
72 }
73
74 #define PAGE_SIZE 4096
75 #define PAGE_START(a) ((a) & ~(PAGE_SIZE-1))
76
77 /* Cache of already validated addresses */
78 #define NLGA 4
79 static unw_word_t last_good_addr[NLGA];
80 static int lga_victim;
81
82 static int
validate_mem(unw_word_t addr)83 validate_mem (unw_word_t addr)
84 {
85 int i, victim;
86 size_t len;
87
88 if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
89 len = PAGE_SIZE;
90 else
91 len = PAGE_SIZE * 2;
92
93 addr = PAGE_START(addr);
94
95 if (addr == 0)
96 return -1;
97
98 for (i = 0; i < NLGA; i++)
99 {
100 if (last_good_addr[i] && (addr == last_good_addr[i]))
101 return 0;
102 }
103
104 if (msync ((void *) addr, len, MS_ASYNC) == -1)
105 return -1;
106
107 victim = lga_victim;
108 for (i = 0; i < NLGA; i++) {
109 if (!last_good_addr[victim]) {
110 last_good_addr[victim++] = addr;
111 return 0;
112 }
113 victim = (victim + 1) % NLGA;
114 }
115
116 /* All slots full. Evict the victim. */
117 last_good_addr[victim] = addr;
118 victim = (victim + 1) % NLGA;
119 lga_victim = victim;
120
121 return 0;
122 }
123
124 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)125 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
126 void *arg)
127 {
128 /* validate address */
129 const struct cursor *c = (const struct cursor *) arg;
130 if (c && validate_mem(addr))
131 return -1;
132
133 if (write)
134 {
135 #ifdef IS_VALIDATE_MEM
136 if (maps_is_writable(as->map_list, addr)) {
137 #endif
138 Debug (16, "mem[%x] <- %x\n", addr, *val);
139 *(unw_word_t *) addr = *val;
140 #ifdef IS_VALIDATE_MEM
141 } else {
142 Debug (16, "Unwritable memory mem[%lx] <- %lx\n", addr, *val);
143 return -1;
144 }
145 #endif
146 }
147 else
148 {
149 #ifdef IS_VALIDATE_MEM
150 if (maps_is_readable(as->map_list, addr)) {
151 #endif
152 *val = *(unw_word_t *) addr;
153 Debug (16, "mem[%x] -> %x\n", addr, *val);
154 #ifdef IS_VALIDATE_MEM
155 } else {
156 Debug (16, "Unreadable memory mem[%lx] <- %lx\n", addr, *val);
157 return -1;
158 }
159 #endif
160 }
161 return 0;
162 }
163
164 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)165 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
166 void *arg)
167 {
168 unw_word_t *addr;
169 unw_tdep_context_t *uc = arg;
170
171 if (unw_is_fpreg (reg))
172 goto badreg;
173
174 Debug (16, "reg = %s\n", unw_regname (reg));
175 if (!(addr = uc_addr (uc, reg)))
176 goto badreg;
177
178 if (write)
179 {
180 *(unw_word_t *) addr = *val;
181 Debug (12, "%s <- %x\n", unw_regname (reg), *val);
182 }
183 else
184 {
185 *val = *(unw_word_t *) addr;
186 Debug (12, "%s -> %x\n", unw_regname (reg), *val);
187 }
188 return 0;
189
190 badreg:
191 Debug (1, "bad register number %u\n", reg);
192 return -UNW_EBADREG;
193 }
194
195 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)196 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
197 int write, void *arg)
198 {
199 unw_tdep_context_t *uc = arg;
200 unw_fpreg_t *addr;
201
202 if (!unw_is_fpreg (reg))
203 goto badreg;
204
205 if (!(addr = uc_addr (uc, reg)))
206 goto badreg;
207
208 if (write)
209 {
210 Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
211 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
212 *(unw_fpreg_t *) addr = *val;
213 }
214 else
215 {
216 *val = *(unw_fpreg_t *) addr;
217 Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
218 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
219 }
220 return 0;
221
222 badreg:
223 Debug (1, "bad register number %u\n", reg);
224 /* attempt to access a non-preserved register */
225 return -UNW_EBADREG;
226 }
227
228 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)229 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
230 char *buf, size_t buf_len, unw_word_t *offp,
231 void *arg)
232 {
233 return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
234 }
235
236 HIDDEN void
arm_local_addr_space_init(void)237 arm_local_addr_space_init (void)
238 {
239 memset (&local_addr_space, 0, sizeof (local_addr_space));
240 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
241 local_addr_space.acc.find_proc_info = arm_find_proc_info;
242 local_addr_space.acc.put_unwind_info = arm_put_unwind_info;
243 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
244 local_addr_space.acc.access_mem = access_mem;
245 local_addr_space.acc.access_reg = access_reg;
246 local_addr_space.acc.access_fpreg = access_fpreg;
247 local_addr_space.acc.resume = arm_local_resume;
248 local_addr_space.acc.get_proc_name = get_static_proc_name;
249 unw_flush_cache (&local_addr_space, 0, 0);
250 }
251
252 HIDDEN void
init_local_addr_space(unw_addr_space_t as)253 init_local_addr_space (unw_addr_space_t as)
254 {
255 memset (as, 0, sizeof (struct unw_addr_space));
256 as->caching_policy = UNW_CACHE_GLOBAL;
257 as->acc.find_proc_info = arm_find_proc_info;
258 as->acc.put_unwind_info = arm_put_unwind_info;
259 as->acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
260 as->acc.access_mem = access_mem;
261 as->acc.access_reg = access_reg;
262 as->acc.access_fpreg = access_fpreg;
263 as->acc.resume = arm_local_resume;
264 as->acc.get_proc_name = get_static_proc_name;
265 unw_flush_cache (as, 0, 0);
266 }
267
268 #endif /* !UNW_REMOTE_ONLY */
269