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

Packit 6c4009
/* Install given context.
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 2cdcdf
#include <asm/prctl.h>
Packit 6c4009
Packit 6c4009
#include "ucontext_i.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
ENTRY(__setcontext)
Packit 6c4009
	/* Load address of the context data structure.  */
Packit 6c4009
	movl	4(%esp), %eax
Packit 6c4009
Packit 6c4009
	/* Get the current signal mask.  Note that we preserve EBX in case
Packit 6c4009
	   the system call fails and we return from the function with an
Packit 6c4009
	   error.  */
Packit 6c4009
	pushl	%ebx
Packit 6c4009
	cfi_adjust_cfa_offset (4)
Packit 6c4009
	xorl	%edx, %edx
Packit 6c4009
	leal	oSIGMASK(%eax), %ecx
Packit 6c4009
	movl	$SIG_SETMASK, %ebx
Packit 6c4009
	cfi_rel_offset (ebx, 0)
Packit 6c4009
	movl	$__NR_sigprocmask, %eax
Packit 6c4009
	ENTER_KERNEL
Packit 6c4009
	popl	%ebx
Packit 6c4009
	cfi_adjust_cfa_offset (-4)
Packit 6c4009
	cfi_restore (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	4(%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), %ecx
Packit 6c4009
	movw	%cx, %fs
Packit 6c4009
Packit 6c4009
	/* Load the new stack pointer.  */
Packit 6c4009
	cfi_def_cfa (eax, 0)
Packit 6c4009
	cfi_offset (edi, oEDI)
Packit 6c4009
	cfi_offset (esi, oESI)
Packit 6c4009
	cfi_offset (ebp, oEBP)
Packit 6c4009
	cfi_offset (ebx, oEBX)
Packit 6c4009
	movl	oESP(%eax), %esp
Packit 6c4009
Packit Service 2cdcdf
#if SHSTK_ENABLED
Packit Service 2cdcdf
	/* Check if Shadow Stack is enabled.  */
Packit Service 2cdcdf
	testl	$X86_FEATURE_1_SHSTK, %gs:FEATURE_1_OFFSET
Packit Service 2cdcdf
	jz	L(no_shstk)
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* If the base of the target shadow stack is the same as the
Packit Service 2cdcdf
	   base of the current shadow stack, we unwind the shadow
Packit Service 2cdcdf
	   stack.  Otherwise it is a stack switch and we look for a
Packit Service 2cdcdf
	   restore token.  */
Packit Service 2cdcdf
	movl	oSSP(%eax), %esi
Packit Service 2cdcdf
	movl	%esi, %edi
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Get the base of the target shadow stack.  */
Packit Service 2cdcdf
	movl	(oSSP + 4)(%eax), %ecx
Packit Service 2cdcdf
	cmpl	%gs:SSP_BASE_OFFSET, %ecx
Packit Service 2cdcdf
	je	L(unwind_shadow_stack)
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Align the saved original shadow stack pointer to the next
Packit Service 2cdcdf
	   8 byte aligned boundary.  */
Packit Service 2cdcdf
	andl	$-8, %esi
Packit Service 2cdcdf
Packit Service 2cdcdf
L(find_restore_token_loop):
Packit Service 2cdcdf
	/* Look for a restore token.  */
Packit Service 2cdcdf
	movl	-8(%esi), %ebx
Packit Service 2cdcdf
	andl	$-8, %ebx
Packit Service 2cdcdf
	cmpl	%esi, %ebx
Packit Service 2cdcdf
	je	L(restore_shadow_stack)
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Try the next slot.  */
Packit Service 2cdcdf
	subl	$8, %esi
Packit Service 2cdcdf
	jmp	L(find_restore_token_loop)
Packit Service 2cdcdf
Packit Service 2cdcdf
L(restore_shadow_stack):
Packit Service 2cdcdf
	/* Pop return address from the shadow stack since setcontext
Packit Service 2cdcdf
	   will not return.  */
Packit Service 2cdcdf
	movl	$1, %ebx
Packit Service 2cdcdf
	incsspd	%ebx
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Use the restore stoken to restore the target shadow stack.  */
Packit Service 2cdcdf
	rstorssp -8(%esi)
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Save the restore token on the old shadow stack.  NB: This
Packit Service 2cdcdf
	   restore token may be checked by setcontext or swapcontext
Packit Service 2cdcdf
	   later.  */
Packit Service 2cdcdf
	saveprevssp
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Record the new shadow stack base that was switched to.  */
Packit Service 2cdcdf
	movl	(oSSP + 4)(%eax), %ebx
Packit Service 2cdcdf
	movl	%ebx, %gs:SSP_BASE_OFFSET
Packit Service 2cdcdf
Packit Service 2cdcdf
L(unwind_shadow_stack):
Packit Service 2cdcdf
	rdsspd	%ebx
Packit Service 2cdcdf
	subl	%edi, %ebx
Packit Service 2cdcdf
	je	L(skip_unwind_shadow_stack)
Packit Service 2cdcdf
	negl	%ebx
Packit Service 2cdcdf
	shrl	$2, %ebx
Packit Service 2cdcdf
	movl	$255, %esi
Packit Service 2cdcdf
L(loop):
Packit Service 2cdcdf
	cmpl	%esi, %ebx
Packit Service 2cdcdf
	cmovb	%ebx, %esi
Packit Service 2cdcdf
	incsspd	%esi
Packit Service 2cdcdf
	subl	%esi, %ebx
Packit Service 2cdcdf
	ja	L(loop)
Packit Service 2cdcdf
Packit Service 2cdcdf
L(skip_unwind_shadow_stack):
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Load the values of all the preserved registers (except ESP).  */
Packit Service 2cdcdf
	movl	oEDI(%eax), %edi
Packit Service 2cdcdf
	movl	oESI(%eax), %esi
Packit Service 2cdcdf
	movl	oEBP(%eax), %ebp
Packit Service 2cdcdf
	movl	oEBX(%eax), %ebx
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Get the return address set with getcontext.  */
Packit Service 2cdcdf
	movl	oEIP(%eax), %ecx
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Check if return address is valid for the case when setcontext
Packit Service 2cdcdf
	   is invoked from L(exitcode) with linked context.  */
Packit Service 2cdcdf
	rdsspd	%eax
Packit Service 2cdcdf
	cmpl	(%eax), %ecx
Packit Service 2cdcdf
	/* Clear EAX to indicate success.  NB: Don't use xorl to keep
Packit Service 2cdcdf
	   EFLAGS for jne.  */
Packit Service 2cdcdf
	movl	$0, %eax
Packit Service 2cdcdf
	jne	L(jmp)
Packit Service 2cdcdf
	/* Return to the new context if return address valid.  */
Packit Service 2cdcdf
	pushl	%ecx
Packit Service 2cdcdf
	ret
Packit Service 2cdcdf
Packit Service 2cdcdf
L(jmp):
Packit Service 2cdcdf
	/* Jump to the new context directly.  */
Packit Service 2cdcdf
	jmp	*%ecx
Packit Service 2cdcdf
Packit Service 2cdcdf
L(no_shstk):
Packit Service 2cdcdf
#endif
Packit Service 2cdcdf
Packit Service 2cdcdf
	/* Fetch the address to return to.  */
Packit Service 2cdcdf
	movl	oEIP(%eax), %ecx
Packit Service 2cdcdf
Packit 6c4009
	/* Push the return address on the new stack so we can return there.  */
Packit 6c4009
	pushl	%ecx
Packit 6c4009
Packit Service bf73bd
	/* 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 bf73bd
Packit Service bf73bd
	/* All done, return 0 for success.  */
Packit Service bf73bd
	xorl	%eax, %eax
Packit 6c4009
Packit 6c4009
	/* End FDE here, we fall into another context.  */
Packit 6c4009
	cfi_endproc
Packit 6c4009
	cfi_startproc
Packit 6c4009
Packit 6c4009
	/* The following 'ret' will pop the address of the code and jump
Packit 6c4009
	   to it.  */
Packit 6c4009
Packit 6c4009
	ret
Packit 6c4009
PSEUDO_END(__setcontext)
Packit 6c4009
libc_hidden_def (__setcontext)
Packit 6c4009
Packit 6c4009
weak_alias (__setcontext, setcontext)