Home | History | Annotate | Download | only in x86
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2010 Konstantin Belousov <kib (at) freebsd.org>
      3 
      4 This file is part of libunwind.
      5 
      6 Permission is hereby granted, free of charge, to any person obtaining
      7 a copy of this software and associated documentation files (the
      8 "Software"), to deal in the Software without restriction, including
      9 without limitation the rights to use, copy, modify, merge, publish,
     10 distribute, sublicense, and/or sell copies of the Software, and to
     11 permit persons to whom the Software is furnished to do so, subject to
     12 the following conditions:
     13 
     14 The above copyright notice and this permission notice shall be
     15 included in all copies or substantial portions of the Software.
     16 
     17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     21 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     24 
     25 #ifdef HAVE_CONFIG_H
     26 #include "config.h"
     27 #endif
     28 
     29 #include <sys/types.h>
     30 #include <signal.h>
     31 #include <stddef.h>
     32 #include <ucontext.h>
     33 #include <machine/sigframe.h>
     34 
     35 #include "unwind_i.h"
     36 #include "offsets.h"
     37 
     38 PROTECTED int
     39 unw_is_signal_frame (unw_cursor_t *cursor)
     40 {
     41   struct cursor *c = (struct cursor *) cursor;
     42   unw_word_t w0, w1, w2, w3, w4, w5, ip;
     43   unw_addr_space_t as;
     44   unw_accessors_t *a;
     45   void *arg;
     46   int ret;
     47 
     48   as = c->dwarf.as;
     49   a = unw_get_accessors (as);
     50   arg = c->dwarf.as_arg;
     51 
     52   /* Check if EIP points at sigreturn() sequence.  It can be:
     53 sigcode+4: from amd64 freebsd32 environment
     54 8d 44 24 20		lea    0x20(%esp),%eax
     55 50			push   %eax
     56 b8 a1 01 00 00		mov    $0x1a1,%eax
     57 50			push   %eax
     58 cd 80			int    $0x80
     59 
     60 sigcode+4: from real i386
     61 8d 44 24 20		lea    0x20(%esp),%eax
     62 50			push   %eax
     63 f7 40 54 00 02 00	testl  $0x20000,0x54(%eax)
     64 75 03			jne    sigcode+21
     65 8e 68 14		mov    0x14(%eax),%gs
     66 b8 a1 01 00 00		mov    $0x1a1,%eax
     67 50			push   %eax
     68 cd 80			int    $0x80
     69 
     70 freebsd4_sigcode+4:
     71 XXX
     72 osigcode:
     73 XXX
     74   */
     75   ip = c->dwarf.ip;
     76   ret = X86_SCF_NONE;
     77   c->sigcontext_format = ret;
     78   if ((*a->access_mem) (as, ip, &w0, 0, arg) < 0 ||
     79       (*a->access_mem) (as, ip + 4, &w1, 0, arg) < 0 ||
     80       (*a->access_mem) (as, ip + 8, &w2, 0, arg) < 0 ||
     81       (*a->access_mem) (as, ip + 12, &w3, 0, arg) < 0)
     82     return ret;
     83   if (w0 == 0x2024448d && w1 == 0x01a1b850 && w2 == 0xcd500000 &&
     84       (w3 & 0xff) == 0x80)
     85     ret = X86_SCF_FREEBSD_SIGFRAME;
     86   else {
     87     if ((*a->access_mem) (as, ip + 16, &w4, 0, arg) < 0 ||
     88 	(*a->access_mem) (as, ip + 20, &w5, 0, arg) < 0)
     89       return ret;
     90     if (w0 == 0x2024448d && w1 == 0x5440f750 && w2 == 0x75000200 &&
     91 	w3 == 0x14688e03 && w4 == 0x0001a1b8 && w5 == 0x80cd5000)
     92       ret = X86_SCF_FREEBSD_SIGFRAME;
     93   }
     94   Debug (16, "returning %d\n", ret);
     95   c->sigcontext_format = ret;
     96   return (ret);
     97 }
     98 
     99 PROTECTED int
    100 unw_handle_signal_frame (unw_cursor_t *cursor)
    101 {
    102   struct cursor *c = (struct cursor *) cursor;
    103   int ret;
    104 
    105   if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) {
    106     struct sigframe *sf;
    107     uintptr_t uc_addr;
    108     struct dwarf_loc esp_loc;
    109 
    110     sf = (struct sigframe *)c->dwarf.cfa;
    111     uc_addr = (uintptr_t)&(sf->sf_uc);
    112     c->sigcontext_addr = c->dwarf.cfa;
    113 
    114     esp_loc = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0);
    115     ret = dwarf_get (&c->dwarf, esp_loc, &c->dwarf.cfa);
    116     if (ret < 0)
    117     {
    118 	    Debug (2, "returning 0\n");
    119 	    return 0;
    120     }
    121 
    122     c->dwarf.loc[EIP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EIP_OFF, 0);
    123     c->dwarf.loc[ESP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0);
    124     c->dwarf.loc[EAX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EAX_OFF, 0);
    125     c->dwarf.loc[ECX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ECX_OFF, 0);
    126     c->dwarf.loc[EDX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDX_OFF, 0);
    127     c->dwarf.loc[EBX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBX_OFF, 0);
    128     c->dwarf.loc[EBP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBP_OFF, 0);
    129     c->dwarf.loc[ESI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESI_OFF, 0);
    130     c->dwarf.loc[EDI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDI_OFF, 0);
    131     c->dwarf.loc[EFLAGS] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EFLAGS_OFF, 0);
    132     c->dwarf.loc[TRAPNO] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_TRAPNO_OFF, 0);
    133     c->dwarf.loc[ST0] = DWARF_NULL_LOC;
    134   } else {
    135     Debug (8, "Gstep: not handling frame format %d\n", c->sigcontext_format);
    136     abort();
    137   }
    138   return 0;
    139 }
    140 
    141 HIDDEN dwarf_loc_t
    142 x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg)
    143 {
    144   unw_word_t addr = c->sigcontext_addr, off, xmm_off;
    145   unw_word_t fpstate, fpformat;
    146   int ret, is_fpstate = 0, is_xmmstate = 0;
    147 
    148   switch (c->sigcontext_format)
    149     {
    150     case X86_SCF_NONE:
    151       return DWARF_REG_LOC (&c->dwarf, reg);
    152 
    153     case X86_SCF_FREEBSD_SIGFRAME:
    154       addr += offsetof(struct sigframe, sf_uc) + FREEBSD_UC_MCONTEXT_OFF;
    155       break;
    156 
    157     case X86_SCF_FREEBSD_SIGFRAME4:
    158       abort();
    159       break;
    160 
    161     case X86_SCF_FREEBSD_OSIGFRAME:
    162       /* XXXKIB */
    163       abort();
    164       break;
    165 
    166     case X86_SCF_FREEBSD_SYSCALL:
    167       /* XXXKIB */
    168       abort();
    169       break;
    170 
    171     default:
    172       /* XXXKIB */
    173       abort();
    174       break;
    175     }
    176 
    177   off = 0; /* shut gcc warning */
    178   switch (reg)
    179     {
    180     case UNW_X86_GS: off = FREEBSD_UC_MCONTEXT_GS_OFF; break;
    181     case UNW_X86_FS: off = FREEBSD_UC_MCONTEXT_FS_OFF; break;
    182     case UNW_X86_ES: off = FREEBSD_UC_MCONTEXT_ES_OFF; break;
    183     case UNW_X86_DS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break;
    184     case UNW_X86_EDI: off = FREEBSD_UC_MCONTEXT_EDI_OFF; break;
    185     case UNW_X86_ESI: off = FREEBSD_UC_MCONTEXT_ESI_OFF; break;
    186     case UNW_X86_EBP: off = FREEBSD_UC_MCONTEXT_EBP_OFF; break;
    187     case UNW_X86_ESP: off = FREEBSD_UC_MCONTEXT_ESP_OFF; break;
    188     case UNW_X86_EBX: off = FREEBSD_UC_MCONTEXT_EBX_OFF; break;
    189     case UNW_X86_EDX: off = FREEBSD_UC_MCONTEXT_EDX_OFF; break;
    190     case UNW_X86_ECX: off = FREEBSD_UC_MCONTEXT_ECX_OFF; break;
    191     case UNW_X86_EAX: off = FREEBSD_UC_MCONTEXT_EAX_OFF; break;
    192     case UNW_X86_TRAPNO: off = FREEBSD_UC_MCONTEXT_TRAPNO_OFF; break;
    193     case UNW_X86_EIP: off = FREEBSD_UC_MCONTEXT_EIP_OFF; break;
    194     case UNW_X86_CS: off = FREEBSD_UC_MCONTEXT_CS_OFF; break;
    195     case UNW_X86_EFLAGS: off = FREEBSD_UC_MCONTEXT_EFLAGS_OFF; break;
    196     case UNW_X86_SS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break;
    197 
    198     case UNW_X86_FCW:
    199       is_fpstate = 1;
    200       off = FREEBSD_UC_MCONTEXT_CW_OFF;
    201       xmm_off = FREEBSD_UC_MCONTEXT_CW_XMM_OFF;
    202       break;
    203     case UNW_X86_FSW:
    204       is_fpstate = 1;
    205       off = FREEBSD_UC_MCONTEXT_SW_OFF;
    206       xmm_off = FREEBSD_UC_MCONTEXT_SW_XMM_OFF;
    207       break;
    208     case UNW_X86_FTW:
    209       is_fpstate = 1;
    210       xmm_off = FREEBSD_UC_MCONTEXT_TAG_XMM_OFF;
    211       off = FREEBSD_UC_MCONTEXT_TAG_OFF;
    212       break;
    213     case UNW_X86_FCS:
    214       is_fpstate = 1;
    215       off = FREEBSD_UC_MCONTEXT_CSSEL_OFF;
    216       xmm_off = FREEBSD_UC_MCONTEXT_CSSEL_XMM_OFF;
    217       break;
    218     case UNW_X86_FIP:
    219       is_fpstate = 1;
    220       off = FREEBSD_UC_MCONTEXT_IPOFF_OFF;
    221       xmm_off = FREEBSD_UC_MCONTEXT_IPOFF_XMM_OFF;
    222       break;
    223     case UNW_X86_FEA:
    224       is_fpstate = 1;
    225       off = FREEBSD_UC_MCONTEXT_DATAOFF_OFF;
    226       xmm_off = FREEBSD_UC_MCONTEXT_DATAOFF_XMM_OFF;
    227       break;
    228     case UNW_X86_FDS:
    229       is_fpstate = 1;
    230       off = FREEBSD_US_MCONTEXT_DATASEL_OFF;
    231       xmm_off = FREEBSD_US_MCONTEXT_DATASEL_XMM_OFF;
    232       break;
    233     case UNW_X86_MXCSR:
    234       is_fpstate = 1;
    235       is_xmmstate = 1;
    236       xmm_off = FREEBSD_UC_MCONTEXT_MXCSR_XMM_OFF;
    237       break;
    238 
    239       /* stacked fp registers */
    240     case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3:
    241     case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7:
    242       is_fpstate = 1;
    243       off = FREEBSD_UC_MCONTEXT_ST0_OFF + 10*(reg - UNW_X86_ST0);
    244       xmm_off = FREEBSD_UC_MCONTEXT_ST0_XMM_OFF + 10*(reg - UNW_X86_ST0);
    245       break;
    246 
    247      /* SSE fp registers */
    248     case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi:
    249     case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi:
    250     case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi:
    251     case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi:
    252     case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi:
    253     case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi:
    254     case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi:
    255     case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi:
    256       is_fpstate = 1;
    257       is_xmmstate = 1;
    258       xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo);
    259       break;
    260     case UNW_X86_XMM0:
    261     case UNW_X86_XMM1:
    262     case UNW_X86_XMM2:
    263     case UNW_X86_XMM3:
    264     case UNW_X86_XMM4:
    265     case UNW_X86_XMM5:
    266     case UNW_X86_XMM6:
    267     case UNW_X86_XMM7:
    268       is_fpstate = 1;
    269       is_xmmstate = 1;
    270       xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 16*(reg - UNW_X86_XMM0);
    271       break;
    272 
    273     case UNW_X86_FOP:
    274     case UNW_X86_TSS:
    275     case UNW_X86_LDT:
    276     default:
    277       return DWARF_REG_LOC (&c->dwarf, reg);
    278     }
    279 
    280   if (is_fpstate)
    281     {
    282       if ((ret = dwarf_get (&c->dwarf,
    283 	   DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPSTATE_OFF),
    284 	   &fpstate)) < 0)
    285 	return DWARF_NULL_LOC;
    286       if (fpstate == FREEBSD_UC_MCONTEXT_FPOWNED_NONE)
    287 	return DWARF_NULL_LOC;
    288       if ((ret = dwarf_get (&c->dwarf,
    289 	   DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPFORMAT_OFF),
    290 	   &fpformat)) < 0)
    291 	return DWARF_NULL_LOC;
    292       if (fpformat == FREEBSD_UC_MCONTEXT_FPFMT_NODEV ||
    293 	  (is_xmmstate && fpformat != FREEBSD_UC_MCONTEXT_FPFMT_XMM))
    294 	return DWARF_NULL_LOC;
    295       if (is_xmmstate)
    296 	off = xmm_off;
    297     }
    298 
    299     return DWARF_MEM_LOC (c, addr + off);
    300 }
    301 
    302 #ifndef UNW_REMOTE_ONLY
    303 HIDDEN void *
    304 x86_r_uc_addr (ucontext_t *uc, int reg)
    305 {
    306   void *addr;
    307 
    308   switch (reg)
    309     {
    310     case UNW_X86_GS:  addr = &uc->uc_mcontext.mc_gs; break;
    311     case UNW_X86_FS:  addr = &uc->uc_mcontext.mc_fs; break;
    312     case UNW_X86_ES:  addr = &uc->uc_mcontext.mc_es; break;
    313     case UNW_X86_DS:  addr = &uc->uc_mcontext.mc_ds; break;
    314     case UNW_X86_EAX: addr = &uc->uc_mcontext.mc_eax; break;
    315     case UNW_X86_EBX: addr = &uc->uc_mcontext.mc_ebx; break;
    316     case UNW_X86_ECX: addr = &uc->uc_mcontext.mc_ecx; break;
    317     case UNW_X86_EDX: addr = &uc->uc_mcontext.mc_edx; break;
    318     case UNW_X86_ESI: addr = &uc->uc_mcontext.mc_esi; break;
    319     case UNW_X86_EDI: addr = &uc->uc_mcontext.mc_edi; break;
    320     case UNW_X86_EBP: addr = &uc->uc_mcontext.mc_ebp; break;
    321     case UNW_X86_EIP: addr = &uc->uc_mcontext.mc_eip; break;
    322     case UNW_X86_ESP: addr = &uc->uc_mcontext.mc_esp; break;
    323     case UNW_X86_TRAPNO:  addr = &uc->uc_mcontext.mc_trapno; break;
    324     case UNW_X86_CS:  addr = &uc->uc_mcontext.mc_cs; break;
    325     case UNW_X86_EFLAGS:  addr = &uc->uc_mcontext.mc_eflags; break;
    326     case UNW_X86_SS:  addr = &uc->uc_mcontext.mc_ss; break;
    327 
    328     default:
    329       addr = NULL;
    330     }
    331   return addr;
    332 }
    333 
    334 HIDDEN int
    335 x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
    336 {
    337   struct cursor *c = (struct cursor *) cursor;
    338   ucontext_t *uc = c->uc;
    339 
    340   /* Ensure c->pi is up-to-date.  On x86, it's relatively common to be
    341      missing DWARF unwind info.  We don't want to fail in that case,
    342      because the frame-chain still would let us do a backtrace at
    343      least.  */
    344   dwarf_make_proc_info (&c->dwarf);
    345 
    346   if (c->sigcontext_format == X86_SCF_NONE) {
    347       Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip);
    348       setcontext (uc);
    349       abort();
    350   } else if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) {
    351       struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
    352 
    353       Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc);
    354       sigreturn((ucontext_t *)((const char *)sc + FREEBSD_SC_UCONTEXT_OFF));
    355       abort();
    356   } else {
    357       Debug (8, "resuming at ip=%x for sigcontext format %d not implemented\n",
    358       c->dwarf.ip, c->sigcontext_format);
    359       abort();
    360   }
    361   return -UNW_EINVAL;
    362 }
    363 
    364 #endif
    365