• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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