1 /* libunwind - a platform-independent unwind library
2 Copyright (C) 2006-2007 IBM
3 Contributed by
4 Corey Ashford <cjashfor@us.ibm.com>
5 Jose Flavio Aguilar Paulino <jflavio@br.ibm.com> <joseflavio@gmail.com>
6
7 This file is part of libunwind.
8
9 Permission is hereby granted, free of charge, to any person obtaining
10 a copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "ucontext_i.h"
32 #include "unwind_i.h"
33
34 #ifdef UNW_REMOTE_ONLY
35
36 /* unw_local_addr_space is a NULL pointer in this case. */
37 unw_addr_space_t unw_local_addr_space;
38
39 #else /* !UNW_REMOTE_ONLY */
40
41 static struct unw_addr_space local_addr_space;
42
43 unw_addr_space_t unw_local_addr_space = &local_addr_space;
44
45 static void *
uc_addr(ucontext_t * uc,int reg)46 uc_addr (ucontext_t *uc, int reg)
47 {
48 void *addr;
49
50 if ((unsigned) (reg - UNW_PPC64_R0) < 32)
51 #if defined(__linux__)
52 addr = &uc->uc_mcontext.gp_regs[reg - UNW_PPC64_R0];
53 #elif defined(__FreeBSD__)
54 addr = &uc->uc_mcontext.mc_gpr[reg - UNW_PPC64_R0];
55 #endif
56
57 else if ((unsigned) (reg - UNW_PPC64_F0) < 32)
58 #if defined(__linux__)
59 addr = &uc->uc_mcontext.fp_regs[reg - UNW_PPC64_F0];
60 #elif defined(__FreeBSD__)
61 addr = &uc->uc_mcontext.mc_fpreg[reg - UNW_PPC64_F0];
62 #endif
63
64 else if ((unsigned) (reg - UNW_PPC64_V0) < 32)
65 #if defined(__linux__)
66 addr = (uc->uc_mcontext.v_regs == 0) ? NULL : &uc->uc_mcontext.v_regs->vrregs[reg - UNW_PPC64_V0][0];
67 #elif defined(__FreeBSD__)
68 addr = &uc->uc_mcontext.mc_avec[(reg - UNW_PPC64_V0)*2];
69 #endif
70
71 else
72 {
73 unsigned gregs_idx;
74
75 switch (reg)
76 {
77 case UNW_PPC64_NIP:
78 gregs_idx = NIP_IDX;
79 break;
80 case UNW_PPC64_CTR:
81 gregs_idx = CTR_IDX;
82 break;
83 case UNW_PPC64_LR:
84 gregs_idx = LINK_IDX;
85 break;
86 case UNW_PPC64_XER:
87 gregs_idx = XER_IDX;
88 break;
89 case UNW_PPC64_CR0:
90 gregs_idx = CCR_IDX;
91 break;
92 default:
93 return NULL;
94 }
95 #if defined(__linux__)
96 addr = &uc->uc_mcontext.gp_regs[gregs_idx];
97 #elif defined(__FreeBSD__)
98 addr = &uc->uc_mcontext.mc_gpr[gregs_idx];
99 #endif
100 }
101 return addr;
102 }
103
104 # ifdef UNW_LOCAL_ONLY
105
106 HIDDEN void *
tdep_uc_addr(ucontext_t * uc,int reg)107 tdep_uc_addr (ucontext_t *uc, int reg)
108 {
109 return uc_addr (uc, reg);
110 }
111
112 # endif /* UNW_LOCAL_ONLY */
113
114 static void
put_unwind_info(unw_addr_space_t as,unw_proc_info_t * proc_info,void * arg)115 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
116 {
117 /* it's a no-op */
118 }
119
120 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)121 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
122 void *arg)
123 {
124 #ifndef UNW_LOCAL_ONLY
125 # pragma weak _U_dyn_info_list_addr
126 if (!_U_dyn_info_list_addr)
127 return -UNW_ENOINFO;
128 #endif
129 // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
130 *dyn_info_list_addr = _U_dyn_info_list_addr ();
131 return 0;
132 }
133
134 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)135 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
136 void *arg)
137 {
138 if (write)
139 {
140 Debug (12, "mem[%lx] <- %lx\n", addr, *val);
141 *(unw_word_t *) addr = *val;
142 }
143 else
144 {
145 *val = *(unw_word_t *) addr;
146 Debug (12, "mem[%lx] -> %lx\n", addr, *val);
147 }
148 return 0;
149 }
150
151 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)152 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
153 int write, void *arg)
154 {
155 unw_word_t *addr;
156 ucontext_t *uc = arg;
157
158 if (UNW_PPC64_F0 <= reg && reg <= UNW_PPC64_F31)
159 goto badreg;
160 if (UNW_PPC64_V0 <= reg && reg <= UNW_PPC64_V31)
161 goto badreg;
162
163 addr = uc_addr (uc, reg);
164 if (!addr)
165 goto badreg;
166
167 if (write)
168 {
169 *(unw_word_t *) addr = *val;
170 Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
171 }
172 else
173 {
174 *val = *(unw_word_t *) addr;
175 Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
176 }
177 return 0;
178
179 badreg:
180 Debug (1, "bad register number %u\n", reg);
181 return -UNW_EBADREG;
182 }
183
184 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)185 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
186 int write, void *arg)
187 {
188 ucontext_t *uc = arg;
189 unw_fpreg_t *addr;
190
191 /* Allow only 32 fregs and 32 vregs */
192 if (!(((unsigned) (reg - UNW_PPC64_F0) < 32)
193 ||((unsigned) (reg - UNW_PPC64_V0) < 32)))
194 goto badreg;
195
196 addr = uc_addr (uc, reg);
197 if (!addr)
198 goto badreg;
199
200 if (write)
201 {
202 Debug (12, "%s <- %016Lf\n", unw_regname (reg), *val);
203 *(unw_fpreg_t *) addr = *val;
204 }
205 else
206 {
207 *val = *(unw_fpreg_t *) addr;
208 Debug (12, "%s -> %016Lf\n", unw_regname (reg), *val);
209 }
210 return 0;
211
212 badreg:
213 Debug (1, "bad register number %u\n", reg);
214 /* attempt to access a non-preserved register */
215 return -UNW_EBADREG;
216 }
217
218 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)219 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
220 char *buf, size_t buf_len, unw_word_t *offp,
221 void *arg)
222 {
223 return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
224 }
225
226 HIDDEN void
ppc64_local_addr_space_init(void)227 ppc64_local_addr_space_init (void)
228 {
229 memset (&local_addr_space, 0, sizeof (local_addr_space));
230 local_addr_space.big_endian = target_is_big_endian();
231 #if _CALL_ELF == 2
232 local_addr_space.abi = UNW_PPC64_ABI_ELFv2;
233 #else
234 local_addr_space.abi = UNW_PPC64_ABI_ELFv1;
235 #endif
236 local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY;
237 local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
238 local_addr_space.acc.put_unwind_info = put_unwind_info;
239 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
240 local_addr_space.acc.access_mem = access_mem;
241 local_addr_space.acc.access_reg = access_reg;
242 local_addr_space.acc.access_fpreg = access_fpreg;
243 local_addr_space.acc.resume = ppc64_local_resume;
244 local_addr_space.acc.get_proc_name = get_static_proc_name;
245 unw_flush_cache (&local_addr_space, 0, 0);
246 }
247
248 #endif /* !UNW_REMOTE_ONLY */
249