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