1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2008 CodeSourcery
3
4This file is part of libunwind.
5
6Permission is hereby granted, free of charge, to any person obtaining
7a copy of this software and associated documentation files (the
8"Software"), to deal in the Software without restriction, including
9without limitation the rights to use, copy, modify, merge, publish,
10distribute, sublicense, and/or sell copies of the Software, and to
11permit persons to whom the Software is furnished to do so, subject to
12the following conditions:
13
14The above copyright notice and this permission notice shall be
15included in all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
24
25#include <stdlib.h>
26#include <string.h>
27
28#include "unwind_i.h"
29
30#ifdef UNW_REMOTE_ONLY
31
32/* unw_local_addr_space is a NULL pointer in this case.  */
33PROTECTED unw_addr_space_t unw_local_addr_space;
34
35#else /* !UNW_REMOTE_ONLY */
36
37static struct unw_addr_space local_addr_space;
38
39PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
40
41/* Return the address of the 64-bit slot in UC for REG (even for o32,
42   where registers are 32-bit, the slots are still 64-bit).  */
43
44static inline void *
45uc_addr (ucontext_t *uc, int reg)
46{
47  if (reg >= UNW_MIPS_R0 && reg < UNW_MIPS_R0 + 32)
48    return &uc->uc_mcontext.gregs[reg - UNW_MIPS_R0];
49  else if (reg == UNW_MIPS_PC)
50    return &uc->uc_mcontext.pc;
51  else
52    return NULL;
53}
54
55# ifdef UNW_LOCAL_ONLY
56
57HIDDEN void *
58tdep_uc_addr (ucontext_t *uc, int reg)
59{
60  char *addr = uc_addr (uc, reg);
61
62  if (reg >= UNW_MIPS_R0 && reg <= UNW_MIPS_R31
63      && tdep_big_endian (unw_local_addr_space)
64      && unw_local_addr_space->abi == UNW_MIPS_ABI_O32)
65    addr += 4;
66
67  return addr;
68}
69
70# endif /* UNW_LOCAL_ONLY */
71
72HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
73
74/* XXX fix me: there is currently no way to locate the dyn-info list
75       by a remote unwinder.  On ia64, this is done via a special
76       unwind-table entry.  Perhaps something similar can be done with
77       DWARF2 unwind info.  */
78
79static void
80put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
81{
82  /* it's a no-op */
83}
84
85static int
86get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
87			void *arg)
88{
89  *dyn_info_list_addr = (unw_word_t) (intptr_t) &_U_dyn_info_list;
90  return 0;
91}
92
93static int
94access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
95	    void *arg)
96{
97  if (write)
98    {
99      /* ANDROID support update. */
100#ifdef UNW_LOCAL_ONLY
101      if (map_local_is_writable (addr, sizeof(unw_word_t)))
102        {
103#endif
104          Debug (16, "mem[%llx] <- %llx\n", (long long) addr, (long long) *val);
105          *(unw_word_t *) (uintptr_t) addr = *val;
106#ifdef UNW_LOCAL_ONLY
107        }
108      else
109        {
110          Debug (16, "Unwritable memory mem[%llx] <- %llx\n", (long long) addr,
111                 (long long) *val);
112          return -1;
113        }
114#endif
115      /* End of ANDROID update. */
116    }
117  else
118    {
119      /* ANDROID support update. */
120#ifdef UNW_LOCAL_ONLY
121      if (map_local_is_readable (addr, sizeof(unw_word_t)))
122        {
123#endif
124          *val = *(unw_word_t *) (uintptr_t) addr;
125          Debug (16, "mem[%llx] -> %llx\n", (long long) addr, (long long) *val);
126#ifdef UNW_LOCAL_ONLY
127        }
128      else
129        {
130          Debug (16, "Unreadable memory mem[%llx] -> XXX\n", (long long) addr);
131          return -1;
132        }
133#endif
134      /* End of ANDROID update. */
135    }
136  return 0;
137}
138
139static int
140access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
141	    void *arg)
142{
143  unw_word_t *addr;
144  ucontext_t *uc = arg;
145
146  if (unw_is_fpreg (reg))
147    goto badreg;
148
149  Debug (16, "reg = %s\n", unw_regname (reg));
150  if (!(addr = uc_addr (uc, reg)))
151    goto badreg;
152
153  if (write)
154    {
155      *(unw_word_t *) (intptr_t) addr = (mips_reg_t) *val;
156      Debug (12, "%s <- %llx\n", unw_regname (reg), (long long) *val);
157    }
158  else
159    {
160      *val = (mips_reg_t) *(unw_word_t *) (intptr_t) addr;
161      Debug (12, "%s -> %llx\n", unw_regname (reg), (long long) *val);
162    }
163  return 0;
164
165 badreg:
166  Debug (1, "bad register number %u\n", reg);
167  return -UNW_EBADREG;
168}
169
170static int
171access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
172	      int write, void *arg)
173{
174  ucontext_t *uc = arg;
175  unw_fpreg_t *addr;
176
177  if (!unw_is_fpreg (reg))
178    goto badreg;
179
180  if (!(addr = uc_addr (uc, reg)))
181    goto badreg;
182
183  if (write)
184    {
185      Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
186	     ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
187      *(unw_fpreg_t *) (intptr_t) addr = *val;
188    }
189  else
190    {
191      *val = *(unw_fpreg_t *) (intptr_t) addr;
192      Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
193	     ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
194    }
195  return 0;
196
197 badreg:
198  Debug (1, "bad register number %u\n", reg);
199  /* attempt to access a non-preserved register */
200  return -UNW_EBADREG;
201}
202
203static int
204get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
205		      char *buf, size_t buf_len, unw_word_t *offp,
206		      void *arg)
207{
208
209  return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp, arg);
210}
211
212static int
213access_mem_unrestricted (unw_addr_space_t as, unw_word_t addr, unw_word_t *val,
214                         int write, void *arg)
215{
216  if (write)
217    return -1;
218
219  *val = *(unw_word_t *) (uintptr_t) addr;
220  Debug (16, "mem[%llx] <- %llx\n", (long long) addr, (long long) *val);
221  return 0;
222}
223
224// This initializes just enough of the address space to call the
225// access memory function.
226PROTECTED void
227unw_local_access_addr_space_init (unw_addr_space_t as)
228{
229  memset (as, 0, sizeof (*as));
230  as->acc.access_mem = access_mem_unrestricted;
231}
232
233HIDDEN void
234mips_local_addr_space_init (void)
235{
236  memset (&local_addr_space, 0, sizeof (local_addr_space));
237  local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
238#if _MIPS_SIM == _ABIO32
239  local_addr_space.abi = UNW_MIPS_ABI_O32;
240#elif _MIPS_SIM == _ABIN32
241  local_addr_space.abi = UNW_MIPS_ABI_N32;
242#elif _MIPS_SIM == _ABI64
243  local_addr_space.abi = UNW_MIPS_ABI_N64;
244#else
245# error Unsupported ABI
246#endif
247  local_addr_space.addr_size = sizeof (void *);
248  local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
249  local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
250  local_addr_space.acc.put_unwind_info = put_unwind_info;
251  local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
252  local_addr_space.acc.access_mem = access_mem;
253  local_addr_space.acc.access_reg = access_reg;
254  local_addr_space.acc.access_fpreg = access_fpreg;
255  local_addr_space.acc.resume = NULL;  /* mips_local_resume?  FIXME!  */
256  local_addr_space.acc.get_proc_name = get_static_proc_name;
257  unw_flush_cache (&local_addr_space, 0, 0);
258
259  map_local_init ();
260}
261
262#endif /* !UNW_REMOTE_ONLY */
263