1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2008 CodeSourcery
3   Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26#include <stdlib.h>
27#include <string.h>
28
29#include "unwind_i.h"
30
31#ifdef UNW_REMOTE_ONLY
32
33/* unw_local_addr_space is a NULL pointer in this case.  */
34PROTECTED unw_addr_space_t unw_local_addr_space;
35
36#else /* !UNW_REMOTE_ONLY */
37
38static struct unw_addr_space local_addr_space;
39
40PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
41
42static inline void *
43uc_addr (ucontext_t *uc, int reg)
44{
45  if (reg >= UNW_SH_R0 && reg <= UNW_SH_PR)
46    return &uc->uc_mcontext.gregs[reg];
47  else
48    return NULL;
49}
50
51# ifdef UNW_LOCAL_ONLY
52
53HIDDEN void *
54tdep_uc_addr (ucontext_t *uc, int reg)
55{
56  return uc_addr (uc, reg);
57}
58
59# endif /* UNW_LOCAL_ONLY */
60
61HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
62
63/* XXX fix me: there is currently no way to locate the dyn-info list
64       by a remote unwinder.  On ia64, this is done via a special
65       unwind-table entry.  Perhaps something similar can be done with
66       DWARF2 unwind info.  */
67
68static void
69put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
70{
71  /* it's a no-op */
72}
73
74static int
75get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
76                        void *arg)
77{
78  *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
79  return 0;
80}
81
82static int
83access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
84            void *arg)
85{
86  if (write)
87    {
88      Debug (16, "mem[%x] <- %x\n", addr, *val);
89      *(unw_word_t *) addr = *val;
90    }
91  else
92    {
93      *val = *(unw_word_t *) addr;
94      Debug (16, "mem[%x] -> %x\n", addr, *val);
95    }
96  return 0;
97}
98
99static int
100access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
101            void *arg)
102{
103  unw_word_t *addr;
104  ucontext_t *uc = arg;
105
106  if (unw_is_fpreg (reg))
107    goto badreg;
108
109  if (!(addr = uc_addr (uc, reg)))
110    goto badreg;
111
112  if (write)
113    {
114      *(unw_word_t *) addr = *val;
115      Debug (12, "%s <- %x\n", unw_regname (reg), *val);
116    }
117  else
118    {
119      *val = *(unw_word_t *) addr;
120      Debug (12, "%s -> %x\n", unw_regname (reg), *val);
121    }
122  return 0;
123
124 badreg:
125  Debug (1, "bad register number %u\n", reg);
126  return -UNW_EBADREG;
127}
128
129static int
130access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
131              int write, void *arg)
132{
133  ucontext_t *uc = arg;
134  unw_fpreg_t *addr;
135
136  if (!unw_is_fpreg (reg))
137    goto badreg;
138
139  if (!(addr = uc_addr (uc, reg)))
140    goto badreg;
141
142  if (write)
143    {
144      Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
145             ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
146      *(unw_fpreg_t *) addr = *val;
147    }
148  else
149    {
150      *val = *(unw_fpreg_t *) addr;
151      Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
152             ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
153    }
154  return 0;
155
156 badreg:
157  Debug (1, "bad register number %u\n", reg);
158  /* attempt to access a non-preserved register */
159  return -UNW_EBADREG;
160}
161
162static int
163get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
164                      char *buf, size_t buf_len, unw_word_t *offp,
165                      void *arg)
166{
167  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
168}
169
170HIDDEN void
171sh_local_addr_space_init (void)
172{
173  memset (&local_addr_space, 0, sizeof (local_addr_space));
174  local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
175  local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
176  local_addr_space.acc.put_unwind_info = put_unwind_info;
177  local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
178  local_addr_space.acc.access_mem = access_mem;
179  local_addr_space.acc.access_reg = access_reg;
180  local_addr_space.acc.access_fpreg = access_fpreg;
181  local_addr_space.acc.resume = sh_local_resume;
182  local_addr_space.acc.get_proc_name = get_static_proc_name;
183  unw_flush_cache (&local_addr_space, 0, 0);
184}
185
186#endif /* !UNW_REMOTE_ONLY */
187