• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2002-2004 Hewlett-Packard Co
3 	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4 
5    Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
6 
7 This file is part of libunwind.
8 
9 Permission is hereby granted, free of charge, to any person obtaining
10 a copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16 
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
27 
28 #include "unwind_i.h"
29 #include <signal.h>
30 
31 /* Recognise PLT entries such as:
32      3bdf0: ff 25 e2 49 13 00 jmpq   *0x1349e2(%rip)
33      3bdf6: 68 ae 03 00 00    pushq  $0x3ae
34      3bdfb: e9 00 c5 ff ff    jmpq   38300 <_init+0x18> */
35 static int
is_plt_entry(struct dwarf_cursor * c)36 is_plt_entry (struct dwarf_cursor *c)
37 {
38   unw_word_t w0, w1;
39   unw_accessors_t *a;
40   int ret;
41 
42   a = unw_get_accessors (c->as);
43   if ((ret = (*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg)) < 0
44       || (ret = (*a->access_mem) (c->as, c->ip + 8, &w1, 0, c->as_arg)) < 0)
45     return 0;
46 
47   ret = (((w0 & 0xffff) == 0x25ff)
48 	 && (((w0 >> 48) & 0xff) == 0x68)
49 	 && (((w1 >> 24) & 0xff) == 0xe9));
50 
51   Debug (14, "ip=0x%lx => 0x%016lx 0x%016lx, ret = %d\n", c->ip, w0, w1, ret);
52   return ret;
53 }
54 
55 PROTECTED int
unw_step(unw_cursor_t * cursor)56 unw_step (unw_cursor_t *cursor)
57 {
58   struct cursor *c = (struct cursor *) cursor;
59   int ret, i;
60 
61 #if CONSERVATIVE_CHECKS
62   int val = c->validate;
63   c->validate = 1;
64 #endif
65 
66   Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx)\n",
67 	 c, c->dwarf.ip, c->dwarf.cfa);
68 
69   unw_word_t old_ip = c->dwarf.ip;
70   unw_word_t old_cfa = c->dwarf.cfa;
71 
72   /* Try DWARF-based unwinding... */
73   c->sigcontext_format = X86_64_SCF_NONE;
74   ret = dwarf_step (&c->dwarf);
75 
76 #if CONSERVATIVE_CHECKS
77   c->validate = val;
78 #endif
79 
80   if (ret < 0 && ret != -UNW_ENOINFO)
81     {
82       Debug (2, "returning %d\n", ret);
83       return ret;
84     }
85 
86   if (likely (ret >= 0))
87     {
88       /* x86_64 ABI specifies that end of call-chain is marked with a
89 	 NULL RBP.  */
90       if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
91 	{
92 	  c->dwarf.ip = 0;
93 	  ret = 0;
94 	}
95     }
96   else
97     {
98       /* DWARF failed.  There isn't much of a usable frame-chain on x86-64,
99 	 but we do need to handle two special-cases:
100 
101 	  (i) signal trampoline: Old kernels and older libcs don't
102 	      export the vDSO needed to get proper unwind info for the
103 	      trampoline.  Recognize that case by looking at the code
104 	      and filling in things by hand.
105 
106 	  (ii) PLT (shared-library) call-stubs: PLT stubs are invoked
107 	      via CALLQ.  Try this for all non-signal trampoline
108 	      code.  */
109 
110       unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
111       struct dwarf_loc rbp_loc, rsp_loc, rip_loc;
112 
113       /* We could get here because of missing/bad unwind information.
114          Validate all addresses before dereferencing. */
115       c->validate = 1;
116 
117       Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
118 
119       if (unw_is_signal_frame (cursor))
120 	{
121           ret = unw_handle_signal_frame(cursor);
122 	  if (ret < 0)
123 	    {
124 	      Debug (2, "returning 0\n");
125 	      return 0;
126 	    }
127 	}
128       else if (is_plt_entry (&c->dwarf))
129 	{
130           /* Like regular frame, CFA = RSP+8, RA = [CFA-8], no regs saved. */
131 	  Debug (2, "found plt entry\n");
132           c->frame_info.cfa_reg_offset = 8;
133           c->frame_info.cfa_reg_rsp = -1;
134           c->frame_info.frame_type = UNW_X86_64_FRAME_STANDARD;
135           c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0);
136           c->dwarf.cfa += 8;
137 	}
138       else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
139         {
140 	  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
141 	    c->dwarf.loc[i] = DWARF_NULL_LOC;
142 	}
143       else
144 	{
145 	  unw_word_t rbp;
146 
147 	  ret = dwarf_get (&c->dwarf, c->dwarf.loc[RBP], &rbp);
148 	  if (ret < 0)
149 	    {
150 	      Debug (2, "returning %d [RBP=0x%lx]\n", ret,
151 		     DWARF_GET_LOC (c->dwarf.loc[RBP]));
152 	      return ret;
153 	    }
154 
155 	  if (!rbp)
156 	    {
157 	      /* Looks like we may have reached the end of the call-chain.  */
158 	      rbp_loc = DWARF_NULL_LOC;
159 	      rsp_loc = DWARF_NULL_LOC;
160 	      rip_loc = DWARF_NULL_LOC;
161 	    }
162 	  else
163 	    {
164 	      unw_word_t rbp1 = 0;
165 	      rbp_loc = DWARF_LOC(rbp, 0);
166 	      rsp_loc = DWARF_NULL_LOC;
167 	      rip_loc = DWARF_LOC (rbp + 8, 0);
168 	      ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1);
169 	      Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n",
170 		     (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
171 		     rbp, c->dwarf.cfa, rbp1);
172 
173 	      /* Heuristic to determine incorrect guess.  For RBP to be a
174 	         valid frame it needs to be above current CFA, but don't
175 		 let it go more than a little.  Note that we can't deduce
176 		 anything about new RBP (rbp1) since it may not be a frame
177 		 pointer in the frame above.  Just check we get the value. */
178               if (ret < 0
179 		  || rbp <= c->dwarf.cfa
180 		  || (rbp - c->dwarf.cfa) > 0x4000)
181 	        {
182                   rip_loc = DWARF_NULL_LOC;
183                   rbp_loc = DWARF_NULL_LOC;
184 		}
185 
186               c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED;
187               c->frame_info.cfa_reg_rsp = 0;
188               c->frame_info.cfa_reg_offset = 16;
189               c->frame_info.rbp_cfa_offset = -16;
190 	      c->dwarf.cfa += 16;
191 	    }
192 
193 	  /* Mark all registers unsaved */
194 	  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
195 	    c->dwarf.loc[i] = DWARF_NULL_LOC;
196 
197           c->dwarf.loc[RBP] = rbp_loc;
198           c->dwarf.loc[RSP] = rsp_loc;
199           c->dwarf.loc[RIP] = rip_loc;
200 	}
201 
202       c->dwarf.ret_addr_column = RIP;
203 
204       if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
205         {
206 	  ret = 0;
207 	  Debug (2, "NULL %%rbp loc, returning %d\n", ret);
208 	  return ret;
209         }
210       if (!DWARF_IS_NULL_LOC (c->dwarf.loc[RIP]))
211 	{
212 	  ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip);
213 	  Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n",
214 		     (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]),
215 		     (unsigned long long) c->dwarf.ip);
216 	  if (ret < 0)
217 	    {
218 	      Debug (2, "returning %d\n", ret);
219 	      return ret;
220 	    }
221 	  ret = 1;
222 	}
223       else
224 	c->dwarf.ip = 0;
225 
226       if (c->dwarf.ip == prev_ip && c->dwarf.cfa == prev_cfa)
227 	return -UNW_EBADFRAME;
228     }
229   /* ANDROID support update. */
230   /* Adjust the pc to the instruction before. */
231   if (c->dwarf.ip)
232     c->dwarf.ip--;
233   /* If the decode yields the exact same ip/cfa as before, then indicate
234      the unwind is complete. */
235   if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
236     {
237       Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
238                __FUNCTION__, (long) c->dwarf.ip);
239       return -UNW_EBADFRAME;
240     }
241   /* End of ANDROID update. */
242   Debug (2, "returning %d\n", ret);
243   return ret;
244 }
245