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