1 /* libunwind - a platform-independent unwind library
2 Copyright (C) 2003-2005 Hewlett-Packard Co
3 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4 Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
5
6 This file is part of libunwind.
7
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
26
27 #include "_UPT_internal.h"
28
29 #if UNW_TARGET_IA64
30 # include <elf.h>
31 # ifdef HAVE_ASM_PTRACE_OFFSETS_H
32 # include <asm/ptrace_offsets.h>
33 # endif
34 # include "tdep-ia64/rse.h"
35 #elif defined(__aarch64__)
36 # include <sys/uio.h>
37 #endif
38
39 #if HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE
40 int
_UPT_access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)41 _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
42 int write, void *arg)
43 {
44 struct UPT_info *ui = arg;
45 pid_t pid = ui->pid;
46
47 #if UNW_DEBUG
48 Debug(16, "using pokeuser: reg: %s [%u], val: %lx, write: %d\n", unw_regname(reg), (unsigned) reg, (long) val, write);
49
50 if (write)
51 Debug (16, "%s <- %lx\n", unw_regname (reg), (long) *val);
52 #endif
53
54 #if UNW_TARGET_IA64
55 if ((unsigned) reg - UNW_IA64_NAT < 32)
56 {
57 unsigned long nat_bits, mask;
58
59 /* The Linux ptrace represents the statc NaT bits as a single word. */
60 mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
61 errno = 0;
62 #ifdef HAVE_TTRACE
63 # warning No support for ttrace() yet.
64 #else
65 nat_bits = ptrace (PTRACE_PEEKUSER, pid, PT_NAT_BITS, 0);
66 if (errno)
67 goto badreg;
68 #endif
69
70 if (write)
71 {
72 if (*val)
73 nat_bits |= mask;
74 else
75 nat_bits &= ~mask;
76 #ifdef HAVE_TTRACE
77 # warning No support for ttrace() yet.
78 #else
79 errno = 0;
80 ptrace (PTRACE_POKEUSER, pid, PT_NAT_BITS, nat_bits);
81 if (errno)
82 goto badreg;
83 #endif
84 }
85 goto out;
86 }
87 else
88 switch (reg)
89 {
90 case UNW_IA64_GR + 0:
91 if (write)
92 goto badreg;
93 *val = 0;
94 return 0;
95
96 case UNW_REG_IP:
97 {
98 unsigned long ip, psr;
99
100 /* distribute bundle-addr. & slot-number across PT_IIP & PT_IPSR. */
101 #ifdef HAVE_TTRACE
102 # warning No support for ttrace() yet.
103 #else
104 errno = 0;
105 psr = ptrace (PTRACE_PEEKUSER, pid, PT_CR_IPSR, 0);
106 if (errno)
107 goto badreg;
108 #endif
109 if (write)
110 {
111 ip = *val & ~0xfUL;
112 psr = (psr & ~0x3UL << 41) | (*val & 0x3);
113 #ifdef HAVE_TTRACE
114 # warning No support for ttrace() yet.
115 #else
116 errno = 0;
117 ptrace (PTRACE_POKEUSER, pid, PT_CR_IIP, ip);
118 ptrace (PTRACE_POKEUSER, pid, PT_CR_IPSR, psr);
119 if (errno)
120 goto badreg;
121 #endif
122 }
123 else
124 {
125 #ifdef HAVE_TTRACE
126 # warning No support for ttrace() yet.
127 #else
128 errno = 0;
129 ip = ptrace (PTRACE_PEEKUSER, pid, PT_CR_IIP, 0);
130 if (errno)
131 goto badreg;
132 #endif
133 *val = ip + ((psr >> 41) & 0x3);
134 }
135 goto out;
136 }
137
138 case UNW_IA64_AR_BSPSTORE:
139 reg = UNW_IA64_AR_BSP;
140 break;
141
142 case UNW_IA64_AR_BSP:
143 case UNW_IA64_BSP:
144 {
145 unsigned long sof, cfm, bsp;
146
147 #ifdef HAVE_TTRACE
148 # warning No support for ttrace() yet.
149 #else
150 /* Account for the fact that ptrace() expects bsp to point
151 _after_ the current register frame. */
152 errno = 0;
153 cfm = ptrace (PTRACE_PEEKUSER, pid, PT_CFM, 0);
154 if (errno)
155 goto badreg;
156 #endif
157 sof = (cfm & 0x7f);
158
159 if (write)
160 {
161 bsp = rse_skip_regs (*val, sof);
162 #ifdef HAVE_TTRACE
163 # warning No support for ttrace() yet.
164 #else
165 errno = 0;
166 ptrace (PTRACE_POKEUSER, pid, PT_AR_BSP, bsp);
167 if (errno)
168 goto badreg;
169 #endif
170 }
171 else
172 {
173 #ifdef HAVE_TTRACE
174 # warning No support for ttrace() yet.
175 #else
176 errno = 0;
177 bsp = ptrace (PTRACE_PEEKUSER, pid, PT_AR_BSP, 0);
178 if (errno)
179 goto badreg;
180 #endif
181 *val = rse_skip_regs (bsp, -sof);
182 }
183 goto out;
184 }
185
186 case UNW_IA64_CFM:
187 /* If we change CFM, we need to adjust ptrace's notion of bsp
188 accordingly, so that the real bsp remains unchanged. */
189 if (write)
190 {
191 unsigned long new_sof, old_sof, cfm, bsp;
192
193 #ifdef HAVE_TTRACE
194 # warning No support for ttrace() yet.
195 #else
196 errno = 0;
197 bsp = ptrace (PTRACE_PEEKUSER, pid, PT_AR_BSP, 0);
198 cfm = ptrace (PTRACE_PEEKUSER, pid, PT_CFM, 0);
199 #endif
200 if (errno)
201 goto badreg;
202 old_sof = (cfm & 0x7f);
203 new_sof = (*val & 0x7f);
204 if (old_sof != new_sof)
205 {
206 bsp = rse_skip_regs (bsp, -old_sof + new_sof);
207 #ifdef HAVE_TTRACE
208 # warning No support for ttrace() yet.
209 #else
210 errno = 0;
211 ptrace (PTRACE_POKEUSER, pid, PT_AR_BSP, 0);
212 if (errno)
213 goto badreg;
214 #endif
215 }
216 #ifdef HAVE_TTRACE
217 # warning No support for ttrace() yet.
218 #else
219 errno = 0;
220 ptrace (PTRACE_POKEUSER, pid, PT_CFM, *val);
221 if (errno)
222 goto badreg;
223 #endif
224 goto out;
225 }
226 break;
227 }
228 #endif /* End of IA64 */
229
230 if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
231 {
232 #if UNW_DEBUG
233 Debug(2, "register out of range: >= %zu / %zu\n", sizeof(_UPT_reg_offset), sizeof(_UPT_reg_offset[0]));
234 #endif
235 errno = EINVAL;
236 goto badreg;
237 }
238
239 #ifdef HAVE_TTRACE
240 # warning No support for ttrace() yet.
241 #else
242 errno = 0;
243 if (write)
244 /* ANDROID support update. */
245 ptrace (PTRACE_POKEUSER, pid, (void*) (uintptr_t) _UPT_reg_offset[reg], (void*) *val);
246 /* End of ANDROID update. */
247 else {
248 #if UNW_DEBUG
249 Debug(16, "ptrace PEEKUSER pid: %lu , reg: %lu , offs: %lu\n", (unsigned long)pid, (unsigned long)reg,
250 (unsigned long)_UPT_reg_offset[reg]);
251 #endif
252 /* ANDROID support update. */
253 *val = ptrace (PTRACE_PEEKUSER, pid, (void*) (uintptr_t) _UPT_reg_offset[reg], 0);
254 /* End of ANDROID update. */
255 }
256 if (errno) {
257 #if UNW_DEBUG
258 Debug(2, "ptrace failure\n");
259 #endif
260 goto badreg;
261 }
262 #endif
263
264 #ifdef UNW_TARGET_IA64
265 out:
266 #endif
267 #if UNW_DEBUG
268 if (!write)
269 Debug (16, "%s[%u] -> %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
270 #endif
271 return 0;
272
273 badreg:
274 Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
275 return -UNW_EBADREG;
276 }
277 #elif HAVE_DECL_PT_GETREGS
278 int
_UPT_access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)279 _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
280 int write, void *arg)
281 {
282 struct UPT_info *ui = arg;
283 pid_t pid = ui->pid;
284 /* ANDROID support update. */
285 #if defined(__mips__)
286 struct
287 {
288 uint64_t regs[32];
289 uint64_t lo;
290 uint64_t hi;
291 uint64_t epc;
292 uint64_t badvaddr;
293 uint64_t status;
294 uint64_t cause;
295 }
296 regs;
297 #else
298 char *r;
299 gregset_t regs;
300 #endif
301
302 #if UNW_DEBUG
303 Debug(16, "using getregs: reg: %s [%u], val: %lx, write: %u\n", unw_regname(reg), (unsigned) reg, (long) val, write);
304
305 if (write)
306 Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
307 #endif
308 #if defined(__mips__)
309 if (ptrace(PTRACE_GETREGS, pid, 0, (void*)®s) == -1)
310 goto badreg;
311 if (write)
312 {
313 if (reg <= UNW_MIPS_R31)
314 regs.regs[reg] = *val;
315 else if (reg == UNW_MIPS_PC)
316 regs.epc = *val;
317 else
318 goto badreg;
319 if (ptrace(PTRACE_SETREGS, pid, 0, (void*)®s) == -1)
320 goto badreg;
321 }
322 else
323 {
324 if (reg <= UNW_MIPS_R31)
325 *val = regs.regs[reg];
326 else if (reg == UNW_MIPS_PC)
327 *val = regs.epc;
328 else
329 goto badreg;
330 }
331 #else
332 if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
333 {
334 errno = EINVAL;
335 goto badreg;
336 }
337 r = (char *)®s + _UPT_reg_offset[reg];
338 if (ptrace(PT_GETREGS, pid, (caddr_t)®s, 0) == -1)
339 goto badreg;
340 if (write) {
341 memcpy(r, val, sizeof(unw_word_t));
342 if (ptrace(PT_SETREGS, pid, (caddr_t)®s, 0) == -1)
343 goto badreg;
344 } else
345 memcpy(val, r, sizeof(unw_word_t));
346 #endif
347 /* End of ANDROID update. */
348 return 0;
349
350 badreg:
351 Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
352 return -UNW_EBADREG;
353 }
354 /* ANDROID support update. */
355 #elif HAVE_DECL_PT_GETREGSET
356 int
_UPT_access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)357 _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
358 int write, void *arg)
359 {
360 struct UPT_info *ui = arg;
361 pid_t pid = ui->pid;
362 #if defined(__aarch64__)
363 struct user_pt_regs regs;
364 struct iovec io;
365 io.iov_base = ®s;
366 io.iov_len = sizeof(regs);
367
368 #if UNW_DEBUG
369 Debug(16, "using getregset: reg: %s [%u], val: %lx, write: %u\n", unw_regname(reg), (unsigned) reg, (long) val, write);
370
371 if (write)
372 Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
373 #endif
374 if (ptrace(PTRACE_GETREGSET, pid, (void*)NT_PRSTATUS, (void*)&io) == -1)
375 goto badreg;
376 if (write)
377 {
378 if (reg == UNW_AARCH64_SP)
379 regs.sp = *val;
380 else if (reg == UNW_AARCH64_PC)
381 regs.pc = *val;
382 else if (reg < UNW_AARCH64_SP)
383 regs.regs[reg] = *val;
384 else
385 goto badreg;
386 if (ptrace(PTRACE_SETREGSET, pid, (void*)NT_PRSTATUS, (void*)&io) == -1)
387 goto badreg;
388 }
389 else
390 {
391 if (reg == UNW_AARCH64_SP)
392 *val = regs.sp;
393 else if (reg == UNW_AARCH64_PC)
394 *val = regs.pc;
395 else if (reg < UNW_AARCH64_SP)
396 *val = regs.regs[reg];
397 else
398 goto badreg;
399 }
400 #else
401 #error Unsupported architecture for getregset
402 #endif
403 return 0;
404
405 badreg:
406 Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
407 return -UNW_EBADREG;
408 }
409 /* End of ANDROID update. */
410 #else
411 #error Port me
412 #endif
413