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 
6 This file is part of libunwind.
7 
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15 
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH 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 
35 static inline int
arm_exidx_step(struct cursor *c)36 arm_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 
86 int
unw_step(unw_cursor_t *cursor)87 unw_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