Initial import
[external/libunwind.git] / src / arm / Gstep.c
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
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
48   if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0)
49      return ret;
50
51   if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
52     return -UNW_ENOINFO;
53
54   ret = arm_exidx_extract (&c->dwarf, buf);
55   if (ret == -UNW_ESTOPUNWIND)
56     return 0;
57   else if (ret < 0)
58     return ret;
59
60   ret = arm_exidx_decode (buf, ret, &c->dwarf);
61   if (ret < 0)
62     return ret;
63
64   if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
65     {
66       Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
67                __FUNCTION__, (long) c->dwarf.ip);
68       return -UNW_EBADFRAME;
69     }
70
71   c->dwarf.pi_valid = 0;
72
73   return (c->dwarf.ip == 0) ? 0 : 1;
74 }
75
76 PROTECTED int
77 unw_handle_signal_frame (unw_cursor_t *cursor)
78 {
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);
83
84   if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0)
85     return -UNW_EUNSPEC;
86
87   /* Obtain signal frame type (non-RT or RT). */
88   ret = unw_is_signal_frame (cursor);
89
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;
94
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.  */
102
103   if (ret == 1)
104     {
105       /* Handle non-RT signal frames. Check if the first word on the stack
106          is the magic number.  */
107       if (sp == 0x5ac3c35a)
108         {
109           c->sigcontext_format = ARM_SCF_LINUX_SIGFRAME;
110           sc_addr = sp_addr + LINUX_UC_MCONTEXT_OFF;
111         }
112       else
113         {
114           c->sigcontext_format = ARM_SCF_LINUX_OLD_SIGFRAME;
115           sc_addr = sp_addr;
116         }
117     }
118   else if (ret == 2)
119     {
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)
123         {
124           c->sigcontext_format = ARM_SCF_LINUX_OLD_RT_SIGFRAME;
125           sc_addr = sp_addr + 8 + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
126         }
127       else
128         {
129           c->sigcontext_format = ARM_SCF_LINUX_RT_SIGFRAME;
130           sc_addr = sp_addr + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
131         }
132     }
133   else
134     return -UNW_EUNSPEC;
135
136   c->sigcontext_addr = sc_addr;
137
138   /* Update the dwarf cursor.
139      Set the location of the registers to the corresponding addresses of the
140      uc_mcontext / sigcontext structure contents.  */
141   c->dwarf.loc[UNW_ARM_R0] = DWARF_LOC (sc_addr + LINUX_SC_R0_OFF, 0);
142   c->dwarf.loc[UNW_ARM_R1] = DWARF_LOC (sc_addr + LINUX_SC_R1_OFF, 0);
143   c->dwarf.loc[UNW_ARM_R2] = DWARF_LOC (sc_addr + LINUX_SC_R2_OFF, 0);
144   c->dwarf.loc[UNW_ARM_R3] = DWARF_LOC (sc_addr + LINUX_SC_R3_OFF, 0);
145   c->dwarf.loc[UNW_ARM_R4] = DWARF_LOC (sc_addr + LINUX_SC_R4_OFF, 0);
146   c->dwarf.loc[UNW_ARM_R5] = DWARF_LOC (sc_addr + LINUX_SC_R5_OFF, 0);
147   c->dwarf.loc[UNW_ARM_R6] = DWARF_LOC (sc_addr + LINUX_SC_R6_OFF, 0);
148   c->dwarf.loc[UNW_ARM_R7] = DWARF_LOC (sc_addr + LINUX_SC_R7_OFF, 0);
149   c->dwarf.loc[UNW_ARM_R8] = DWARF_LOC (sc_addr + LINUX_SC_R8_OFF, 0);
150   c->dwarf.loc[UNW_ARM_R9] = DWARF_LOC (sc_addr + LINUX_SC_R9_OFF, 0);
151   c->dwarf.loc[UNW_ARM_R10] = DWARF_LOC (sc_addr + LINUX_SC_R10_OFF, 0);
152   c->dwarf.loc[UNW_ARM_R11] = DWARF_LOC (sc_addr + LINUX_SC_FP_OFF, 0);
153   c->dwarf.loc[UNW_ARM_R12] = DWARF_LOC (sc_addr + LINUX_SC_IP_OFF, 0);
154   c->dwarf.loc[UNW_ARM_R13] = DWARF_LOC (sc_addr + LINUX_SC_SP_OFF, 0);
155   c->dwarf.loc[UNW_ARM_R14] = DWARF_LOC (sc_addr + LINUX_SC_LR_OFF, 0);
156   c->dwarf.loc[UNW_ARM_R15] = DWARF_LOC (sc_addr + LINUX_SC_PC_OFF, 0);
157
158   /* Set SP/CFA and PC/IP.  */
159   dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R13], &c->dwarf.cfa);
160   dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R15], &c->dwarf.ip);
161
162   c->dwarf.pi_valid = 0;
163
164   return 1;
165 }
166
167 PROTECTED int
168 unw_step (unw_cursor_t *cursor)
169 {
170   struct cursor *c = (struct cursor *) cursor;
171   int ret = -UNW_EUNSPEC;
172
173   Debug (1, "(cursor=%p)\n", c);
174
175   /* Check if this is a signal frame. */
176   if (unw_is_signal_frame (cursor))
177      return unw_handle_signal_frame (cursor);
178
179 #ifdef CONFIG_DEBUG_FRAME
180   /* First, try DWARF-based unwinding. */
181   if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
182     {
183       ret = dwarf_step (&c->dwarf);
184       Debug(1, "dwarf_step()=%d\n", ret);
185
186       if (likely (ret > 0))
187         return 1;
188       else if (unlikely (ret == -UNW_ESTOPUNWIND))
189         return ret;
190
191     if (ret < 0 && ret != -UNW_ENOINFO)
192       {
193         Debug (2, "returning %d\n", ret);
194         return ret;
195       }
196     }
197 #endif /* CONFIG_DEBUG_FRAME */
198
199   /* Next, try extbl-based unwinding. */
200   if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
201     {
202       ret = arm_exidx_step (c);
203       if (ret > 0)
204         return 1;
205       if (ret == -UNW_ESTOPUNWIND || ret == 0)
206         return ret;
207     }
208
209   /* Fall back on APCS frame parsing.
210      Note: This won't work in case the ARM EABI is used. */
211   if (unlikely (ret < 0))
212     {
213       if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
214         {
215           ret = UNW_ESUCCESS;
216           /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
217           unw_word_t instr, i;
218           Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
219           dwarf_loc_t ip_loc, fp_loc;
220           unw_word_t frame;
221           /* Mark all registers unsaved, since we don't know where
222              they are saved (if at all), except for the EBP and
223              EIP.  */
224           if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
225             {
226               return 0;
227             }
228           for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
229             c->dwarf.loc[i] = DWARF_NULL_LOC;
230           }
231           if (frame)
232             {
233               if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
234                 {
235                   return 0;
236                 }
237               instr -= 8;
238               if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
239                 {
240                   return 0;
241                 }
242               if ((instr & 0xFFFFD800) == 0xE92DD800)
243                 {
244                   /* Standard APCS frame. */
245                   ip_loc = DWARF_LOC(frame - 4, 0);
246                   fp_loc = DWARF_LOC(frame - 12, 0);
247                 }
248               else
249                 {
250                   /* Codesourcery optimized normal frame. */
251                   ip_loc = DWARF_LOC(frame, 0);
252                   fp_loc = DWARF_LOC(frame - 4, 0);
253                 }
254               if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
255                 {
256                   return 0;
257                 }
258               c->dwarf.loc[UNW_ARM_R12] = ip_loc;
259               c->dwarf.loc[UNW_ARM_R11] = fp_loc;
260               c->dwarf.pi_valid = 0;
261               Debug(15, "ip=%lx\n", c->dwarf.ip);
262             }
263           else
264             {
265               ret = -UNW_ENOINFO;
266             }
267         }
268     }
269   return ret == -UNW_ENOINFO ? 0 : 1;
270 }