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 #endif
36
37 #if HAVE_DECL_PTRACE_SETREGSET
38 #include <sys/uio.h>
39 int
_UPT_access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)40 _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
41 int write, void *arg)
42 {
43 struct UPT_info *ui = arg;
44 pid_t pid = ui->pid;
45 gregset_t regs;
46 char *r;
47 struct iovec loc;
48
49 #if UNW_DEBUG
50 Debug(16, "using getregset: reg: %s [%u], val: %lx, write: %u\n",
51 unw_regname(reg), (unsigned) reg, (long) val, write);
52
53 if (write)
54 Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
55 #endif
56 if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
57 {
58 errno = EINVAL;
59 goto badreg;
60 }
61
62 loc.iov_base = ®s;
63 loc.iov_len = sizeof(regs);
64
65 r = (char *)®s + _UPT_reg_offset[reg];
66 if (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, &loc) == -1)
67 goto badreg;
68 if (write) {
69 memcpy(r, val, sizeof(unw_word_t));
70 if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &loc) == -1)
71 goto badreg;
72 } else
73 memcpy(val, r, sizeof(unw_word_t));
74 return 0;
75
76 badreg:
77 Dprintf ("bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
78 return -UNW_EBADREG;
79 }
80 #elif HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE
81 int
_UPT_access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)82 _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
83 int write, void *arg)
84 {
85 struct UPT_info *ui = arg;
86 pid_t pid = ui->pid;
87
88 #if UNW_DEBUG
89 Debug(16, "using pokeuser: reg: %s [%u], val: %lx, write: %d\n", unw_regname(reg), (unsigned) reg, (long) val, write);
90
91 if (write)
92 Debug (16, "%s <- %lx\n", unw_regname (reg), (long) *val);
93 #endif
94
95 #if UNW_TARGET_IA64
96 if ((unsigned) reg - UNW_IA64_NAT < 32)
97 {
98 unsigned long nat_bits, mask;
99
100 /* The Linux ptrace represents the statc NaT bits as a single word. */
101 mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
102 errno = 0;
103 #ifdef HAVE_TTRACE
104 # warning No support for ttrace() yet.
105 #else
106 nat_bits = ptrace (PTRACE_PEEKUSER, pid, PT_NAT_BITS, 0);
107 if (errno)
108 goto badreg;
109 #endif
110
111 if (write)
112 {
113 if (*val)
114 nat_bits |= mask;
115 else
116 nat_bits &= ~mask;
117 #ifdef HAVE_TTRACE
118 # warning No support for ttrace() yet.
119 #else
120 errno = 0;
121 ptrace (PTRACE_POKEUSER, pid, PT_NAT_BITS, nat_bits);
122 if (errno)
123 goto badreg;
124 #endif
125 }
126 goto out;
127 }
128 else
129 switch (reg)
130 {
131 case UNW_IA64_GR + 0:
132 if (write)
133 goto badreg;
134 *val = 0;
135 return 0;
136
137 case UNW_REG_IP:
138 {
139 unsigned long ip, psr;
140
141 /* distribute bundle-addr. & slot-number across PT_IIP & PT_IPSR. */
142 #ifdef HAVE_TTRACE
143 # warning No support for ttrace() yet.
144 #else
145 errno = 0;
146 psr = ptrace (PTRACE_PEEKUSER, pid, PT_CR_IPSR, 0);
147 if (errno)
148 goto badreg;
149 #endif
150 if (write)
151 {
152 ip = *val & ~0xfUL;
153 psr = (psr & ~0x3UL << 41) | (*val & 0x3);
154 #ifdef HAVE_TTRACE
155 # warning No support for ttrace() yet.
156 #else
157 errno = 0;
158 ptrace (PTRACE_POKEUSER, pid, PT_CR_IIP, ip);
159 ptrace (PTRACE_POKEUSER, pid, PT_CR_IPSR, psr);
160 if (errno)
161 goto badreg;
162 #endif
163 }
164 else
165 {
166 #ifdef HAVE_TTRACE
167 # warning No support for ttrace() yet.
168 #else
169 errno = 0;
170 ip = ptrace (PTRACE_PEEKUSER, pid, PT_CR_IIP, 0);
171 if (errno)
172 goto badreg;
173 #endif
174 *val = ip + ((psr >> 41) & 0x3);
175 }
176 goto out;
177 }
178
179 case UNW_IA64_AR_BSPSTORE:
180 reg = UNW_IA64_AR_BSP;
181 break;
182
183 case UNW_IA64_AR_BSP:
184 case UNW_IA64_BSP:
185 {
186 unsigned long sof, cfm, bsp;
187
188 #ifdef HAVE_TTRACE
189 # warning No support for ttrace() yet.
190 #else
191 /* Account for the fact that ptrace() expects bsp to point
192 _after_ the current register frame. */
193 errno = 0;
194 cfm = ptrace (PTRACE_PEEKUSER, pid, PT_CFM, 0);
195 if (errno)
196 goto badreg;
197 #endif
198 sof = (cfm & 0x7f);
199
200 if (write)
201 {
202 bsp = rse_skip_regs (*val, sof);
203 #ifdef HAVE_TTRACE
204 # warning No support for ttrace() yet.
205 #else
206 errno = 0;
207 ptrace (PTRACE_POKEUSER, pid, PT_AR_BSP, bsp);
208 if (errno)
209 goto badreg;
210 #endif
211 }
212 else
213 {
214 #ifdef HAVE_TTRACE
215 # warning No support for ttrace() yet.
216 #else
217 errno = 0;
218 bsp = ptrace (PTRACE_PEEKUSER, pid, PT_AR_BSP, 0);
219 if (errno)
220 goto badreg;
221 #endif
222 *val = rse_skip_regs (bsp, -sof);
223 }
224 goto out;
225 }
226
227 case UNW_IA64_CFM:
228 /* If we change CFM, we need to adjust ptrace's notion of bsp
229 accordingly, so that the real bsp remains unchanged. */
230 if (write)
231 {
232 unsigned long new_sof, old_sof, cfm, bsp;
233
234 #ifdef HAVE_TTRACE
235 # warning No support for ttrace() yet.
236 #else
237 errno = 0;
238 bsp = ptrace (PTRACE_PEEKUSER, pid, PT_AR_BSP, 0);
239 cfm = ptrace (PTRACE_PEEKUSER, pid, PT_CFM, 0);
240 #endif
241 if (errno)
242 goto badreg;
243 old_sof = (cfm & 0x7f);
244 new_sof = (*val & 0x7f);
245 if (old_sof != new_sof)
246 {
247 bsp = rse_skip_regs (bsp, -old_sof + new_sof);
248 #ifdef HAVE_TTRACE
249 # warning No support for ttrace() yet.
250 #else
251 errno = 0;
252 ptrace (PTRACE_POKEUSER, pid, PT_AR_BSP, 0);
253 if (errno)
254 goto badreg;
255 #endif
256 }
257 #ifdef HAVE_TTRACE
258 # warning No support for ttrace() yet.
259 #else
260 errno = 0;
261 ptrace (PTRACE_POKEUSER, pid, PT_CFM, *val);
262 if (errno)
263 goto badreg;
264 #endif
265 goto out;
266 }
267 break;
268 }
269 #endif /* End of IA64 */
270
271 #if UNW_TARGET_RISCV
272 if (reg == UNW_RISCV_X0) {
273 if (write)
274 goto badreg;
275
276 *val = 0;
277 return 0;
278 }
279 #endif /* End of RISCV */
280
281 if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
282 {
283 #if UNW_DEBUG
284 Debug(2, "register out of range: >= %zu / %zu\n", sizeof(_UPT_reg_offset), sizeof(_UPT_reg_offset[0]));
285 #endif
286 errno = EINVAL;
287 goto badreg;
288 }
289
290 #ifdef HAVE_TTRACE
291 # warning No support for ttrace() yet.
292 #else
293 errno = 0;
294 if (write)
295 ptrace (PTRACE_POKEUSER, pid, _UPT_reg_offset[reg], *val);
296 else {
297 #if UNW_DEBUG
298 Debug(16, "ptrace PEEKUSER pid: %lu , reg: %lu , offs: %lu\n", (unsigned long)pid, (unsigned long)reg,
299 (unsigned long)_UPT_reg_offset[reg]);
300 #endif
301 *val = ptrace (PTRACE_PEEKUSER, pid, _UPT_reg_offset[reg], 0);
302 }
303 if (errno) {
304 #if UNW_DEBUG
305 Debug(2, "ptrace failure\n");
306 #endif
307 goto badreg;
308 }
309 #endif
310
311 #ifdef UNW_TARGET_IA64
312 out:
313 #endif
314 #if UNW_DEBUG
315 if (!write)
316 Debug (16, "%s[%u] -> %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
317 #endif
318 return 0;
319
320 badreg:
321 Dprintf ("bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
322 return -UNW_EBADREG;
323 }
324 #elif HAVE_DECL_PT_GETREGS
325 int
_UPT_access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)326 _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
327 int write, void *arg)
328 {
329 struct UPT_info *ui = arg;
330 pid_t pid = ui->pid;
331 gregset_t regs;
332 char *r;
333
334 #if UNW_DEBUG
335 Debug(16, "using getregs: reg: %s [%u], val: %lx, write: %u\n", unw_regname(reg), (unsigned) reg, (long) val, write);
336
337 if (write)
338 Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
339 #endif
340 if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
341 {
342 errno = EINVAL;
343 goto badreg;
344 }
345 r = (char *)®s + _UPT_reg_offset[reg];
346 if (ptrace(PT_GETREGS, pid, (caddr_t)®s, 0) == -1)
347 goto badreg;
348 if (write) {
349 memcpy(r, val, sizeof(unw_word_t));
350 if (ptrace(PT_SETREGS, pid, (caddr_t)®s, 0) == -1)
351 goto badreg;
352 } else
353 memcpy(val, r, sizeof(unw_word_t));
354 return 0;
355
356 badreg:
357 Dprintf ("bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
358 return -UNW_EBADREG;
359 }
360 #else
361 #error Port me
362 #endif
363