]>
Commit | Line | Data |
---|---|---|
89c54dd3 | 1 | /* DWARF2 EH unwinding support for FreeBSD: AMD x86-64 and x86. |
7adcbafe | 2 | Copyright (C) 2015-2022 Free Software Foundation, Inc. |
89c54dd3 JM |
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> | |
59fcf6c3 AT |
31 | #include <unistd.h> |
32 | #include <sys/sysctl.h> | |
89c54dd3 | 33 | #include <sys/ucontext.h> |
59fcf6c3 | 34 | #include <sys/user.h> |
89c54dd3 JM |
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 | ||
59fcf6c3 AT |
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 | ||
89c54dd3 JM |
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 | ||
59fcf6c3 | 81 | #ifndef KERN_PROC_SIGTRAMP |
89c54dd3 JM |
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; | |
59fcf6c3 AT |
101 | #else |
102 | if (x86_64_outside_sigtramp_range(context->ra)) | |
103 | return _URC_END_OF_STACK; | |
104 | #endif | |
89c54dd3 JM |
105 | |
106 | sf = (struct sigframe *) context->cfa; | |
107 | new_cfa = sf->REG_NAME(rsp); | |
108 | fs->regs.cfa_how = CFA_REG_OFFSET; | |
59fcf6c3 | 109 | fs->regs.cfa_reg = __LIBGCC_STACK_POINTER_REGNUM__; |
89c54dd3 JM |
110 | fs->regs.cfa_offset = new_cfa - (long) context->cfa; |
111 | ||
112 | /* The SVR4 register numbering macros aren't usable in libgcc. */ | |
146e4591 | 113 | fs->regs.how[0] = REG_SAVED_OFFSET; |
89c54dd3 | 114 | fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(rax) - new_cfa; |
146e4591 | 115 | fs->regs.how[1] = REG_SAVED_OFFSET; |
89c54dd3 | 116 | fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(rdx) - new_cfa; |
146e4591 | 117 | fs->regs.how[2] = REG_SAVED_OFFSET; |
89c54dd3 | 118 | fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(rcx) - new_cfa; |
146e4591 | 119 | fs->regs.how[3] = REG_SAVED_OFFSET; |
89c54dd3 | 120 | fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(rbx) - new_cfa; |
146e4591 | 121 | fs->regs.how[4] = REG_SAVED_OFFSET; |
89c54dd3 | 122 | fs->regs.reg[4].loc.offset = (long)&sf->REG_NAME(rsi) - new_cfa; |
146e4591 | 123 | fs->regs.how[5] = REG_SAVED_OFFSET; |
89c54dd3 | 124 | fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(rdi) - new_cfa; |
146e4591 | 125 | fs->regs.how[6] = REG_SAVED_OFFSET; |
89c54dd3 | 126 | fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(rbp) - new_cfa; |
146e4591 | 127 | fs->regs.how[8] = REG_SAVED_OFFSET; |
89c54dd3 | 128 | fs->regs.reg[8].loc.offset = (long)&sf->REG_NAME(r8) - new_cfa; |
146e4591 | 129 | fs->regs.how[9] = REG_SAVED_OFFSET; |
89c54dd3 | 130 | fs->regs.reg[9].loc.offset = (long)&sf->REG_NAME(r9) - new_cfa; |
146e4591 | 131 | fs->regs.how[10] = REG_SAVED_OFFSET; |
89c54dd3 | 132 | fs->regs.reg[10].loc.offset = (long)&sf->REG_NAME(r10) - new_cfa; |
146e4591 | 133 | fs->regs.how[11] = REG_SAVED_OFFSET; |
89c54dd3 | 134 | fs->regs.reg[11].loc.offset = (long)&sf->REG_NAME(r11) - new_cfa; |
146e4591 | 135 | fs->regs.how[12] = REG_SAVED_OFFSET; |
89c54dd3 | 136 | fs->regs.reg[12].loc.offset = (long)&sf->REG_NAME(r12) - new_cfa; |
146e4591 | 137 | fs->regs.how[13] = REG_SAVED_OFFSET; |
89c54dd3 | 138 | fs->regs.reg[13].loc.offset = (long)&sf->REG_NAME(r13) - new_cfa; |
146e4591 | 139 | fs->regs.how[14] = REG_SAVED_OFFSET; |
89c54dd3 | 140 | fs->regs.reg[14].loc.offset = (long)&sf->REG_NAME(r14) - new_cfa; |
146e4591 | 141 | fs->regs.how[15] = REG_SAVED_OFFSET; |
89c54dd3 | 142 | fs->regs.reg[15].loc.offset = (long)&sf->REG_NAME(r15) - new_cfa; |
146e4591 | 143 | fs->regs.how[16] = REG_SAVED_OFFSET; |
89c54dd3 JM |
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. */ | |
146e4591 | 192 | fs->regs.how[0] = REG_SAVED_OFFSET; |
89c54dd3 | 193 | fs->regs.reg[0].loc.offset = (long)&sf->REG_NAME(eax) - new_cfa; |
146e4591 | 194 | fs->regs.how[3] = REG_SAVED_OFFSET; |
89c54dd3 | 195 | fs->regs.reg[3].loc.offset = (long)&sf->REG_NAME(ebx) - new_cfa; |
146e4591 | 196 | fs->regs.how[1] = REG_SAVED_OFFSET; |
89c54dd3 | 197 | fs->regs.reg[1].loc.offset = (long)&sf->REG_NAME(ecx) - new_cfa; |
146e4591 | 198 | fs->regs.how[2] = REG_SAVED_OFFSET; |
89c54dd3 | 199 | fs->regs.reg[2].loc.offset = (long)&sf->REG_NAME(edx) - new_cfa; |
146e4591 | 200 | fs->regs.how[6] = REG_SAVED_OFFSET; |
89c54dd3 | 201 | fs->regs.reg[6].loc.offset = (long)&sf->REG_NAME(esi) - new_cfa; |
146e4591 | 202 | fs->regs.how[7] = REG_SAVED_OFFSET; |
89c54dd3 | 203 | fs->regs.reg[7].loc.offset = (long)&sf->REG_NAME(edi) - new_cfa; |
146e4591 | 204 | fs->regs.how[5] = REG_SAVED_OFFSET; |
89c54dd3 | 205 | fs->regs.reg[5].loc.offset = (long)&sf->REG_NAME(ebp) - new_cfa; |
146e4591 | 206 | fs->regs.how[8] = REG_SAVED_OFFSET; |
89c54dd3 JM |
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__ */ |