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