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 Debug (16, "mem[%x] <- %x\n", addr, *val);
136 *(unw_word_t *) addr = *val;
137 }
138 else
139 {
140 *val = *(unw_word_t *) addr;
141 Debug (16, "mem[%x] -> %x\n", addr, *val);
142 }
143 return 0;
144 }
145
146 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)147 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
148 void *arg)
149 {
150 unw_word_t *addr;
151 unw_tdep_context_t *uc = arg;
152
153 if (unw_is_fpreg (reg))
154 goto badreg;
155
156 Debug (16, "reg = %s\n", unw_regname (reg));
157 if (!(addr = uc_addr (uc, reg)))
158 goto badreg;
159
160 if (write)
161 {
162 *(unw_word_t *) addr = *val;
163 Debug (12, "%s <- %x\n", unw_regname (reg), *val);
164 }
165 else
166 {
167 *val = *(unw_word_t *) addr;
168 Debug (12, "%s -> %x\n", unw_regname (reg), *val);
169 }
170 return 0;
171
172 badreg:
173 Debug (1, "bad register number %u\n", reg);
174 return -UNW_EBADREG;
175 }
176
177 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)178 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
179 int write, void *arg)
180 {
181 unw_tdep_context_t *uc = arg;
182 unw_fpreg_t *addr;
183
184 if (!unw_is_fpreg (reg))
185 goto badreg;
186
187 if (!(addr = uc_addr (uc, reg)))
188 goto badreg;
189
190 if (write)
191 {
192 Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
193 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
194 *(unw_fpreg_t *) addr = *val;
195 }
196 else
197 {
198 *val = *(unw_fpreg_t *) addr;
199 Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
200 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
201 }
202 return 0;
203
204 badreg:
205 Debug (1, "bad register number %u\n", reg);
206 /* attempt to access a non-preserved register */
207 return -UNW_EBADREG;
208 }
209
210 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)211 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
212 char *buf, size_t buf_len, unw_word_t *offp,
213 void *arg)
214 {
215 return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
216 }
217
218 HIDDEN void
arm_local_addr_space_init(void)219 arm_local_addr_space_init (void)
220 {
221 memset (&local_addr_space, 0, sizeof (local_addr_space));
222 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
223 local_addr_space.acc.find_proc_info = arm_find_proc_info;
224 local_addr_space.acc.put_unwind_info = arm_put_unwind_info;
225 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
226 local_addr_space.acc.access_mem = access_mem;
227 local_addr_space.acc.access_reg = access_reg;
228 local_addr_space.acc.access_fpreg = access_fpreg;
229 local_addr_space.acc.resume = arm_local_resume;
230 local_addr_space.acc.get_proc_name = get_static_proc_name;
231 unw_flush_cache (&local_addr_space, 0, 0);
232 }
233
234 HIDDEN void
init_local_addr_space(unw_addr_space_t as)235 init_local_addr_space (unw_addr_space_t as)
236 {
237 memset (as, 0, sizeof (struct unw_addr_space));
238 as->caching_policy = UNW_CACHE_GLOBAL;
239 as->acc.find_proc_info = arm_find_proc_info;
240 as->acc.put_unwind_info = arm_put_unwind_info;
241 as->acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
242 as->acc.access_mem = access_mem;
243 as->acc.access_reg = access_reg;
244 as->acc.access_fpreg = access_fpreg;
245 as->acc.resume = arm_local_resume;
246 as->acc.get_proc_name = get_static_proc_name;
247 unw_flush_cache (as, 0, 0);
248 }
249
250 #endif /* !UNW_REMOTE_ONLY */
251