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