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