• 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   /* Try DWARF-based unwinding... */
70   c->sigcontext_format = X86_64_SCF_NONE;
71   ret = dwarf_step (&c->dwarf);
72 
73 #if CONSERVATIVE_CHECKS
74   c->validate = val;
75 #endif
76 
77   if (ret < 0 && ret != -UNW_ENOINFO)
78     {
79       Debug (2, "returning %d\n", ret);
80       return ret;
81     }
82 
83   if (likely (ret >= 0))
84     {
85       /* x86_64 ABI specifies that end of call-chain is marked with a
86 	 NULL RBP.  */
87       if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
88 	{
89 	  c->dwarf.ip = 0;
90 	  ret = 0;
91 	}
92     }
93   else
94     {
95       /* DWARF failed.  There isn't much of a usable frame-chain on x86-64,
96 	 but we do need to handle two special-cases:
97 
98 	  (i) signal trampoline: Old kernels and older libcs don't
99 	      export the vDSO needed to get proper unwind info for the
100 	      trampoline.  Recognize that case by looking at the code
101 	      and filling in things by hand.
102 
103 	  (ii) PLT (shared-library) call-stubs: PLT stubs are invoked
104 	      via CALLQ.  Try this for all non-signal trampoline
105 	      code.  */
106 
107       unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
108       struct dwarf_loc rbp_loc, rsp_loc, rip_loc;
109 
110       /* We could get here because of missing/bad unwind information.
111          Validate all addresses before dereferencing. */
112       c->validate = 1;
113 
114       Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
115 
116       if (unw_is_signal_frame (cursor))
117 	{
118           ret = unw_handle_signal_frame(cursor);
119 	  if (ret < 0)
120 	    {
121 	      Debug (2, "returning 0\n");
122 	      return 0;
123 	    }
124 	}
125       else if (is_plt_entry (&c->dwarf))
126 	{
127           /* Like regular frame, CFA = RSP+8, RA = [CFA-8], no regs saved. */
128 	  Debug (2, "found plt entry\n");
129           c->frame_info.cfa_reg_offset = 8;
130           c->frame_info.cfa_reg_rsp = -1;
131           c->frame_info.frame_type = UNW_X86_64_FRAME_STANDARD;
132           c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0);
133           c->dwarf.cfa += 8;
134 	}
135       else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
136         {
137 	  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
138 	    c->dwarf.loc[i] = DWARF_NULL_LOC;
139 	}
140       else
141 	{
142 	  unw_word_t rbp;
143 
144 	  ret = dwarf_get (&c->dwarf, c->dwarf.loc[RBP], &rbp);
145 	  if (ret < 0)
146 	    {
147 	      Debug (2, "returning %d [RBP=0x%lx]\n", ret,
148 		     DWARF_GET_LOC (c->dwarf.loc[RBP]));
149 	      return ret;
150 	    }
151 
152 	  if (!rbp)
153 	    {
154 	      /* Looks like we may have reached the end of the call-chain.  */
155 	      rbp_loc = DWARF_NULL_LOC;
156 	      rsp_loc = DWARF_NULL_LOC;
157 	      rip_loc = DWARF_NULL_LOC;
158 	    }
159 	  else
160 	    {
161 	      unw_word_t rbp1 = 0;
162 	      rbp_loc = DWARF_LOC(rbp, 0);
163 	      rsp_loc = DWARF_NULL_LOC;
164 	      rip_loc = DWARF_LOC (rbp + 8, 0);
165 	      ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1);
166 	      Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n",
167 		     (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
168 		     rbp, c->dwarf.cfa, rbp1);
169 
170 	      /* Heuristic to determine incorrect guess.  For RBP to be a
171 	         valid frame it needs to be above current CFA, but don't
172 		 let it go more than a little.  Note that we can't deduce
173 		 anything about new RBP (rbp1) since it may not be a frame
174 		 pointer in the frame above.  Just check we get the value. */
175               if (ret < 0
176 		  || rbp <= c->dwarf.cfa
177 		  || (rbp - c->dwarf.cfa) > 0x4000)
178 	        {
179                   rip_loc = DWARF_NULL_LOC;
180                   rbp_loc = DWARF_NULL_LOC;
181 		}
182 
183               c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED;
184               c->frame_info.cfa_reg_rsp = 0;
185               c->frame_info.cfa_reg_offset = 16;
186               c->frame_info.rbp_cfa_offset = -16;
187 	      c->dwarf.cfa += 16;
188 	    }
189 
190 	  /* Mark all registers unsaved */
191 	  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
192 	    c->dwarf.loc[i] = DWARF_NULL_LOC;
193 
194           c->dwarf.loc[RBP] = rbp_loc;
195           c->dwarf.loc[RSP] = rsp_loc;
196           c->dwarf.loc[RIP] = rip_loc;
197 	}
198 
199       c->dwarf.ret_addr_column = RIP;
200 
201       if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
202         {
203 	  ret = 0;
204 	  Debug (2, "NULL %%rbp loc, returning %d\n", ret);
205 	  return ret;
206         }
207       if (!DWARF_IS_NULL_LOC (c->dwarf.loc[RIP]))
208 	{
209 	  ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip);
210 	  Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n",
211 		     (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]),
212 		     (unsigned long long) c->dwarf.ip);
213 	  if (ret < 0)
214 	    {
215 	      Debug (2, "returning %d\n", ret);
216 	      return ret;
217 	    }
218 	  ret = 1;
219 	}
220       else
221 	c->dwarf.ip = 0;
222 
223       if (c->dwarf.ip == prev_ip && c->dwarf.cfa == prev_cfa)
224 	return -UNW_EBADFRAME;
225     }
226   /* ANDROID support update. */
227   /* Adjust the pc to the instruction before. */
228   if (c->dwarf.ip)
229     c->dwarf.ip--;
230   /* End of ANDROID update. */
231   Debug (2, "returning %d\n", ret);
232   return ret;
233 }
234