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  unw_word_t ip = c->dwarf.ip;
48  if (c->dwarf.use_prev_instr)
49    /* The least bit denotes thumb/arm mode, clear it. */
50    ip = (ip & ~(unw_word_t)0x1) - 1;
51
52  /* check dynamic info first --- it overrides everything else */
53  ret = unwi_find_dynamic_proc_info (c->dwarf.as, ip, &c->dwarf.pi, 1,
54                                     c->dwarf.as_arg);
55  if (ret == -UNW_ENOINFO)
56    {
57      if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0)
58        return ret;
59    }
60
61  if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
62    return -UNW_ENOINFO;
63
64  ret = arm_exidx_extract (&c->dwarf, buf);
65  if (ret == -UNW_ESTOPUNWIND)
66    return 0;
67  else if (ret < 0)
68    return ret;
69
70  ret = arm_exidx_decode (buf, ret, &c->dwarf);
71  if (ret < 0)
72    return ret;
73
74  if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
75    {
76      Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
77               __FUNCTION__, (long) c->dwarf.ip);
78      return -UNW_EBADFRAME;
79    }
80
81  c->dwarf.pi_valid = 0;
82
83  return (c->dwarf.ip == 0) ? 0 : 1;
84}
85
86int
87unw_step (unw_cursor_t *cursor)
88{
89  struct cursor *c = (struct cursor *) cursor;
90  int ret = -UNW_EUNSPEC;
91
92  Debug (1, "(cursor=%p)\n", c);
93
94  /* Check if this is a signal frame. */
95  if (unw_is_signal_frame (cursor) > 0)
96     return arm_handle_signal_frame (cursor);
97
98#ifdef CONFIG_DEBUG_FRAME
99  /* First, try DWARF-based unwinding. */
100  if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
101    {
102      ret = dwarf_step (&c->dwarf);
103      Debug(1, "dwarf_step()=%d\n", ret);
104
105      if (likely (ret > 0))
106        return 1;
107      else if (unlikely (ret == -UNW_ESTOPUNWIND))
108        return ret;
109
110      if (ret < 0 && ret != -UNW_ENOINFO)
111        {
112          Debug (2, "returning %d\n", ret);
113          return ret;
114        }
115    }
116#endif /* CONFIG_DEBUG_FRAME */
117
118  /* Next, try extbl-based unwinding. */
119  if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
120    {
121      Debug (13, "%s(ret=%d), trying extbl\n",
122             UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "",
123             ret);
124      ret = arm_exidx_step (c);
125      if (ret > 0)
126        return 1;
127      if (ret == -UNW_ESTOPUNWIND || ret == 0)
128        return ret;
129    }
130
131  /* Fall back on APCS frame parsing.
132     Note: This won't work in case the ARM EABI is used. */
133#ifdef __FreeBSD__
134  if (0)
135#else
136  if (unlikely (ret < 0))
137#endif
138    {
139      if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
140        {
141          Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n",
142                 UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "",
143                 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "",
144                 UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "",
145                 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "",
146                 ret);
147          ret = UNW_ESUCCESS;
148          /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
149          unw_word_t instr, i;
150          dwarf_loc_t ip_loc, fp_loc;
151          unw_word_t frame;
152          /* Mark all registers unsaved, since we don't know where
153             they are saved (if at all), except for the EBP and
154             EIP.  */
155          if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
156            {
157              return 0;
158            }
159          for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
160            c->dwarf.loc[i] = DWARF_NULL_LOC;
161          }
162          if (frame)
163            {
164              if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
165                {
166                  return 0;
167                }
168              instr -= 8;
169              if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
170                {
171                  return 0;
172                }
173              if ((instr & 0xFFFFD800) == 0xE92DD800)
174                {
175                  /* Standard APCS frame. */
176                  ip_loc = DWARF_LOC(frame - 4, 0);
177                  fp_loc = DWARF_LOC(frame - 12, 0);
178                }
179              else
180                {
181                  /* Codesourcery optimized normal frame. */
182                  ip_loc = DWARF_LOC(frame, 0);
183                  fp_loc = DWARF_LOC(frame - 4, 0);
184                }
185              if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
186                {
187                  return 0;
188                }
189              c->dwarf.loc[UNW_ARM_R12] = ip_loc;
190              c->dwarf.loc[UNW_ARM_R11] = fp_loc;
191              c->dwarf.pi_valid = 0;
192              Debug(15, "ip=%x\n", c->dwarf.ip);
193            }
194          else
195            {
196              ret = -UNW_ENOINFO;
197            }
198        }
199    }
200  return ret == -UNW_ENOINFO ? 0 : ret;
201}
202