Blame sysdeps/unix/sysv/linux/i386/swapcontext.S

Packit 6c4009
/* Save current context and install the given one.
Packit 6c4009
   Copyright (C) 2001-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <sysdep.h>
Packit Service 701d2f
#include <asm/prctl.h>
Packit 6c4009
Packit 6c4009
#include "ucontext_i.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
ENTRY(__swapcontext)
Packit 6c4009
	/* Load address of the context data structure we save in.  */
Packit 6c4009
	movl	4(%esp), %eax
Packit 6c4009
Packit Service 8ac21f
	/* Save the preserved register values and the return address.  */
Packit 6c4009
	movl	%edi, oEDI(%eax)
Packit 6c4009
	movl	%esi, oESI(%eax)
Packit 6c4009
	movl	%ebp, oEBP(%eax)
Packit 6c4009
	movl	(%esp), %ecx
Packit 6c4009
	movl	%ecx, oEIP(%eax)
Packit 6c4009
	leal	4(%esp), %ecx
Packit 6c4009
	movl	%ecx, oESP(%eax)
Packit 6c4009
	movl	%ebx, oEBX(%eax)
Packit 6c4009
Packit 6c4009
	/* Save the FS segment register.  */
Packit 6c4009
	xorl	%edx, %edx
Packit 6c4009
	movw	%fs, %dx
Packit 6c4009
	movl	%edx, oFS(%eax)
Packit 6c4009
Packit 6c4009
	/* We have separate floating-point register content memory on the
Packit 6c4009
	   stack.  We use the __fpregs_mem block in the context.  Set the
Packit 6c4009
	   links up correctly.  */
Packit 6c4009
	leal	oFPREGSMEM(%eax), %ecx
Packit 6c4009
	movl	%ecx, oFPREGS(%eax)
Packit 6c4009
	/* Save the floating-point context.  */
Packit 6c4009
	fnstenv	(%ecx)
Packit 6c4009
Packit 6c4009
	/* Load address of the context data structure we have to load.  */
Packit 6c4009
	movl	8(%esp), %ecx
Packit 6c4009
Packit 6c4009
	/* Save the current signal mask and install the new one.  */
Packit 6c4009
	pushl	%ebx
Packit 6c4009
	leal	oSIGMASK(%eax), %edx
Packit 6c4009
	leal	oSIGMASK(%ecx), %ecx
Packit 6c4009
	movl	$SIG_SETMASK, %ebx
Packit 6c4009
	movl	$__NR_sigprocmask, %eax
Packit 6c4009
	ENTER_KERNEL
Packit 6c4009
	popl	%ebx
Packit 6c4009
	cmpl	$-4095, %eax		/* Check %eax for error.  */
Packit 6c4009
	jae	SYSCALL_ERROR_LABEL	/* Jump to error handler if error.  */
Packit 6c4009
Packit 6c4009
	/* EAX was modified, reload it.  */
Packit 6c4009
	movl	8(%esp), %eax
Packit 6c4009
Packit 6c4009
	/* Restore the floating-point context.  Not the registers, only the
Packit 6c4009
	   rest.  */
Packit 6c4009
	movl	oFPREGS(%eax), %ecx
Packit 6c4009
	fldenv	(%ecx)
Packit 6c4009
Packit 6c4009
	/* Restore the FS segment register.  We don't touch the GS register
Packit 6c4009
	   since it is used for threads.  */
Packit 6c4009
	movl	oFS(%eax), %edx
Packit 6c4009
	movw	%dx, %fs
Packit 6c4009
Packit Service 701d2f
#if SHSTK_ENABLED
Packit Service 701d2f
	/* Check if Shadow Stack is enabled.  */
Packit Service 701d2f
	testl	$X86_FEATURE_1_SHSTK, %gs:FEATURE_1_OFFSET
Packit Service 701d2f
	jz	L(no_shstk)
Packit Service 701d2f
Packit Service 701d2f
	xorl	%eax, %eax
Packit Service 701d2f
	cmpl	%gs:SSP_BASE_OFFSET, %eax
Packit Service 701d2f
	jnz	L(shadow_stack_bound_recorded)
Packit Service 701d2f
Packit Service 701d2f
	/* Get the base address and size of the default shadow stack
Packit Service 701d2f
	   which must be the current shadow stack since nothing has
Packit Service 701d2f
	   been recorded yet.  */
Packit Service 701d2f
	sub	$24, %esp
Packit Service 701d2f
	mov	%esp, %ecx
Packit Service 701d2f
	movl	$ARCH_CET_STATUS, %ebx
Packit Service 701d2f
	movl	$__NR_arch_prctl, %eax
Packit Service 701d2f
	ENTER_KERNEL
Packit Service 701d2f
	testl	%eax, %eax
Packit Service 701d2f
	jz	L(continue_no_err)
Packit Service 701d2f
Packit Service 701d2f
	/* This should never happen.  */
Packit Service 701d2f
	hlt
Packit Service 701d2f
Packit Service 701d2f
L(continue_no_err):
Packit Service 701d2f
	/* Record the base of the current shadow stack.  */
Packit Service 701d2f
	movl	8(%esp), %eax
Packit Service 701d2f
	movl	%eax, %gs:SSP_BASE_OFFSET
Packit Service 701d2f
	add	$24, %esp
Packit Service 701d2f
Packit Service 701d2f
L(shadow_stack_bound_recorded):
Packit Service 701d2f
	/* Load address of the context data structure we save in.  */
Packit Service 701d2f
	movl	4(%esp), %eax
Packit Service 701d2f
Packit Service 701d2f
	/* Load address of the context data structure we swap in  */
Packit Service 701d2f
	movl	8(%esp), %edx
Packit Service 701d2f
Packit Service 701d2f
       /* If we unwind the stack, we can't undo stack unwinding.  Just
Packit Service 701d2f
	   save the target shadow stack pointer as the current shadow
Packit Service 701d2f
	   stack pointer.   */
Packit Service 701d2f
	movl	oSSP(%edx), %ecx
Packit Service 701d2f
	movl	%ecx, oSSP(%eax)
Packit Service 701d2f
Packit Service 701d2f
	/* Save the current shadow stack base in ucontext.  */
Packit Service 701d2f
	movl	%gs:SSP_BASE_OFFSET, %ecx
Packit Service 701d2f
	movl	%ecx, (oSSP + 4)(%eax)
Packit Service 701d2f
Packit Service 701d2f
	/* If the base of the target shadow stack is the same as the
Packit Service 701d2f
	   base of the current shadow stack, we unwind the shadow
Packit Service 701d2f
	   stack.  Otherwise it is a stack switch and we look for a
Packit Service 701d2f
	   restore token.  */
Packit Service 701d2f
	movl	oSSP(%edx), %esi
Packit Service 701d2f
	movl	%esi, %edi
Packit Service 701d2f
Packit Service 701d2f
	/* Get the base of the target shadow stack.  */
Packit Service 701d2f
	movl	(oSSP + 4)(%edx), %ecx
Packit Service 701d2f
	cmpl	%gs:SSP_BASE_OFFSET, %ecx
Packit Service 701d2f
	je	L(unwind_shadow_stack)
Packit Service 701d2f
Packit Service 701d2f
	/* Align the saved original shadow stack pointer to the next
Packit Service 701d2f
	   8 byte aligned boundary.  */
Packit Service 701d2f
	andl	$-8, %esi
Packit Service 701d2f
Packit Service 701d2f
L(find_restore_token_loop):
Packit Service 701d2f
	/* Look for a restore token.  */
Packit Service 701d2f
	movl	-8(%esi), %ebx
Packit Service 701d2f
	andl	$-8, %ebx
Packit Service 701d2f
	cmpl	%esi, %ebx
Packit Service 701d2f
	je	L(restore_shadow_stack)
Packit Service 701d2f
Packit Service 701d2f
	/* Try the next slot.  */
Packit Service 701d2f
	subl	$8, %esi
Packit Service 701d2f
	jmp	L(find_restore_token_loop)
Packit Service 701d2f
Packit Service 701d2f
L(restore_shadow_stack):
Packit Service 701d2f
	/* The target shadow stack will be restored.  Save the current
Packit Service 701d2f
	   shadow stack pointer.  */
Packit Service 701d2f
	rdsspd	%ecx
Packit Service 701d2f
	movl	%ecx, oSSP(%eax)
Packit Service 701d2f
Packit Service 701d2f
	/* Use the restore stoken to restore the target shadow stack.  */
Packit Service 701d2f
	rstorssp -8(%esi)
Packit Service 701d2f
Packit Service 701d2f
	/* Save the restore token on the old shadow stack.  NB: This
Packit Service 701d2f
	   restore token may be checked by setcontext or swapcontext
Packit Service 701d2f
	   later.  */
Packit Service 701d2f
	saveprevssp
Packit Service 701d2f
Packit Service 701d2f
	/* Record the new shadow stack base that was switched to.  */
Packit Service 701d2f
	movl	(oSSP + 4)(%edx), %ebx
Packit Service 701d2f
	movl	%ebx, %gs:SSP_BASE_OFFSET
Packit Service 701d2f
Packit Service 701d2f
L(unwind_shadow_stack):
Packit Service 701d2f
	rdsspd	%ebx
Packit Service 701d2f
	subl	%edi, %ebx
Packit Service 701d2f
	je	L(skip_unwind_shadow_stack)
Packit Service 701d2f
	negl	%ebx
Packit Service 701d2f
	shrl	$2, %ebx
Packit Service 701d2f
	movl	$255, %esi
Packit Service 701d2f
L(loop):
Packit Service 701d2f
	cmpl	%esi, %ebx
Packit Service 701d2f
	cmovb	%ebx, %esi
Packit Service 701d2f
	incsspd	%esi
Packit Service 701d2f
	subl	%esi, %ebx
Packit Service 701d2f
	ja	L(loop)
Packit Service 701d2f
Packit Service 701d2f
L(skip_unwind_shadow_stack):
Packit Service 701d2f
Packit Service 701d2f
	/* Load the new stack pointer.  */
Packit Service 701d2f
	movl	oESP(%edx), %esp
Packit Service 701d2f
Packit Service 701d2f
	/* Load the values of all the preserved registers (except ESP).  */
Packit Service 701d2f
	movl	oEDI(%edx), %edi
Packit Service 701d2f
	movl	oESI(%edx), %esi
Packit Service 701d2f
	movl	oEBP(%edx), %ebp
Packit Service 701d2f
	movl	oEBX(%edx), %ebx
Packit Service 701d2f
Packit Service 701d2f
	/* Get the return address set with getcontext.  */
Packit Service 701d2f
	movl	oEIP(%edx), %ecx
Packit Service 701d2f
Packit Service 701d2f
	/* Check if return address is valid for the case when setcontext
Packit Service 701d2f
	   is invoked from L(exitcode) with linked context.  */
Packit Service 701d2f
	rdsspd	%eax
Packit Service 701d2f
	cmpl	(%eax), %ecx
Packit Service 701d2f
	/* Clear EAX to indicate success.  NB: Don't use xorl to keep
Packit Service 701d2f
	   EFLAGS for jne.  */
Packit Service 701d2f
	movl	$0, %eax
Packit Service 701d2f
	jne	L(jmp)
Packit Service 701d2f
	/* Return to the new context if return address valid.  */
Packit Service 701d2f
	pushl	%ecx
Packit Service 701d2f
	ret
Packit Service 701d2f
Packit Service 701d2f
L(jmp):
Packit Service 701d2f
	/* Jump to the new context directly.  */
Packit Service 701d2f
	jmp	*%ecx
Packit Service 701d2f
Packit Service 701d2f
L(no_shstk):
Packit Service 701d2f
#endif
Packit Service 701d2f
Packit 6c4009
	/* Fetch the address to return to.  */
Packit 6c4009
	movl	oEIP(%eax), %ecx
Packit 6c4009
Packit 6c4009
	/* Load the new stack pointer.  */
Packit 6c4009
	movl	oESP(%eax), %esp
Packit 6c4009
Packit 6c4009
	/* Push the return address on the new stack so we can return there.  */
Packit 6c4009
	pushl	%ecx
Packit 6c4009
Packit Service 8ac21f
	/* Load the values of all the preserved registers (except ESP).  */
Packit 6c4009
	movl	oEDI(%eax), %edi
Packit 6c4009
	movl	oESI(%eax), %esi
Packit 6c4009
	movl	oEBP(%eax), %ebp
Packit 6c4009
	movl	oEBX(%eax), %ebx
Packit Service 8ac21f
Packit Service 8ac21f
	/* All done, return 0 for success.  */
Packit Service 8ac21f
	xorl	%eax, %eax
Packit 6c4009
Packit 6c4009
	/* The following 'ret' will pop the address of the code and jump
Packit 6c4009
	   to it.  */
Packit 6c4009
	ret
Packit 6c4009
PSEUDO_END(__swapcontext)
Packit 6c4009
Packit 6c4009
weak_alias (__swapcontext, swapcontext)