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 <inttypes.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "unwind_i.h"
30
31 static struct unw_addr_space local_addr_space;
32
33 PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
34
35 HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
36
37 static inline void *
uc_addr(unw_tdep_context_t * uc,int reg)38 uc_addr (unw_tdep_context_t *uc, int reg)
39 {
40 // TODO: Do we want to use mcontext_t et. al.?
41 // For now this is cheating. See def in musl arm/signal.h.
42 if (reg >= UNW_ARM_R0 && reg < UNW_ARM_R0 + 16)
43 return &uc->uc_mcontext.arm_r0 + (reg - UNW_ARM_R0);
44 else
45 return NULL;
46 }
47
48 /* XXX fix me: there is currently no way to locate the dyn-info list
49 by a remote unwinder. On ia64, this is done via a special
50 unwind-table entry. Perhaps something similar can be done with
51 DWARF2 unwind info. */
52
53 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)54 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
55 void *arg)
56 {
57 *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
58 return 0;
59 }
60
61 #define PAGE_SIZE 4096
62 #define PAGE_START(a) ((a) & ~(PAGE_SIZE-1))
63
64 /* Cache of already validated addresses */
65 #define NLGA 4
66 static unw_word_t last_good_addr[NLGA];
67 static int lga_victim;
68
69 static int
validate_mem(unw_word_t addr)70 validate_mem (unw_word_t addr)
71 {
72 int i, victim;
73 size_t len;
74
75 if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
76 len = PAGE_SIZE;
77 else
78 len = PAGE_SIZE * 2;
79
80 addr = PAGE_START(addr);
81
82 if (addr == 0)
83 return -1;
84
85 for (i = 0; i < NLGA; i++)
86 {
87 if (last_good_addr[i] && (addr == last_good_addr[i]))
88 return 0;
89 }
90
91 if (msync ((void *) addr, len, MS_ASYNC) == -1)
92 return -1;
93
94 victim = lga_victim;
95 for (i = 0; i < NLGA; i++) {
96 if (!last_good_addr[victim]) {
97 last_good_addr[victim++] = addr;
98 return 0;
99 }
100 victim = (victim + 1) % NLGA;
101 }
102
103 /* All slots full. Evict the victim. */
104 last_good_addr[victim] = addr;
105 victim = (victim + 1) % NLGA;
106 lga_victim = victim;
107
108 return 0;
109 }
110
111 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)112 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
113 void *arg)
114 {
115 if (write)
116 {
117 Debug (16, "mem[%" PRIxPTR "x <- %" PRIxPTR "\n", addr, *val);
118 *(unw_word_t *) addr = *val;
119 }
120 else
121 {
122 /* validate address */
123 const struct cursor *c = (const struct cursor *) arg;
124 if (c && validate_mem(addr))
125 return -1;
126
127 *val = *(unw_word_t *) addr;
128 Debug (16, "mem[%" PRIxPTR "] -> %" PRIxPTR "\n", addr, *val);
129 }
130 return 0;
131 }
132
133 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)134 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
135 void *arg)
136 {
137 unw_word_t *addr;
138 unw_tdep_context_t *uc = arg;
139
140 if (unw_is_fpreg (reg))
141 goto badreg;
142
143 if (!(addr = uc_addr (uc, reg)))
144 goto badreg;
145
146 if (write)
147 {
148 *(unw_word_t *) addr = *val;
149 Debug (12, "%s <- %" PRIxPTR "\n", unw_regname (reg), *val);
150 }
151 else
152 {
153 *val = *(unw_word_t *) addr;
154 Debug (12, "%s -> %" PRIxPTR "\n", unw_regname (reg), *val);
155 }
156 return 0;
157
158 badreg:
159 Debug (1, "bad register number %u\n", reg);
160 return -UNW_EBADREG;
161 }
162
163 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)164 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
165 int write, void *arg)
166 {
167 unw_tdep_context_t *uc = arg;
168 unw_fpreg_t *addr;
169
170 if (!unw_is_fpreg (reg))
171 goto badreg;
172
173 if (!(addr = uc_addr (uc, reg)))
174 goto badreg;
175
176 if (write)
177 {
178 Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
179 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
180 *(unw_fpreg_t *) addr = *val;
181 }
182 else
183 {
184 *val = *(unw_fpreg_t *) addr;
185 Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
186 ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
187 }
188 return 0;
189
190 badreg:
191 Debug (1, "bad register number %u\n", reg);
192 /* attempt to access a non-preserved register */
193 return -UNW_EBADREG;
194 }
195
196 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)197 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
198 char *buf, size_t buf_len, unw_word_t *offp,
199 void *arg)
200 {
201 return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
202 }
203
204 HIDDEN void
arm_local_addr_space_init(void)205 arm_local_addr_space_init (void)
206 {
207 memset (&local_addr_space, 0, sizeof (local_addr_space));
208 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
209 local_addr_space.acc.find_proc_info = arm_find_proc_info;
210 local_addr_space.acc.put_unwind_info = arm_put_unwind_info;
211 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
212 local_addr_space.acc.access_mem = access_mem;
213 local_addr_space.acc.access_reg = access_reg;
214 local_addr_space.acc.access_fpreg = access_fpreg;
215 local_addr_space.acc.resume = NULL;
216 local_addr_space.acc.get_proc_name = get_static_proc_name;
217 local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
218 unw_flush_cache (&local_addr_space, 0, 0);
219 }
220