Gstep.c revision d4fbc8326a91d246af29ff17131a12ed7ae87140
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
7This file is part of libunwind.
8
9Permission is hereby granted, free of charge, to any person obtaining
10a copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be
18included in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26WITH 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> */
35static int
36is_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
55PROTECTED int
56unw_step (unw_cursor_t *cursor)
57{
58  struct cursor *c = (struct cursor *) cursor;
59  int ret, i;
60
61  Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx)\n",
62	 c, c->dwarf.ip, c->dwarf.cfa);
63
64  /* Try DWARF-based unwinding... */
65  c->sigcontext_format = X86_64_SCF_NONE;
66  ret = dwarf_step (&c->dwarf);
67
68  if (ret < 0 && ret != -UNW_ENOINFO)
69    {
70      Debug (2, "returning %d\n", ret);
71      return ret;
72    }
73
74  if (likely (ret >= 0))
75    {
76      /* x86_64 ABI specifies that end of call-chain is marked with a
77	 NULL RBP.  */
78      if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
79	c->dwarf.ip = 0;
80    }
81  else
82    {
83      /* DWARF failed.  There isn't much of a usable frame-chain on x86-64,
84	 but we do need to handle two special-cases:
85
86	  (i) signal trampoline: Old kernels and older libcs don't
87	      export the vDSO needed to get proper unwind info for the
88	      trampoline.  Recognize that case by looking at the code
89	      and filling in things by hand.
90
91	  (ii) PLT (shared-library) call-stubs: PLT stubs are invoked
92	      via CALLQ.  Try this for all non-signal trampoline
93	      code.  */
94
95      unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
96      struct dwarf_loc rbp_loc, rsp_loc, rip_loc;
97
98      /* We could get here because of missing/bad unwind information.
99         Validate all addresses before dereferencing. */
100      c->validate = 1;
101
102      Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
103
104      if (unw_is_signal_frame (cursor))
105	{
106          ret = unw_handle_signal_frame(cursor);
107	  if (ret < 0)
108	    {
109	      Debug (2, "returning 0\n");
110	      return 0;
111	    }
112	}
113      else if (is_plt_entry (&c->dwarf))
114	{
115	  Debug (2, "found plt entry\n");
116          c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0);
117          c->dwarf.cfa += 8;
118	}
119      else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
120        {
121	  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
122	    c->dwarf.loc[i] = DWARF_NULL_LOC;
123	}
124      else
125	{
126	  unw_word_t rbp;
127
128	  ret = dwarf_get (&c->dwarf, c->dwarf.loc[RBP], &rbp);
129	  if (ret < 0)
130	    {
131	      Debug (2, "returning %d [RBP=0x%lx]\n", ret,
132		     DWARF_GET_LOC (c->dwarf.loc[RBP]));
133	      return ret;
134	    }
135
136	  if (!rbp)
137	    {
138	      /* Looks like we may have reached the end of the call-chain.  */
139	      rbp_loc = DWARF_NULL_LOC;
140	      rsp_loc = DWARF_NULL_LOC;
141	      rip_loc = DWARF_NULL_LOC;
142	    }
143	  else
144	    {
145	      unw_word_t rbp1;
146	      Debug (1, "[RBP=0x%Lx] = 0x%Lx (cfa = 0x%Lx)\n",
147		     (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
148		     (unsigned long long) rbp,
149		     (unsigned long long) c->dwarf.cfa);
150
151	      rbp_loc = DWARF_LOC(rbp, 0);
152	      rsp_loc = DWARF_NULL_LOC;
153	      rip_loc = DWARF_LOC (rbp + 8, 0);
154              /* Heuristic to recognize a bogus frame pointer */
155	      ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1);
156              if (ret || ((rbp1 - rbp) > 0x4000))
157                rbp_loc = DWARF_NULL_LOC;
158	      c->dwarf.cfa += 16;
159	    }
160
161	  /* Mark all registers unsaved */
162	  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
163	    c->dwarf.loc[i] = DWARF_NULL_LOC;
164
165          c->dwarf.loc[RBP] = rbp_loc;
166          c->dwarf.loc[RSP] = rsp_loc;
167          c->dwarf.loc[RIP] = rip_loc;
168	}
169
170      c->dwarf.ret_addr_column = RIP;
171
172      if (!DWARF_IS_NULL_LOC (c->dwarf.loc[RIP]))
173	{
174	  ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip);
175	  Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n",
176		     (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]),
177		     (unsigned long long) c->dwarf.ip);
178	  if (ret < 0)
179	    {
180	      Debug (2, "returning %d\n", ret);
181	      return ret;
182	    }
183	}
184      else
185	c->dwarf.ip = 0;
186
187      if (c->dwarf.ip == prev_ip && c->dwarf.cfa == prev_cfa)
188	return -UNW_EBADFRAME;
189    }
190  ret = (c->dwarf.ip == 0) ? 0 : 1;
191  Debug (2, "returning %d\n", ret);
192  return ret;
193}
194