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*)&regs) == -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*)&regs) == -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 *)&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 #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 = &regs;
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