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