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
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH 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
40int
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
278int
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
356int
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