1d4e76214Sopenharmony_ci/* libunwind - a platform-independent unwind library
2d4e76214Sopenharmony_ci   Copyright (C) 2008 CodeSourcery
3d4e76214Sopenharmony_ci   Copyright 2011 Linaro Limited
4d4e76214Sopenharmony_ci   Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
5d4e76214Sopenharmony_ci
6d4e76214Sopenharmony_ciThis file is part of libunwind.
7d4e76214Sopenharmony_ci
8d4e76214Sopenharmony_ciPermission is hereby granted, free of charge, to any person obtaining
9d4e76214Sopenharmony_cia copy of this software and associated documentation files (the
10d4e76214Sopenharmony_ci"Software"), to deal in the Software without restriction, including
11d4e76214Sopenharmony_ciwithout limitation the rights to use, copy, modify, merge, publish,
12d4e76214Sopenharmony_cidistribute, sublicense, and/or sell copies of the Software, and to
13d4e76214Sopenharmony_cipermit persons to whom the Software is furnished to do so, subject to
14d4e76214Sopenharmony_cithe following conditions:
15d4e76214Sopenharmony_ci
16d4e76214Sopenharmony_ciThe above copyright notice and this permission notice shall be
17d4e76214Sopenharmony_ciincluded in all copies or substantial portions of the Software.
18d4e76214Sopenharmony_ci
19d4e76214Sopenharmony_ciTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20d4e76214Sopenharmony_ciEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d4e76214Sopenharmony_ciMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22d4e76214Sopenharmony_ciNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23d4e76214Sopenharmony_ciLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24d4e76214Sopenharmony_ciOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25d4e76214Sopenharmony_ciWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26d4e76214Sopenharmony_ci
27d4e76214Sopenharmony_ci#include "unwind_i.h"
28d4e76214Sopenharmony_ci#include "offsets.h"
29d4e76214Sopenharmony_ci#include "ex_tables.h"
30d4e76214Sopenharmony_ci
31d4e76214Sopenharmony_ci#include <signal.h>
32d4e76214Sopenharmony_ci
33d4e76214Sopenharmony_ci#define arm_exidx_step  UNW_OBJ(arm_exidx_step)
34d4e76214Sopenharmony_ci
35d4e76214Sopenharmony_cistatic inline int
36d4e76214Sopenharmony_ciarm_exidx_step (struct cursor *c)
37d4e76214Sopenharmony_ci{
38d4e76214Sopenharmony_ci  unw_word_t old_ip, old_cfa;
39d4e76214Sopenharmony_ci  uint8_t buf[32];
40d4e76214Sopenharmony_ci  int ret;
41d4e76214Sopenharmony_ci
42d4e76214Sopenharmony_ci  old_ip = c->dwarf.ip;
43d4e76214Sopenharmony_ci  old_cfa = c->dwarf.cfa;
44d4e76214Sopenharmony_ci
45d4e76214Sopenharmony_ci  /* mark PC unsaved */
46d4e76214Sopenharmony_ci  c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
47d4e76214Sopenharmony_ci  unw_word_t ip = c->dwarf.ip;
48d4e76214Sopenharmony_ci  if (c->dwarf.use_prev_instr)
49d4e76214Sopenharmony_ci    /* The least bit denotes thumb/arm mode, clear it. */
50d4e76214Sopenharmony_ci    ip = (ip & ~(unw_word_t)0x1) - 1;
51d4e76214Sopenharmony_ci
52d4e76214Sopenharmony_ci  /* check dynamic info first --- it overrides everything else */
53d4e76214Sopenharmony_ci  ret = unwi_find_dynamic_proc_info (c->dwarf.as, ip, &c->dwarf.pi, 1,
54d4e76214Sopenharmony_ci                                     c->dwarf.as_arg);
55d4e76214Sopenharmony_ci  if (ret == -UNW_ENOINFO)
56d4e76214Sopenharmony_ci    {
57d4e76214Sopenharmony_ci      if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0)
58d4e76214Sopenharmony_ci        return ret;
59d4e76214Sopenharmony_ci    }
60d4e76214Sopenharmony_ci
61d4e76214Sopenharmony_ci  if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
62d4e76214Sopenharmony_ci    return -UNW_ENOINFO;
63d4e76214Sopenharmony_ci
64d4e76214Sopenharmony_ci  ret = arm_exidx_extract (&c->dwarf, buf);
65d4e76214Sopenharmony_ci  if (ret == -UNW_ESTOPUNWIND)
66d4e76214Sopenharmony_ci    return 0;
67d4e76214Sopenharmony_ci  else if (ret < 0)
68d4e76214Sopenharmony_ci    return ret;
69d4e76214Sopenharmony_ci
70d4e76214Sopenharmony_ci  ret = arm_exidx_decode (buf, ret, &c->dwarf);
71d4e76214Sopenharmony_ci  if (ret < 0)
72d4e76214Sopenharmony_ci    return ret;
73d4e76214Sopenharmony_ci
74d4e76214Sopenharmony_ci  if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
75d4e76214Sopenharmony_ci    {
76d4e76214Sopenharmony_ci      Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
77d4e76214Sopenharmony_ci               __FUNCTION__, (long) c->dwarf.ip);
78d4e76214Sopenharmony_ci      return -UNW_EBADFRAME;
79d4e76214Sopenharmony_ci    }
80d4e76214Sopenharmony_ci
81d4e76214Sopenharmony_ci  c->dwarf.pi_valid = 0;
82d4e76214Sopenharmony_ci
83d4e76214Sopenharmony_ci  return (c->dwarf.ip == 0) ? 0 : 1;
84d4e76214Sopenharmony_ci}
85d4e76214Sopenharmony_ci
86d4e76214Sopenharmony_ciint
87d4e76214Sopenharmony_ciunw_step (unw_cursor_t *cursor)
88d4e76214Sopenharmony_ci{
89d4e76214Sopenharmony_ci  struct cursor *c = (struct cursor *) cursor;
90d4e76214Sopenharmony_ci  int ret = -UNW_EUNSPEC;
91d4e76214Sopenharmony_ci
92d4e76214Sopenharmony_ci  Debug (1, "(cursor=%p)\n", c);
93d4e76214Sopenharmony_ci
94d4e76214Sopenharmony_ci  /* Check if this is a signal frame. */
95d4e76214Sopenharmony_ci  if (unw_is_signal_frame (cursor) > 0)
96d4e76214Sopenharmony_ci     return arm_handle_signal_frame (cursor);
97d4e76214Sopenharmony_ci
98d4e76214Sopenharmony_ci#ifdef CONFIG_DEBUG_FRAME
99d4e76214Sopenharmony_ci  /* First, try DWARF-based unwinding. */
100d4e76214Sopenharmony_ci  if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
101d4e76214Sopenharmony_ci    {
102d4e76214Sopenharmony_ci      ret = dwarf_step (&c->dwarf);
103d4e76214Sopenharmony_ci      Debug(1, "dwarf_step()=%d\n", ret);
104d4e76214Sopenharmony_ci
105d4e76214Sopenharmony_ci      if (likely (ret > 0))
106d4e76214Sopenharmony_ci        return 1;
107d4e76214Sopenharmony_ci      else if (unlikely (ret == -UNW_ESTOPUNWIND))
108d4e76214Sopenharmony_ci        return ret;
109d4e76214Sopenharmony_ci
110d4e76214Sopenharmony_ci      if (ret < 0 && ret != -UNW_ENOINFO)
111d4e76214Sopenharmony_ci        {
112d4e76214Sopenharmony_ci          Debug (2, "returning %d\n", ret);
113d4e76214Sopenharmony_ci          return ret;
114d4e76214Sopenharmony_ci        }
115d4e76214Sopenharmony_ci    }
116d4e76214Sopenharmony_ci#endif /* CONFIG_DEBUG_FRAME */
117d4e76214Sopenharmony_ci
118d4e76214Sopenharmony_ci  /* Next, try extbl-based unwinding. */
119d4e76214Sopenharmony_ci  if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
120d4e76214Sopenharmony_ci    {
121d4e76214Sopenharmony_ci      Debug (13, "%s(ret=%d), trying extbl\n",
122d4e76214Sopenharmony_ci             UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "",
123d4e76214Sopenharmony_ci             ret);
124d4e76214Sopenharmony_ci      ret = arm_exidx_step (c);
125d4e76214Sopenharmony_ci      if (ret > 0)
126d4e76214Sopenharmony_ci        return 1;
127d4e76214Sopenharmony_ci      if (ret == -UNW_ESTOPUNWIND || ret == 0)
128d4e76214Sopenharmony_ci        return ret;
129d4e76214Sopenharmony_ci    }
130d4e76214Sopenharmony_ci
131d4e76214Sopenharmony_ci  /* Fall back on APCS frame parsing.
132d4e76214Sopenharmony_ci     Note: This won't work in case the ARM EABI is used. */
133d4e76214Sopenharmony_ci#ifdef __FreeBSD__
134d4e76214Sopenharmony_ci  if (0)
135d4e76214Sopenharmony_ci#else
136d4e76214Sopenharmony_ci  if (unlikely (ret < 0))
137d4e76214Sopenharmony_ci#endif
138d4e76214Sopenharmony_ci    {
139d4e76214Sopenharmony_ci      if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
140d4e76214Sopenharmony_ci        {
141d4e76214Sopenharmony_ci          Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n",
142d4e76214Sopenharmony_ci                 UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "",
143d4e76214Sopenharmony_ci                 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "",
144d4e76214Sopenharmony_ci                 UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "",
145d4e76214Sopenharmony_ci                 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "",
146d4e76214Sopenharmony_ci                 ret);
147d4e76214Sopenharmony_ci          ret = UNW_ESUCCESS;
148d4e76214Sopenharmony_ci          /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
149d4e76214Sopenharmony_ci          unw_word_t instr, i;
150d4e76214Sopenharmony_ci          dwarf_loc_t ip_loc, fp_loc;
151d4e76214Sopenharmony_ci          unw_word_t frame;
152d4e76214Sopenharmony_ci          /* Mark all registers unsaved, since we don't know where
153d4e76214Sopenharmony_ci             they are saved (if at all), except for the EBP and
154d4e76214Sopenharmony_ci             EIP.  */
155d4e76214Sopenharmony_ci          if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
156d4e76214Sopenharmony_ci            {
157d4e76214Sopenharmony_ci              return 0;
158d4e76214Sopenharmony_ci            }
159d4e76214Sopenharmony_ci          for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
160d4e76214Sopenharmony_ci            c->dwarf.loc[i] = DWARF_NULL_LOC;
161d4e76214Sopenharmony_ci          }
162d4e76214Sopenharmony_ci          if (frame)
163d4e76214Sopenharmony_ci            {
164d4e76214Sopenharmony_ci              if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
165d4e76214Sopenharmony_ci                {
166d4e76214Sopenharmony_ci                  return 0;
167d4e76214Sopenharmony_ci                }
168d4e76214Sopenharmony_ci              instr -= 8;
169d4e76214Sopenharmony_ci              if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
170d4e76214Sopenharmony_ci                {
171d4e76214Sopenharmony_ci                  return 0;
172d4e76214Sopenharmony_ci                }
173d4e76214Sopenharmony_ci              if ((instr & 0xFFFFD800) == 0xE92DD800)
174d4e76214Sopenharmony_ci                {
175d4e76214Sopenharmony_ci                  /* Standard APCS frame. */
176d4e76214Sopenharmony_ci                  ip_loc = DWARF_LOC(frame - 4, 0);
177d4e76214Sopenharmony_ci                  fp_loc = DWARF_LOC(frame - 12, 0);
178d4e76214Sopenharmony_ci                }
179d4e76214Sopenharmony_ci              else
180d4e76214Sopenharmony_ci                {
181d4e76214Sopenharmony_ci                  /* Codesourcery optimized normal frame. */
182d4e76214Sopenharmony_ci                  ip_loc = DWARF_LOC(frame, 0);
183d4e76214Sopenharmony_ci                  fp_loc = DWARF_LOC(frame - 4, 0);
184d4e76214Sopenharmony_ci                }
185d4e76214Sopenharmony_ci              if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
186d4e76214Sopenharmony_ci                {
187d4e76214Sopenharmony_ci                  return 0;
188d4e76214Sopenharmony_ci                }
189d4e76214Sopenharmony_ci              c->dwarf.loc[UNW_ARM_R12] = ip_loc;
190d4e76214Sopenharmony_ci              c->dwarf.loc[UNW_ARM_R11] = fp_loc;
191d4e76214Sopenharmony_ci              c->dwarf.pi_valid = 0;
192d4e76214Sopenharmony_ci              Debug(15, "ip=%x\n", c->dwarf.ip);
193d4e76214Sopenharmony_ci            }
194d4e76214Sopenharmony_ci          else
195d4e76214Sopenharmony_ci            {
196d4e76214Sopenharmony_ci              ret = -UNW_ENOINFO;
197d4e76214Sopenharmony_ci            }
198d4e76214Sopenharmony_ci        }
199d4e76214Sopenharmony_ci    }
200d4e76214Sopenharmony_ci  return ret == -UNW_ENOINFO ? 0 : ret;
201d4e76214Sopenharmony_ci}
202