• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &regs;
63   loc.iov_len = sizeof(regs);
64 
65   r = (char *)&regs + _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 *)&regs + _UPT_reg_offset[reg];
346   if (ptrace(PT_GETREGS, pid, (caddr_t)&regs, 0) == -1)
347     goto badreg;
348   if (write) {
349       memcpy(r, val, sizeof(unw_word_t));
350       if (ptrace(PT_SETREGS, pid, (caddr_t)&regs, 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