Home | History | Annotate | Line # | Download | only in i386
      1 /* DWARF2 EH unwinding support for FreeBSD: AMD x86-64 and x86.
      2    Copyright (C) 2015-2024 Free Software Foundation, Inc.
      3    Contributed by John Marino <gnugcc (at) marino.st>
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 3, or (at your option)
     10 any later version.
     11 
     12 GCC is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 Under Section 7 of GPL version 3, you are granted additional
     18 permissions described in the GCC Runtime Library Exception, version
     19 3.1, as published by the Free Software Foundation.
     20 
     21 You should have received a copy of the GNU General Public License and
     22 a copy of the GCC Runtime Library Exception along with this program;
     23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     24 <http://www.gnu.org/licenses/>.  */
     25 
     26 /* Do code reading to identify a signal frame, and set the frame
     27    state data appropriately.  See unwind-dw2.c for the structs. */
     28 
     29 #include <sys/types.h>
     30 #include <signal.h>
     31 #include <unistd.h>
     32 #include <sys/sysctl.h>
     33 #include <sys/ucontext.h>
     34 #include <sys/user.h>
     35 #include <machine/sigframe.h>
     36 
     37 #define REG_NAME(reg)	sf_uc.uc_mcontext.mc_## reg
     38 
     39 #ifdef __x86_64__
     40 #define MD_FALLBACK_FRAME_STATE_FOR x86_64_freebsd_fallback_frame_state
     41 
     42 #ifdef KERN_PROC_SIGTRAMP
     43 /* FreeBSD past 9.3 provides a kern.proc.sigtramp.<pid> sysctl that
     44    returns the location of the signal trampoline. Use this to find
     45    out whether we're in a trampoline.
     46 */
     47 static int
     48 x86_64_outside_sigtramp_range (unsigned char *pc)
     49 {
     50   static int sigtramp_range_determined = 0;
     51   static unsigned char *sigtramp_start, *sigtramp_end;
     52 
     53   if (sigtramp_range_determined == 0)
     54     {
     55       struct kinfo_sigtramp kst = {0};
     56       size_t len = sizeof (kst);
     57       int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_SIGTRAMP, getpid() };
     58 
     59       sigtramp_range_determined = 1;
     60       if (sysctl (mib, 4, &kst, &len, NULL, 0) == 0)
     61       {
     62         sigtramp_range_determined = 2;
     63         sigtramp_start = kst.ksigtramp_start;
     64         sigtramp_end   = kst.ksigtramp_end;
     65       }
     66     }
     67   if (sigtramp_range_determined < 2)  /* sysctl failed if < 2 */
     68     return 1;
     69 
     70   return (pc < sigtramp_start || pc >= sigtramp_end);
     71 }
     72 #endif
     73 
     74 static _Unwind_Reason_Code
     75 x86_64_freebsd_fallback_frame_state
     76 (struct _Unwind_Context *context, _Unwind_FrameState *fs)
     77 {
     78   struct sigframe *sf;
     79   long new_cfa;
     80 
     81 #ifndef KERN_PROC_SIGTRAMP
     82   /* Prior to FreeBSD 9, the signal trampoline was located immediately
     83      before the ps_strings.  To support non-executable stacks on AMD64,
     84      the sigtramp was moved to a shared page for FreeBSD 9.  Unfortunately
     85      this means looking frame patterns again (sys/amd64/amd64/sigtramp.S)
     86      rather than using the robust and convenient KERN_PS_STRINGS trick.
     87 
     88      <pc + 00>:  lea     0x10(%rsp),%rdi
     89      <pc + 05>:  pushq   $0x0
     90      <pc + 17>:  mov     $0x1a1,%rax
     91      <pc + 14>:  syscall
     92 
     93      If we can't find this pattern, we're at the end of the stack.
     94   */
     95 
     96   if (!(   *(unsigned int *)(context->ra)      == 0x247c8d48
     97         && *(unsigned int *)(context->ra +  4) == 0x48006a10
     98         && *(unsigned int *)(context->ra +  8) == 0x01a1c0c7
     99         && *(unsigned int *)(context->ra + 12) == 0x050f0000 ))
    100     return _URC_END_OF_STACK;
    101 #else
    102   if (x86_64_outside_sigtramp_range(context->ra))
    103     return _URC_END_OF_STACK;
    104 #endif
    105 
    106   sf = (struct sigframe *) context->cfa;
    107   new_cfa = sf->REG_NAME(rsp);
    108   fs->regs.cfa_how = CFA_REG_OFFSET;
    109   fs->regs.cfa_reg =  __LIBGCC_STACK_POINTER_REGNUM__;
    110   fs->regs.cfa_offset = new_cfa - (long) context->cfa;
    111 
    112   /* The SVR4 register numbering macros aren't usable in libgcc.  */
    113   fs->regs.how[0] = REG_SAVED_OFFSET;
    114   fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(rax) - new_cfa;
    115   fs->regs.how[1] = REG_SAVED_OFFSET;
    116   fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(rdx) - new_cfa;
    117   fs->regs.how[2] = REG_SAVED_OFFSET;
    118   fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(rcx) - new_cfa;
    119   fs->regs.how[3] = REG_SAVED_OFFSET;
    120   fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(rbx) - new_cfa;
    121   fs->regs.how[4] = REG_SAVED_OFFSET;
    122   fs->regs.reg[4].loc.offset = (long)&sf->REG_NAME(rsi) - new_cfa;
    123   fs->regs.how[5] = REG_SAVED_OFFSET;
    124   fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(rdi) - new_cfa;
    125   fs->regs.how[6] = REG_SAVED_OFFSET;
    126   fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(rbp) - new_cfa;
    127   fs->regs.how[8] = REG_SAVED_OFFSET;
    128   fs->regs.reg[8].loc.offset = (long)&sf->REG_NAME(r8) - new_cfa;
    129   fs->regs.how[9] = REG_SAVED_OFFSET;
    130   fs->regs.reg[9].loc.offset = (long)&sf->REG_NAME(r9) - new_cfa;
    131   fs->regs.how[10] = REG_SAVED_OFFSET;
    132   fs->regs.reg[10].loc.offset = (long)&sf->REG_NAME(r10) - new_cfa;
    133   fs->regs.how[11] = REG_SAVED_OFFSET;
    134   fs->regs.reg[11].loc.offset = (long)&sf->REG_NAME(r11) - new_cfa;
    135   fs->regs.how[12] = REG_SAVED_OFFSET;
    136   fs->regs.reg[12].loc.offset = (long)&sf->REG_NAME(r12) - new_cfa;
    137   fs->regs.how[13] = REG_SAVED_OFFSET;
    138   fs->regs.reg[13].loc.offset = (long)&sf->REG_NAME(r13) - new_cfa;
    139   fs->regs.how[14] = REG_SAVED_OFFSET;
    140   fs->regs.reg[14].loc.offset = (long)&sf->REG_NAME(r14) - new_cfa;
    141   fs->regs.how[15] = REG_SAVED_OFFSET;
    142   fs->regs.reg[15].loc.offset = (long)&sf->REG_NAME(r15) - new_cfa;
    143   fs->regs.how[16] = REG_SAVED_OFFSET;
    144   fs->regs.reg[16].loc.offset = (long)&sf->REG_NAME(rip) - new_cfa;
    145   fs->retaddr_column = 16;
    146   fs->signal_frame = 1;
    147   return _URC_NO_REASON;
    148 }
    149 
    150 #else /* Next section is for i386  */
    151 
    152 #define MD_FALLBACK_FRAME_STATE_FOR x86_freebsd_fallback_frame_state
    153 
    154 /*
    155  * We can't use KERN_PS_STRINGS anymore if we want to support FreeBSD32
    156  * compat on AMD64.  The sigtramp is in a shared page in that case so the
    157  * x86_sigtramp_range only works on a true i386 system.  We have to
    158  * search for the sigtramp frame if we want it working everywhere.
    159  */
    160 
    161 static _Unwind_Reason_Code
    162 x86_freebsd_fallback_frame_state
    163 (struct _Unwind_Context *context, _Unwind_FrameState *fs)
    164 {
    165   struct sigframe *sf;
    166   long new_cfa;
    167 
    168 /*
    169  * i386 sigtramp frame we are looking for follows.
    170  * Apparently PSL_VM is variable, so we can't look past context->ra + 4
    171  * <sigcode>:
    172  *   0:	ff 54 24 10          	call   *0x10(%esp)          *SIGF_HANDLER
    173  *   4:	8d 44 24 20          	lea    0x20(%esp),%eax       SIGF_UC
    174  *   8:	50                   	push   %eax
    175  *   9:	f7 40 54 00 00 02 00 	testl  $0x20000,0x54(%eax)  $PSL_VM
    176  *  10:	75 03                	jne    15 <sigcode+0x15>
    177  *  12:	8e 68 14             	mov    0x14(%eax),%gs        UC_GS
    178  *  15:	b8 a1 01 00 00       	mov    0x1a1,%eax           $SYS_sigreturn
    179  */
    180 
    181   if (!(   *(unsigned int *)(context->ra - 4) == 0x102454ff
    182         && *(unsigned int *)(context->ra)     == 0x2024448d ))
    183     return _URC_END_OF_STACK;
    184 
    185   sf = (struct sigframe *) context->cfa;
    186   new_cfa = sf->REG_NAME(esp);
    187   fs->regs.cfa_how = CFA_REG_OFFSET;
    188   fs->regs.cfa_reg = 4;
    189   fs->regs.cfa_offset = new_cfa - (long) context->cfa;
    190 
    191   /* The SVR4 register numbering macros aren't usable in libgcc.  */
    192   fs->regs.how[0] = REG_SAVED_OFFSET;
    193   fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(eax) - new_cfa;
    194   fs->regs.how[3] = REG_SAVED_OFFSET;
    195   fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(ebx) - new_cfa;
    196   fs->regs.how[1] = REG_SAVED_OFFSET;
    197   fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(ecx) - new_cfa;
    198   fs->regs.how[2] = REG_SAVED_OFFSET;
    199   fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(edx) - new_cfa;
    200   fs->regs.how[6] = REG_SAVED_OFFSET;
    201   fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(esi) - new_cfa;
    202   fs->regs.how[7] = REG_SAVED_OFFSET;
    203   fs->regs.reg[7].loc.offset = (long)&sf->REG_NAME(edi) - new_cfa;
    204   fs->regs.how[5] = REG_SAVED_OFFSET;
    205   fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(ebp) - new_cfa;
    206   fs->regs.how[8] = REG_SAVED_OFFSET;
    207   fs->regs.reg[8].loc.offset = (long)&sf->REG_NAME(eip) - new_cfa;
    208   fs->retaddr_column = 8;
    209   fs->signal_frame = 1;
    210   return _URC_NO_REASON;
    211 }
    212 #endif /* ifdef __x86_64__  */
    213