1 /* Save current context and install the given one.
2 Copyright (C) 2001-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
20 #include <asm/prctl.h>
22 #include "ucontext_i.h"
26 /* Load address of the context data structure we save in. */
29 /* Save the preserved register values and the return address. */
39 /* Save the FS segment register. */
44 /* We have separate floating-point register content memory on the
45 stack. We use the __fpregs_mem block in the context. Set the
46 links up correctly. */
47 leal oFPREGSMEM(%eax), %ecx
48 movl %ecx, oFPREGS(%eax)
49 /* Save the floating-point context. */
52 /* Load address of the context data structure we have to load. */
55 /* Save the current signal mask and install the new one. */
57 leal oSIGMASK(%eax), %edx
58 leal oSIGMASK(%ecx), %ecx
59 movl $SIG_SETMASK, %ebx
60 movl $__NR_sigprocmask, %eax
63 cmpl $-4095, %eax /* Check %eax for error. */
64 jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
66 /* EAX was modified, reload it. */
69 /* Restore the floating-point context. Not the registers, only the
71 movl oFPREGS(%eax), %ecx
74 /* Restore the FS segment register. We don't touch the GS register
75 since it is used for threads. */
80 /* Check if Shadow Stack is enabled. */
81 testl $X86_FEATURE_1_SHSTK, %gs:FEATURE_1_OFFSET
85 cmpl %gs:SSP_BASE_OFFSET, %eax
86 jnz L(shadow_stack_bound_recorded)
88 /* Get the base address and size of the default shadow stack
89 which must be the current shadow stack since nothing has
93 movl $ARCH_CET_STATUS, %ebx
94 movl $__NR_arch_prctl, %eax
99 /* This should never happen. */
103 /* Record the base of the current shadow stack. */
105 movl %eax, %gs:SSP_BASE_OFFSET
108 L(shadow_stack_bound_recorded):
109 /* Load address of the context data structure we save in. */
112 /* Load address of the context data structure we swap in */
115 /* If we unwind the stack, we can't undo stack unwinding. Just
116 save the target shadow stack pointer as the current shadow
118 movl oSSP(%edx), %ecx
119 movl %ecx, oSSP(%eax)
121 /* Save the current shadow stack base in ucontext. */
122 movl %gs:SSP_BASE_OFFSET, %ecx
123 movl %ecx, (oSSP + 4)(%eax)
125 /* If the base of the target shadow stack is the same as the
126 base of the current shadow stack, we unwind the shadow
127 stack. Otherwise it is a stack switch and we look for a
129 movl oSSP(%edx), %esi
132 /* Get the base of the target shadow stack. */
133 movl (oSSP + 4)(%edx), %ecx
134 cmpl %gs:SSP_BASE_OFFSET, %ecx
135 je L(unwind_shadow_stack)
137 /* Align the saved original shadow stack pointer to the next
138 8 byte aligned boundary. */
141 L(find_restore_token_loop):
142 /* Look for a restore token. */
146 je L(restore_shadow_stack)
148 /* Try the next slot. */
150 jmp L(find_restore_token_loop)
152 L(restore_shadow_stack):
153 /* The target shadow stack will be restored. Save the current
154 shadow stack pointer. */
156 movl %ecx, oSSP(%eax)
158 /* Use the restore stoken to restore the target shadow stack. */
161 /* Save the restore token on the old shadow stack. NB: This
162 restore token may be checked by setcontext or swapcontext
166 /* Record the new shadow stack base that was switched to. */
167 movl (oSSP + 4)(%edx), %ebx
168 movl %ebx, %gs:SSP_BASE_OFFSET
170 L(unwind_shadow_stack):
173 je L(skip_unwind_shadow_stack)
184 L(skip_unwind_shadow_stack):
186 /* Load the new stack pointer. */
187 movl oESP(%edx), %esp
189 /* Load the values of all the preserved registers (except ESP). */
190 movl oEDI(%edx), %edi
191 movl oESI(%edx), %esi
192 movl oEBP(%edx), %ebp
193 movl oEBX(%edx), %ebx
195 /* Get the return address set with getcontext. */
196 movl oEIP(%edx), %ecx
198 /* Check if return address is valid for the case when setcontext
199 is invoked from L(exitcode) with linked context. */
202 /* Clear EAX to indicate success. NB: Don't use xorl to keep
206 /* Return to the new context if return address valid. */
211 /* Jump to the new context directly. */
217 /* Fetch the address to return to. */
218 movl oEIP(%eax), %ecx
220 /* Load the new stack pointer. */
221 movl oESP(%eax), %esp
223 /* Push the return address on the new stack so we can return there. */
226 /* Load the values of all the preserved registers (except ESP). */
227 movl oEDI(%eax), %edi
228 movl oESI(%eax), %esi
229 movl oEBP(%eax), %ebp
230 movl oEBX(%eax), %ebx
232 /* All done, return 0 for success. */
235 /* The following 'ret' will pop the address of the code and jump
238 PSEUDO_END(__swapcontext)
240 weak_alias (__swapcontext, swapcontext)