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 #include "map_info.h"
34
35 #define arm_exidx_step UNW_OBJ(arm_exidx_step)
36 static inline int
arm_exidx_step(struct cursor * c)37 arm_exidx_step (struct cursor *c)
38 {
39 unw_word_t old_ip, old_cfa;
40 uint8_t buf[32];
41 int ret;
42
43 old_ip = c->dwarf.ip;
44 old_cfa = c->dwarf.cfa;
45
46 /* mark PC unsaved */
47 c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
48 unw_word_t ip = c->dwarf.ip;
49 if (c->dwarf.use_prev_instr)
50 /* The least bit denotes thumb/arm mode, clear it. */
51 ip = (ip & ~(unw_word_t)0x1) - 1;
52
53 /* check dynamic info first --- it overrides everything else */
54 ret = unwi_find_dynamic_proc_info (c->dwarf.as, ip, &c->dwarf.pi, 1,
55 c->dwarf.as_arg);
56 if (ret == -UNW_ENOINFO)
57 {
58 if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0)
59 return ret;
60 }
61
62 if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
63 return -UNW_ENOINFO;
64
65 ret = arm_exidx_extract (&c->dwarf, buf);
66 if (ret == -UNW_ESTOPUNWIND)
67 return 0;
68 else if (ret < 0)
69 return ret;
70
71 ret = arm_exidx_decode (buf, ret, &c->dwarf);
72 if (ret < 0)
73 return ret;
74
75 if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
76 {
77 Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
78 __FUNCTION__, (long) c->dwarf.ip);
79 return -UNW_EBADFRAME;
80 }
81
82 c->dwarf.pi_valid = 0;
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 /* Add for using lr backtrace when pc is zero */
97 ret = arm_handle_signal_frame (cursor);
98 if ( c->dwarf.ip == 0x0 )
99 {
100 unw_word_t lr;
101 if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R14], &lr) >= 0)
102 {
103 if (lr != c->dwarf.ip)
104 {
105 Debug(1, "fix ip = 0 action \n");
106 c->dwarf.ip = lr;
107 return ret;
108 }
109 }
110 }
111 return ret;
112 /* Add for using lr backtrace when pc is zero */
113 }
114
115 #ifdef CONFIG_DEBUG_FRAME
116 /* First, try DWARF-based unwinding. */
117 if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
118 {
119 ret = dwarf_step (&c->dwarf);
120 Debug(1, "dwarf_step()=%d\n", ret);
121
122 if (likely (ret > 0))
123 return 1;
124 else if (unlikely (ret == -UNW_ESTOPUNWIND))
125 return ret;
126
127 if (ret < 0 && ret != -UNW_ENOINFO)
128 {
129 Debug (2, "returning %d\n", ret);
130 return ret;
131 }
132 }
133 #endif /* CONFIG_DEBUG_FRAME */
134
135 /* Next, try extbl-based unwinding. */
136 if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
137 {
138 Debug (13, "%s(ret=%d), trying extbl\n",
139 UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "",
140 ret);
141 ret = arm_exidx_step (c);
142 if (ret > 0)
143 return 1;
144 if (ret == -UNW_ESTOPUNWIND || ret == 0)
145 ret = -1; // try frame pointer
146 }
147
148 /* Fall back on APCS frame parsing.
149 Note: This won't work in case the ARM EABI is used. */
150 #ifdef __FreeBSD__
151 if (0)
152 #else
153 if (unlikely (ret < 0))
154 #endif
155 {
156 if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
157 {
158 Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n",
159 UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "",
160 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "",
161 UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "",
162 (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "",
163 ret);
164 ret = UNW_ESUCCESS;
165 /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
166 unw_word_t instr, i;
167 dwarf_loc_t ip_loc, fp_loc;
168 unw_word_t frame;
169 /* Mark all registers unsaved, since we don't know where
170 they are saved (if at all), except for the EBP and
171 EIP. */
172 if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
173 {
174 return 0;
175 }
176 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
177 c->dwarf.loc[i] = DWARF_NULL_LOC;
178 }
179 if (frame)
180 {
181 #ifndef CC_IS_CLANG
182 if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
183 {
184 return 0;
185 }
186 instr -= 8;
187 if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
188 {
189 return 0;
190 }
191 if ((instr & 0xFFFFD800) == 0xE92DD800)
192 {
193 /* Standard APCS frame. */
194 ip_loc = DWARF_LOC(frame - 4, 0);
195 fp_loc = DWARF_LOC(frame - 12, 0);
196 }
197 else
198 {
199 /* Codesourcery optimized normal frame. */
200 ip_loc = DWARF_LOC(frame, 0);
201 fp_loc = DWARF_LOC(frame - 4, 0);
202 }
203 #else
204 ip_loc = DWARF_LOC(frame + 4, 0);
205 fp_loc = DWARF_LOC(frame, 0);
206 if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
207 {
208 return 0;
209 }
210 c->dwarf.loc[UNW_ARM_R12] = ip_loc;
211 c->dwarf.loc[UNW_ARM_R11] = fp_loc;
212 return 1;
213 #endif
214 if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
215 {
216 return 0;
217 }
218 c->dwarf.loc[UNW_ARM_R12] = ip_loc;
219 c->dwarf.loc[UNW_ARM_R11] = fp_loc;
220 c->dwarf.pi_valid = 0;
221 Debug(15, "ip=%x\n", c->dwarf.ip);
222 }
223 else
224 {
225 ret = -UNW_ENOINFO;
226 }
227 }
228 }
229 return ret == -UNW_ENOINFO ? 0 : ret;
230 }
231