Make UEFI boot-platform build again
[haiku.git] / src / libs / libunwind / arm / Gstep.c
blob79f2dd2204d30c1daa193dfe91ce83a78ad3ebdb
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>
6 This file is part of libunwind.
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:
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
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. */
27 #include "unwind_i.h"
28 #include "offsets.h"
29 #include "ex_tables.h"
31 #include <signal.h>
33 #define arm_exidx_step UNW_OBJ(arm_exidx_step)
35 static inline int
36 arm_exidx_step (struct cursor *c)
38 unw_word_t old_ip, old_cfa;
39 uint8_t buf[32];
40 int ret;
42 old_ip = c->dwarf.ip;
43 old_cfa = c->dwarf.cfa;
45 /* mark PC unsaved */
46 c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
48 if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0)
49 return ret;
51 if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
52 return -UNW_ENOINFO;
54 ret = arm_exidx_extract (&c->dwarf, buf);
55 if (ret == -UNW_ESTOPUNWIND)
56 return 0;
57 else if (ret < 0)
58 return ret;
60 ret = arm_exidx_decode (buf, ret, &c->dwarf);
61 if (ret < 0)
62 return ret;
64 if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
66 Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
67 __FUNCTION__, (long) c->dwarf.ip);
68 return -UNW_EBADFRAME;
71 c->dwarf.pi_valid = 0;
73 return (c->dwarf.ip == 0) ? 0 : 1;
76 PROTECTED int
77 unw_handle_signal_frame (unw_cursor_t *cursor)
79 struct cursor *c = (struct cursor *) cursor;
80 int ret;
81 unw_word_t sc_addr, sp, sp_addr = c->dwarf.cfa;
82 struct dwarf_loc sp_loc = DWARF_LOC (sp_addr, 0);
84 if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0)
85 return -UNW_EUNSPEC;
87 /* Obtain signal frame type (non-RT or RT). */
88 ret = unw_is_signal_frame (cursor);
90 /* Save the SP and PC to be able to return execution at this point
91 later in time (unw_resume). */
92 c->sigcontext_sp = c->dwarf.cfa;
93 c->sigcontext_pc = c->dwarf.ip;
95 /* Since kernel version 2.6.18 the non-RT signal frame starts with a
96 ucontext while the RT signal frame starts with a siginfo, followed
97 by a sigframe whose first element is an ucontext.
98 Prior 2.6.18 the non-RT signal frame starts with a sigcontext while
99 the RT signal frame starts with two pointers followed by a siginfo
100 and an ucontext. The first pointer points to the start of the siginfo
101 structure and the second one to the ucontext structure. */
103 if (ret == 1)
105 /* Handle non-RT signal frames. Check if the first word on the stack
106 is the magic number. */
107 if (sp == 0x5ac3c35a)
109 c->sigcontext_format = ARM_SCF_LINUX_SIGFRAME;
110 sc_addr = sp_addr + LINUX_UC_MCONTEXT_OFF;
112 else
114 c->sigcontext_format = ARM_SCF_LINUX_OLD_SIGFRAME;
115 sc_addr = sp_addr;
118 else if (ret == 2)
120 /* Handle RT signal frames. Check if the first word on the stack is a
121 pointer to the siginfo structure. */
122 if (sp == sp_addr + 8)
124 c->sigcontext_format = ARM_SCF_LINUX_OLD_RT_SIGFRAME;
125 sc_addr = sp_addr + 8 + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
127 else
129 c->sigcontext_format = ARM_SCF_LINUX_RT_SIGFRAME;
130 sc_addr = sp_addr + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
133 else
134 return -UNW_EUNSPEC;
136 c->sigcontext_addr = sc_addr;
137 c->frame_info.frame_type = UNW_ARM_FRAME_SIGRETURN;
138 c->frame_info.cfa_reg_offset = sc_addr - sp_addr;
140 /* Update the dwarf cursor.
141 Set the location of the registers to the corresponding addresses of the
142 uc_mcontext / sigcontext structure contents. */
143 c->dwarf.loc[UNW_ARM_R0] = DWARF_LOC (sc_addr + LINUX_SC_R0_OFF, 0);
144 c->dwarf.loc[UNW_ARM_R1] = DWARF_LOC (sc_addr + LINUX_SC_R1_OFF, 0);
145 c->dwarf.loc[UNW_ARM_R2] = DWARF_LOC (sc_addr + LINUX_SC_R2_OFF, 0);
146 c->dwarf.loc[UNW_ARM_R3] = DWARF_LOC (sc_addr + LINUX_SC_R3_OFF, 0);
147 c->dwarf.loc[UNW_ARM_R4] = DWARF_LOC (sc_addr + LINUX_SC_R4_OFF, 0);
148 c->dwarf.loc[UNW_ARM_R5] = DWARF_LOC (sc_addr + LINUX_SC_R5_OFF, 0);
149 c->dwarf.loc[UNW_ARM_R6] = DWARF_LOC (sc_addr + LINUX_SC_R6_OFF, 0);
150 c->dwarf.loc[UNW_ARM_R7] = DWARF_LOC (sc_addr + LINUX_SC_R7_OFF, 0);
151 c->dwarf.loc[UNW_ARM_R8] = DWARF_LOC (sc_addr + LINUX_SC_R8_OFF, 0);
152 c->dwarf.loc[UNW_ARM_R9] = DWARF_LOC (sc_addr + LINUX_SC_R9_OFF, 0);
153 c->dwarf.loc[UNW_ARM_R10] = DWARF_LOC (sc_addr + LINUX_SC_R10_OFF, 0);
154 c->dwarf.loc[UNW_ARM_R11] = DWARF_LOC (sc_addr + LINUX_SC_FP_OFF, 0);
155 c->dwarf.loc[UNW_ARM_R12] = DWARF_LOC (sc_addr + LINUX_SC_IP_OFF, 0);
156 c->dwarf.loc[UNW_ARM_R13] = DWARF_LOC (sc_addr + LINUX_SC_SP_OFF, 0);
157 c->dwarf.loc[UNW_ARM_R14] = DWARF_LOC (sc_addr + LINUX_SC_LR_OFF, 0);
158 c->dwarf.loc[UNW_ARM_R15] = DWARF_LOC (sc_addr + LINUX_SC_PC_OFF, 0);
160 /* Set SP/CFA and PC/IP. */
161 dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R13], &c->dwarf.cfa);
162 dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R15], &c->dwarf.ip);
164 c->dwarf.pi_valid = 0;
166 return 1;
169 PROTECTED int
170 unw_step (unw_cursor_t *cursor)
172 struct cursor *c = (struct cursor *) cursor;
173 int ret = -UNW_EUNSPEC;
175 Debug (1, "(cursor=%p)\n", c);
177 /* Check if this is a signal frame. */
178 if (unw_is_signal_frame (cursor))
179 return unw_handle_signal_frame (cursor);
181 #ifdef CONFIG_DEBUG_FRAME
182 /* First, try DWARF-based unwinding. */
183 if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
185 ret = dwarf_step (&c->dwarf);
186 Debug(1, "dwarf_step()=%d\n", ret);
188 if (likely (ret > 0))
189 return 1;
190 else if (unlikely (ret == -UNW_ESTOPUNWIND))
191 return ret;
193 if (ret < 0 && ret != -UNW_ENOINFO)
195 Debug (2, "returning %d\n", ret);
196 return ret;
199 #endif /* CONFIG_DEBUG_FRAME */
201 /* Next, try extbl-based unwinding. */
202 if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
204 ret = arm_exidx_step (c);
205 if (ret > 0)
206 return 1;
207 if (ret == -UNW_ESTOPUNWIND || ret == 0)
208 return ret;
211 /* Fall back on APCS frame parsing.
212 Note: This won't work in case the ARM EABI is used. */
213 if (unlikely (ret < 0))
215 if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
217 ret = UNW_ESUCCESS;
218 /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
219 unw_word_t instr, i;
220 Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
221 dwarf_loc_t ip_loc, fp_loc;
222 unw_word_t frame;
223 /* Mark all registers unsaved, since we don't know where
224 they are saved (if at all), except for the EBP and
225 EIP. */
226 if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
228 return 0;
230 for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
231 c->dwarf.loc[i] = DWARF_NULL_LOC;
233 if (frame)
235 if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
237 return 0;
239 instr -= 8;
240 if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
242 return 0;
244 if ((instr & 0xFFFFD800) == 0xE92DD800)
246 /* Standard APCS frame. */
247 ip_loc = DWARF_LOC(frame - 4, 0);
248 fp_loc = DWARF_LOC(frame - 12, 0);
250 else
252 /* Codesourcery optimized normal frame. */
253 ip_loc = DWARF_LOC(frame, 0);
254 fp_loc = DWARF_LOC(frame - 4, 0);
256 if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
258 return 0;
260 c->dwarf.loc[UNW_ARM_R12] = ip_loc;
261 c->dwarf.loc[UNW_ARM_R11] = fp_loc;
262 c->dwarf.pi_valid = 0;
263 Debug(15, "ip=%lx\n", c->dwarf.ip);
265 else
267 ret = -UNW_ENOINFO;
271 return ret == -UNW_ENOINFO ? 0 : 1;