1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2008 CodeSourcery
3   Copyright 2011 Linaro Limited
4   Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
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 "unwind_i.h"
28#include "offsets.h"
29#include "ex_tables.h"
30
31#include <signal.h>
32
33#define arm_exidx_step	UNW_OBJ(arm_exidx_step)
34
35static inline int
36arm_exidx_step (struct cursor *c)
37{
38  unw_word_t old_ip, old_cfa;
39  uint8_t buf[32];
40  int ret;
41
42  old_ip = c->dwarf.ip;
43  old_cfa = c->dwarf.cfa;
44
45  /* mark PC unsaved */
46  c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
47
48  if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0)
49     return ret;
50
51  if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
52    return -UNW_ENOINFO;
53
54  ret = arm_exidx_extract (&c->dwarf, buf);
55  if (ret == -UNW_ESTOPUNWIND)
56    return 0;
57  else if (ret < 0)
58    return ret;
59
60  ret = arm_exidx_decode (buf, ret, &c->dwarf);
61  if (ret < 0)
62    return ret;
63
64  if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
65    {
66      Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
67	       __FUNCTION__, (long) c->dwarf.ip);
68      return -UNW_EBADFRAME;
69    }
70
71  c->dwarf.pi_valid = 0;
72
73  return (c->dwarf.ip == 0) ? 0 : 1;
74}
75
76/* ANDROID support update. */
77
78/* When taking a step back up the stack, the pc will point to the next
79 * instruction to execute, not the currently executing instruction. This
80 * function adjusts the pc to the currently executing instruction.
81 */
82static void adjust_ip(struct cursor *c)
83{
84  unw_word_t ip, value;
85  ip = c->dwarf.ip;
86
87  if (ip)
88    {
89      int adjust = 4;
90      if (ip & 1)
91        {
92          /* Thumb instructions, the currently executing instruction could be
93          * 2 or 4 bytes, so adjust appropriately.
94          */
95          unw_addr_space_t as;
96          unw_accessors_t *a;
97          void *arg;
98
99          as = c->dwarf.as;
100          a = unw_get_accessors (as);
101          arg = c->dwarf.as_arg;
102
103          if (ip < 5 || (*a->access_mem) (as, ip-5, &value, 0, arg) < 0 ||
104              (value & 0xe000f000) != 0xe000f000)
105            adjust = 2;
106        }
107      c->dwarf.ip -= adjust;
108    }
109}
110/* End of ANDROID update. */
111
112PROTECTED int
113unw_handle_signal_frame (unw_cursor_t *cursor)
114{
115  struct cursor *c = (struct cursor *) cursor;
116  int ret;
117  unw_word_t sc_addr, sp, sp_addr = c->dwarf.cfa;
118  struct dwarf_loc sp_loc = DWARF_LOC (sp_addr, 0);
119
120  if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0)
121    return -UNW_EUNSPEC;
122
123  /* Obtain signal frame type (non-RT or RT). */
124  ret = unw_is_signal_frame (cursor);
125
126  /* Save the SP and PC to be able to return execution at this point
127     later in time (unw_resume).  */
128  c->sigcontext_sp = c->dwarf.cfa;
129  c->sigcontext_pc = c->dwarf.ip;
130
131  /* Since kernel version 2.6.18 the non-RT signal frame starts with a
132     ucontext while the RT signal frame starts with a siginfo, followed
133     by a sigframe whose first element is an ucontext.
134     Prior 2.6.18 the non-RT signal frame starts with a sigcontext while
135     the RT signal frame starts with two pointers followed by a siginfo
136     and an ucontext. The first pointer points to the start of the siginfo
137     structure and the second one to the ucontext structure.  */
138
139  if (ret == 1)
140    {
141      /* Handle non-RT signal frames. Check if the first word on the stack
142	 is the magic number.  */
143      if (sp == 0x5ac3c35a)
144	{
145	  c->sigcontext_format = ARM_SCF_LINUX_SIGFRAME;
146	  sc_addr = sp_addr + LINUX_UC_MCONTEXT_OFF;
147	}
148      else
149	{
150	  c->sigcontext_format = ARM_SCF_LINUX_OLD_SIGFRAME;
151	  sc_addr = sp_addr;
152	}
153    }
154  else if (ret == 2)
155    {
156      /* Handle RT signal frames. Check if the first word on the stack is a
157	 pointer to the siginfo structure.  */
158      if (sp == sp_addr + 8)
159	{
160	  c->sigcontext_format = ARM_SCF_LINUX_OLD_RT_SIGFRAME;
161	  sc_addr = sp_addr + 8 + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
162	}
163      else
164	{
165	  c->sigcontext_format = ARM_SCF_LINUX_RT_SIGFRAME;
166	  sc_addr = sp_addr + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
167	}
168    }
169  else
170    return -UNW_EUNSPEC;
171
172  c->sigcontext_addr = sc_addr;
173
174  /* Update the dwarf cursor.
175     Set the location of the registers to the corresponding addresses of the
176     uc_mcontext / sigcontext structure contents.  */
177  c->dwarf.loc[UNW_ARM_R0] = DWARF_LOC (sc_addr + LINUX_SC_R0_OFF, 0);
178  c->dwarf.loc[UNW_ARM_R1] = DWARF_LOC (sc_addr + LINUX_SC_R1_OFF, 0);
179  c->dwarf.loc[UNW_ARM_R2] = DWARF_LOC (sc_addr + LINUX_SC_R2_OFF, 0);
180  c->dwarf.loc[UNW_ARM_R3] = DWARF_LOC (sc_addr + LINUX_SC_R3_OFF, 0);
181  c->dwarf.loc[UNW_ARM_R4] = DWARF_LOC (sc_addr + LINUX_SC_R4_OFF, 0);
182  c->dwarf.loc[UNW_ARM_R5] = DWARF_LOC (sc_addr + LINUX_SC_R5_OFF, 0);
183  c->dwarf.loc[UNW_ARM_R6] = DWARF_LOC (sc_addr + LINUX_SC_R6_OFF, 0);
184  c->dwarf.loc[UNW_ARM_R7] = DWARF_LOC (sc_addr + LINUX_SC_R7_OFF, 0);
185  c->dwarf.loc[UNW_ARM_R8] = DWARF_LOC (sc_addr + LINUX_SC_R8_OFF, 0);
186  c->dwarf.loc[UNW_ARM_R9] = DWARF_LOC (sc_addr + LINUX_SC_R9_OFF, 0);
187  c->dwarf.loc[UNW_ARM_R10] = DWARF_LOC (sc_addr + LINUX_SC_R10_OFF, 0);
188  c->dwarf.loc[UNW_ARM_R11] = DWARF_LOC (sc_addr + LINUX_SC_FP_OFF, 0);
189  c->dwarf.loc[UNW_ARM_R12] = DWARF_LOC (sc_addr + LINUX_SC_IP_OFF, 0);
190  c->dwarf.loc[UNW_ARM_R13] = DWARF_LOC (sc_addr + LINUX_SC_SP_OFF, 0);
191  c->dwarf.loc[UNW_ARM_R14] = DWARF_LOC (sc_addr + LINUX_SC_LR_OFF, 0);
192  c->dwarf.loc[UNW_ARM_R15] = DWARF_LOC (sc_addr + LINUX_SC_PC_OFF, 0);
193
194  /* Set SP/CFA and PC/IP.  */
195  dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R13], &c->dwarf.cfa);
196  dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R15], &c->dwarf.ip);
197
198  c->dwarf.pi_valid = 0;
199
200  return 1;
201}
202
203PROTECTED int
204unw_step (unw_cursor_t *cursor)
205{
206  struct cursor *c = (struct cursor *) cursor;
207  int ret = -UNW_EUNSPEC;
208
209  Debug (1, "(cursor=%p)\n", c);
210
211  /* Check if this is a signal frame. */
212  if (unw_is_signal_frame (cursor))
213    {
214      ret = unw_handle_signal_frame (cursor);
215    }
216
217#ifdef CONFIG_DEBUG_FRAME
218  /* First, try DWARF-based unwinding. */
219  if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
220    {
221      ret = dwarf_step (&c->dwarf);
222      Debug(1, "dwarf_step()=%d\n", ret);
223
224      if (likely (ret > 0))
225        ret = 1;
226      else if (unlikely (ret == -UNW_ESTOPUNWIND))
227        ret = 0;
228    }
229#endif /* CONFIG_DEBUG_FRAME */
230
231  /* Next, try extbl-based unwinding. */
232  if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
233    {
234      ret = arm_exidx_step (c);
235      if (ret > 0)
236	ret = 1;
237      if (ret == -UNW_ESTOPUNWIND || ret == 0)
238	ret = 0;
239    }
240
241  /* Fall back on APCS frame parsing.
242     Note: This won't work in case the ARM EABI is used. */
243  if (unlikely (ret < 0))
244    {
245      if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
246        {
247          ret = UNW_ESUCCESS;
248          /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
249          unw_word_t instr, i;
250          Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
251          dwarf_loc_t ip_loc, fp_loc;
252          unw_word_t frame;
253          /* Mark all registers unsaved, since we don't know where
254             they are saved (if at all), except for the EBP and
255             EIP.  */
256          if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
257            {
258              return 0;
259            }
260          for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
261            c->dwarf.loc[i] = DWARF_NULL_LOC;
262          }
263          if (frame)
264            {
265              if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
266                {
267                  return 0;
268                }
269              instr -= 8;
270              if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
271                {
272                  return 0;
273                }
274              if ((instr & 0xFFFFD800) == 0xE92DD800)
275                {
276                  /* Standard APCS frame. */
277                  ip_loc = DWARF_LOC(frame - 4, 0);
278                  fp_loc = DWARF_LOC(frame - 12, 0);
279                }
280              else
281                {
282                  /* Codesourcery optimized normal frame. */
283                  ip_loc = DWARF_LOC(frame, 0);
284                  fp_loc = DWARF_LOC(frame - 4, 0);
285                }
286              if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
287                {
288                  return 0;
289                }
290              c->dwarf.loc[UNW_ARM_R12] = ip_loc;
291              c->dwarf.loc[UNW_ARM_R11] = fp_loc;
292              c->dwarf.pi_valid = 0;
293              Debug(15, "ip=%x\n", c->dwarf.ip);
294            }
295          else
296            {
297              ret = -UNW_ENOINFO;
298            }
299        }
300    }
301
302  /* ANDROID support update. */
303  if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_LR) && c->dwarf.frame == 0)
304    {
305      /* If this is the first frame, the code may be executing garbage
306       * in the middle of nowhere. In this case, try using the lr as
307       * the pc.
308       */
309      unw_word_t lr;
310      if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R14], &lr) >= 0)
311        {
312          if (lr != c->dwarf.ip)
313            {
314              ret = 1;
315              c->dwarf.ip = lr;
316            }
317        }
318    }
319  /* End of ANDROID update. */
320
321  if (ret >= 0)
322    {
323      c->dwarf.frame++;
324      adjust_ip(c);
325    }
326  return ret == -UNW_ENOINFO ? 0 : ret;
327}
328