Home | History | Annotate | Download | only in arm
      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 (at) 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 #define arm_exidx_step	UNW_OBJ(arm_exidx_step)
     34 
     35 static inline int
     36 arm_exidx_step (struct cursor *c)
     37 {
     38   unw_word_t old_ip, old_cfa;
     39   uint8_t buf[32];
     40   int ret;
     41 
     42   old_ip = c->dwarf.ip;
     43   old_cfa = c->dwarf.cfa;
     44 
     45   /* mark PC unsaved */
     46   c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
     47 
     48   if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0)
     49      return ret;
     50 
     51   if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
     52     return -UNW_ENOINFO;
     53 
     54   ret = arm_exidx_extract (&c->dwarf, buf);
     55   if (ret == -UNW_ESTOPUNWIND)
     56     return 0;
     57   else if (ret < 0)
     58     return ret;
     59 
     60   ret = arm_exidx_decode (buf, ret, &c->dwarf);
     61   if (ret < 0)
     62     return ret;
     63 
     64   if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
     65     {
     66       Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
     67 	       __FUNCTION__, (long) c->dwarf.ip);
     68       return -UNW_EBADFRAME;
     69     }
     70 
     71   c->dwarf.pi_valid = 0;
     72 
     73   return (c->dwarf.ip == 0) ? 0 : 1;
     74 }
     75 
     76 /* ANDROID support update. */
     77 
     78 /* When taking a step back up the stack, the pc will point to the next
     79  * instruction to execute, not the currently executing instruction. This
     80  * function adjusts the pc to the currently executing instruction.
     81  */
     82 static void adjust_ip(struct cursor *c)
     83 {
     84   unw_word_t ip, value;
     85   ip = c->dwarf.ip;
     86 
     87   if (ip)
     88     {
     89       int adjust = 4;
     90       if (ip & 1)
     91         {
     92           /* Thumb instructions, the currently executing instruction could be
     93           * 2 or 4 bytes, so adjust appropriately.
     94           */
     95           unw_addr_space_t as;
     96           unw_accessors_t *a;
     97           void *arg;
     98 
     99           as = c->dwarf.as;
    100           a = unw_get_accessors (as);
    101           arg = c->dwarf.as_arg;
    102 
    103           if (ip < 5 || (*a->access_mem) (as, ip-5, &value, 0, arg) < 0 ||
    104               (value & 0xe000f000) != 0xe000f000)
    105             adjust = 2;
    106         }
    107       c->dwarf.ip -= adjust;
    108     }
    109 }
    110 /* End of ANDROID update. */
    111 
    112 PROTECTED int
    113 unw_handle_signal_frame (unw_cursor_t *cursor)
    114 {
    115   struct cursor *c = (struct cursor *) cursor;
    116   int ret;
    117   unw_word_t sc_addr, sp, sp_addr = c->dwarf.cfa;
    118   struct dwarf_loc sp_loc = DWARF_LOC (sp_addr, 0);
    119 
    120   if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0)
    121     return -UNW_EUNSPEC;
    122 
    123   /* Obtain signal frame type (non-RT or RT). */
    124   ret = unw_is_signal_frame (cursor);
    125 
    126   /* Save the SP and PC to be able to return execution at this point
    127      later in time (unw_resume).  */
    128   c->sigcontext_sp = c->dwarf.cfa;
    129   c->sigcontext_pc = c->dwarf.ip;
    130 
    131   /* Since kernel version 2.6.18 the non-RT signal frame starts with a
    132      ucontext while the RT signal frame starts with a siginfo, followed
    133      by a sigframe whose first element is an ucontext.
    134      Prior 2.6.18 the non-RT signal frame starts with a sigcontext while
    135      the RT signal frame starts with two pointers followed by a siginfo
    136      and an ucontext. The first pointer points to the start of the siginfo
    137      structure and the second one to the ucontext structure.  */
    138 
    139   if (ret == 1)
    140     {
    141       /* Handle non-RT signal frames. Check if the first word on the stack
    142 	 is the magic number.  */
    143       if (sp == 0x5ac3c35a)
    144 	{
    145 	  c->sigcontext_format = ARM_SCF_LINUX_SIGFRAME;
    146 	  sc_addr = sp_addr + LINUX_UC_MCONTEXT_OFF;
    147 	}
    148       else
    149 	{
    150 	  c->sigcontext_format = ARM_SCF_LINUX_OLD_SIGFRAME;
    151 	  sc_addr = sp_addr;
    152 	}
    153     }
    154   else if (ret == 2)
    155     {
    156       /* Handle RT signal frames. Check if the first word on the stack is a
    157 	 pointer to the siginfo structure.  */
    158       if (sp == sp_addr + 8)
    159 	{
    160 	  c->sigcontext_format = ARM_SCF_LINUX_OLD_RT_SIGFRAME;
    161 	  sc_addr = sp_addr + 8 + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
    162 	}
    163       else
    164 	{
    165 	  c->sigcontext_format = ARM_SCF_LINUX_RT_SIGFRAME;
    166 	  sc_addr = sp_addr + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF;
    167 	}
    168     }
    169   else
    170     return -UNW_EUNSPEC;
    171 
    172   c->sigcontext_addr = sc_addr;
    173 
    174   /* Update the dwarf cursor.
    175      Set the location of the registers to the corresponding addresses of the
    176      uc_mcontext / sigcontext structure contents.  */
    177   c->dwarf.loc[UNW_ARM_R0] = DWARF_LOC (sc_addr + LINUX_SC_R0_OFF, 0);
    178   c->dwarf.loc[UNW_ARM_R1] = DWARF_LOC (sc_addr + LINUX_SC_R1_OFF, 0);
    179   c->dwarf.loc[UNW_ARM_R2] = DWARF_LOC (sc_addr + LINUX_SC_R2_OFF, 0);
    180   c->dwarf.loc[UNW_ARM_R3] = DWARF_LOC (sc_addr + LINUX_SC_R3_OFF, 0);
    181   c->dwarf.loc[UNW_ARM_R4] = DWARF_LOC (sc_addr + LINUX_SC_R4_OFF, 0);
    182   c->dwarf.loc[UNW_ARM_R5] = DWARF_LOC (sc_addr + LINUX_SC_R5_OFF, 0);
    183   c->dwarf.loc[UNW_ARM_R6] = DWARF_LOC (sc_addr + LINUX_SC_R6_OFF, 0);
    184   c->dwarf.loc[UNW_ARM_R7] = DWARF_LOC (sc_addr + LINUX_SC_R7_OFF, 0);
    185   c->dwarf.loc[UNW_ARM_R8] = DWARF_LOC (sc_addr + LINUX_SC_R8_OFF, 0);
    186   c->dwarf.loc[UNW_ARM_R9] = DWARF_LOC (sc_addr + LINUX_SC_R9_OFF, 0);
    187   c->dwarf.loc[UNW_ARM_R10] = DWARF_LOC (sc_addr + LINUX_SC_R10_OFF, 0);
    188   c->dwarf.loc[UNW_ARM_R11] = DWARF_LOC (sc_addr + LINUX_SC_FP_OFF, 0);
    189   c->dwarf.loc[UNW_ARM_R12] = DWARF_LOC (sc_addr + LINUX_SC_IP_OFF, 0);
    190   c->dwarf.loc[UNW_ARM_R13] = DWARF_LOC (sc_addr + LINUX_SC_SP_OFF, 0);
    191   c->dwarf.loc[UNW_ARM_R14] = DWARF_LOC (sc_addr + LINUX_SC_LR_OFF, 0);
    192   c->dwarf.loc[UNW_ARM_R15] = DWARF_LOC (sc_addr + LINUX_SC_PC_OFF, 0);
    193 
    194   /* Set SP/CFA and PC/IP.  */
    195   dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R13], &c->dwarf.cfa);
    196   dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R15], &c->dwarf.ip);
    197 
    198   c->dwarf.pi_valid = 0;
    199 
    200   return 1;
    201 }
    202 
    203 PROTECTED int
    204 unw_step (unw_cursor_t *cursor)
    205 {
    206   struct cursor *c = (struct cursor *) cursor;
    207   int ret = -UNW_EUNSPEC;
    208 
    209   Debug (1, "(cursor=%p)\n", c);
    210 
    211   /* Check if this is a signal frame. */
    212   if (unw_is_signal_frame (cursor))
    213     {
    214       ret = unw_handle_signal_frame (cursor);
    215     }
    216 
    217 #ifdef CONFIG_DEBUG_FRAME
    218   /* First, try DWARF-based unwinding. */
    219   if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
    220     {
    221       ret = dwarf_step (&c->dwarf);
    222       Debug(1, "dwarf_step()=%d\n", ret);
    223 
    224       if (likely (ret > 0))
    225         ret = 1;
    226       else if (unlikely (ret == -UNW_ESTOPUNWIND))
    227         ret = 0;
    228     }
    229 #endif /* CONFIG_DEBUG_FRAME */
    230 
    231   /* Next, try extbl-based unwinding. */
    232   if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
    233     {
    234       ret = arm_exidx_step (c);
    235       if (ret > 0)
    236 	ret = 1;
    237       if (ret == -UNW_ESTOPUNWIND || ret == 0)
    238 	ret = 0;
    239     }
    240 
    241   /* Fall back on APCS frame parsing.
    242      Note: This won't work in case the ARM EABI is used. */
    243   if (unlikely (ret < 0))
    244     {
    245       if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
    246         {
    247           ret = UNW_ESUCCESS;
    248           /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
    249           unw_word_t instr, i;
    250           Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
    251           dwarf_loc_t ip_loc, fp_loc;
    252           unw_word_t frame;
    253           /* Mark all registers unsaved, since we don't know where
    254              they are saved (if at all), except for the EBP and
    255              EIP.  */
    256           if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0)
    257             {
    258               return 0;
    259             }
    260           for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) {
    261             c->dwarf.loc[i] = DWARF_NULL_LOC;
    262           }
    263           if (frame)
    264             {
    265               if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0)
    266                 {
    267                   return 0;
    268                 }
    269               instr -= 8;
    270               if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0)
    271                 {
    272                   return 0;
    273                 }
    274               if ((instr & 0xFFFFD800) == 0xE92DD800)
    275                 {
    276                   /* Standard APCS frame. */
    277                   ip_loc = DWARF_LOC(frame - 4, 0);
    278                   fp_loc = DWARF_LOC(frame - 12, 0);
    279                 }
    280               else
    281                 {
    282                   /* Codesourcery optimized normal frame. */
    283                   ip_loc = DWARF_LOC(frame, 0);
    284                   fp_loc = DWARF_LOC(frame - 4, 0);
    285                 }
    286               if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0)
    287                 {
    288                   return 0;
    289                 }
    290               c->dwarf.loc[UNW_ARM_R12] = ip_loc;
    291               c->dwarf.loc[UNW_ARM_R11] = fp_loc;
    292               c->dwarf.pi_valid = 0;
    293               Debug(15, "ip=%x\n", c->dwarf.ip);
    294             }
    295           else
    296             {
    297               ret = -UNW_ENOINFO;
    298             }
    299         }
    300     }
    301 
    302   /* ANDROID support update. */
    303   if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_LR) && c->dwarf.frame == 0)
    304     {
    305       /* If this is the first frame, the code may be executing garbage
    306        * in the middle of nowhere. In this case, try using the lr as
    307        * the pc.
    308        */
    309       unw_word_t lr;
    310       if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R14], &lr) >= 0)
    311         {
    312           if (lr != c->dwarf.ip)
    313             {
    314               ret = 1;
    315               c->dwarf.ip = lr;
    316             }
    317         }
    318     }
    319   /* End of ANDROID update. */
    320 
    321   if (ret >= 0)
    322     {
    323       c->dwarf.frame++;
    324       adjust_ip(c);
    325     }
    326   return ret == -UNW_ENOINFO ? 0 : ret;
    327 }
    328