1 /* DWARF2 EH unwinding support for FreeBSD: AMD x86-64 and x86. 2 Copyright (C) 2015-2020 Free Software Foundation, Inc. 3 Contributed by John Marino <gnugcc@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.reg[0].how = REG_SAVED_OFFSET; 114 fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(rax) - new_cfa; 115 fs->regs.reg[1].how = REG_SAVED_OFFSET; 116 fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(rdx) - new_cfa; 117 fs->regs.reg[2].how = REG_SAVED_OFFSET; 118 fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(rcx) - new_cfa; 119 fs->regs.reg[3].how = REG_SAVED_OFFSET; 120 fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(rbx) - new_cfa; 121 fs->regs.reg[4].how = REG_SAVED_OFFSET; 122 fs->regs.reg[4].loc.offset = (long)&sf->REG_NAME(rsi) - new_cfa; 123 fs->regs.reg[5].how = REG_SAVED_OFFSET; 124 fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(rdi) - new_cfa; 125 fs->regs.reg[6].how = REG_SAVED_OFFSET; 126 fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(rbp) - new_cfa; 127 fs->regs.reg[8].how = REG_SAVED_OFFSET; 128 fs->regs.reg[8].loc.offset = (long)&sf->REG_NAME(r8) - new_cfa; 129 fs->regs.reg[9].how = REG_SAVED_OFFSET; 130 fs->regs.reg[9].loc.offset = (long)&sf->REG_NAME(r9) - new_cfa; 131 fs->regs.reg[10].how = REG_SAVED_OFFSET; 132 fs->regs.reg[10].loc.offset = (long)&sf->REG_NAME(r10) - new_cfa; 133 fs->regs.reg[11].how = REG_SAVED_OFFSET; 134 fs->regs.reg[11].loc.offset = (long)&sf->REG_NAME(r11) - new_cfa; 135 fs->regs.reg[12].how = REG_SAVED_OFFSET; 136 fs->regs.reg[12].loc.offset = (long)&sf->REG_NAME(r12) - new_cfa; 137 fs->regs.reg[13].how = REG_SAVED_OFFSET; 138 fs->regs.reg[13].loc.offset = (long)&sf->REG_NAME(r13) - new_cfa; 139 fs->regs.reg[14].how = REG_SAVED_OFFSET; 140 fs->regs.reg[14].loc.offset = (long)&sf->REG_NAME(r14) - new_cfa; 141 fs->regs.reg[15].how = REG_SAVED_OFFSET; 142 fs->regs.reg[15].loc.offset = (long)&sf->REG_NAME(r15) - new_cfa; 143 fs->regs.reg[16].how = 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.reg[0].how = REG_SAVED_OFFSET; 193 fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(eax) - new_cfa; 194 fs->regs.reg[3].how = REG_SAVED_OFFSET; 195 fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(ebx) - new_cfa; 196 fs->regs.reg[1].how = REG_SAVED_OFFSET; 197 fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(ecx) - new_cfa; 198 fs->regs.reg[2].how = REG_SAVED_OFFSET; 199 fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(edx) - new_cfa; 200 fs->regs.reg[6].how = REG_SAVED_OFFSET; 201 fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(esi) - new_cfa; 202 fs->regs.reg[7].how = REG_SAVED_OFFSET; 203 fs->regs.reg[7].loc.offset = (long)&sf->REG_NAME(edi) - new_cfa; 204 fs->regs.reg[5].how = REG_SAVED_OFFSET; 205 fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(ebp) - new_cfa; 206 fs->regs.reg[8].how = 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