Home | History | Annotate | Download | only in x86_64
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2002-2004 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) hpl.hp.com>
      4 
      5    Modified for x86_64 by Max Asbock <masbock (at) 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
     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
     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