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

Packit 6c4009
/* Install given context.
Packit 6c4009
   Copyright (C) 2002-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Andreas Jaeger <aj@suse.de>, 2002.
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 6c4009
#include <asm/prctl.h>
Packit 6c4009
Packit 6c4009
#include "ucontext_i.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*  int __setcontext (const ucontext_t *ucp)
Packit 6c4009
Packit 6c4009
  Restores the machine context in UCP and thereby resumes execution
Packit 6c4009
  in that context.
Packit 6c4009
Packit 6c4009
  This implementation is intended to be used for *synchronous* context
Packit 6c4009
  switches only.  Therefore, it does not have to restore anything
Packit 6c4009
  other than the PRESERVED state.  */
Packit 6c4009
Packit 6c4009
ENTRY(__setcontext)
Packit 6c4009
	/* Save argument since syscall will destroy it.  */
Packit 6c4009
	pushq	%rdi
Packit 6c4009
	cfi_adjust_cfa_offset(8)
Packit 6c4009
Packit 6c4009
	/* Set the signal mask with
Packit 6c4009
	   rt_sigprocmask (SIG_SETMASK, mask, NULL, _NSIG/8).  */
Packit 6c4009
	leaq	oSIGMASK(%rdi), %rsi
Packit 6c4009
	xorl	%edx, %edx
Packit 6c4009
	movl	$SIG_SETMASK, %edi
Packit 6c4009
	movl	$_NSIG8,%r10d
Packit 6c4009
	movl	$__NR_rt_sigprocmask, %eax
Packit 6c4009
	syscall
Packit 6c4009
	/* Pop the pointer into RDX. The choice is arbitrary, but
Packit 6c4009
	   leaving RDI and RSI available for use later can avoid
Packit 6c4009
	   shuffling values.  */
Packit 6c4009
	popq	%rdx
Packit 6c4009
	cfi_adjust_cfa_offset(-8)
Packit 6c4009
	cmpq	$-4095, %rax		/* Check %rax for error.  */
Packit 6c4009
	jae	SYSCALL_ERROR_LABEL	/* Jump to error handler if error.  */
Packit 6c4009
Packit 6c4009
	/* Restore the floating-point context.  Not the registers, only the
Packit 6c4009
	   rest.  */
Packit 6c4009
	movq	oFPREGS(%rdx), %rcx
Packit 6c4009
	fldenv	(%rcx)
Packit 6c4009
	ldmxcsr oMXCSR(%rdx)
Packit 6c4009
Packit 6c4009
Packit 6c4009
	/* Load the new stack pointer, the preserved registers and
Packit 6c4009
	   registers used for passing args.  */
Packit 6c4009
	cfi_def_cfa(%rdx, 0)
Packit 6c4009
	cfi_offset(%rbx,oRBX)
Packit 6c4009
	cfi_offset(%rbp,oRBP)
Packit 6c4009
	cfi_offset(%r12,oR12)
Packit 6c4009
	cfi_offset(%r13,oR13)
Packit 6c4009
	cfi_offset(%r14,oR14)
Packit 6c4009
	cfi_offset(%r15,oR15)
Packit 6c4009
	cfi_offset(%rsp,oRSP)
Packit 6c4009
	cfi_offset(%rip,oRIP)
Packit 6c4009
Packit 6c4009
	movq	oRSP(%rdx), %rsp
Packit 6c4009
	movq	oRBX(%rdx), %rbx
Packit 6c4009
	movq	oRBP(%rdx), %rbp
Packit 6c4009
	movq	oR12(%rdx), %r12
Packit 6c4009
	movq	oR13(%rdx), %r13
Packit 6c4009
	movq	oR14(%rdx), %r14
Packit 6c4009
	movq	oR15(%rdx), %r15
Packit 6c4009
Packit 6c4009
#if SHSTK_ENABLED
Packit 6c4009
	/* Check if shadow stack is enabled.  */
Packit 6c4009
	testl	$X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET
Packit 6c4009
	jz	L(no_shstk)
Packit 6c4009
Packit 6c4009
	/* If the base of the target shadow stack is the same as the
Packit 6c4009
	   base of the current shadow stack, we unwind the shadow
Packit 6c4009
	   stack.  Otherwise it is a stack switch and we look for a
Packit 6c4009
	   restore token.  */
Packit 6c4009
	movq	oSSP(%rdx), %rsi
Packit 6c4009
	movq	%rsi, %rdi
Packit 6c4009
Packit 6c4009
	/* Get the base of the target shadow stack.  */
Packit 6c4009
	movq	(oSSP + 8)(%rdx), %rcx
Packit 6c4009
	cmpq	%fs:SSP_BASE_OFFSET, %rcx
Packit 6c4009
	je	L(unwind_shadow_stack)
Packit 6c4009
Packit 6c4009
L(find_restore_token_loop):
Packit 6c4009
	/* Look for a restore token.  */
Packit 6c4009
	movq	-8(%rsi), %rax
Packit 6c4009
	andq	$-8, %rax
Packit 6c4009
	cmpq	%rsi, %rax
Packit 6c4009
	je	L(restore_shadow_stack)
Packit 6c4009
Packit 6c4009
	/* Try the next slot.  */
Packit 6c4009
	subq	$8, %rsi
Packit 6c4009
	jmp	L(find_restore_token_loop)
Packit 6c4009
Packit 6c4009
L(restore_shadow_stack):
Packit 6c4009
	/* Pop return address from the shadow stack since setcontext
Packit 6c4009
	   will not return.  */
Packit 6c4009
	movq	$1, %rax
Packit 6c4009
	incsspq	%rax
Packit 6c4009
Packit 6c4009
	/* Use the restore stoken to restore the target shadow stack.  */
Packit 6c4009
	rstorssp -8(%rsi)
Packit 6c4009
Packit 6c4009
	/* Save the restore token on the old shadow stack.  NB: This
Packit 6c4009
	   restore token may be checked by setcontext or swapcontext
Packit 6c4009
	   later.  */
Packit 6c4009
	saveprevssp
Packit 6c4009
Packit 6c4009
	/* Record the new shadow stack base that was switched to.  */
Packit 6c4009
	movq	(oSSP + 8)(%rdx), %rax
Packit 6c4009
	movq	%rax, %fs:SSP_BASE_OFFSET
Packit 6c4009
Packit 6c4009
L(unwind_shadow_stack):
Packit 6c4009
	rdsspq	%rcx
Packit 6c4009
	subq	%rdi, %rcx
Packit 6c4009
	je	L(skip_unwind_shadow_stack)
Packit 6c4009
	negq	%rcx
Packit 6c4009
	shrq	$3, %rcx
Packit 6c4009
	movl	$255, %esi
Packit 6c4009
L(loop):
Packit 6c4009
	cmpq	%rsi, %rcx
Packit 6c4009
	cmovb	%rcx, %rsi
Packit 6c4009
	incsspq	%rsi
Packit 6c4009
	subq	%rsi, %rcx
Packit 6c4009
	ja	L(loop)
Packit 6c4009
Packit 6c4009
L(skip_unwind_shadow_stack):
Packit 6c4009
	movq	oRSI(%rdx), %rsi
Packit 6c4009
	movq	oRDI(%rdx), %rdi
Packit 6c4009
	movq	oRCX(%rdx), %rcx
Packit 6c4009
	movq	oR8(%rdx), %r8
Packit 6c4009
	movq	oR9(%rdx), %r9
Packit 6c4009
Packit 6c4009
	/* Get the return address set with getcontext.  */
Packit 6c4009
	movq	oRIP(%rdx), %r10
Packit 6c4009
Packit 6c4009
	/* Setup finally %rdx.  */
Packit 6c4009
	movq	oRDX(%rdx), %rdx
Packit 6c4009
Packit 6c4009
	/* Check if return address is valid for the case when setcontext
Packit 6c4009
	   is invoked from __start_context with linked context.  */
Packit 6c4009
	rdsspq	%rax
Packit 6c4009
	cmpq	(%rax), %r10
Packit 6c4009
	/* Clear RAX to indicate success.  NB: Don't use xorl to keep
Packit 6c4009
	   EFLAGS for jne.  */
Packit 6c4009
	movl	$0, %eax
Packit 6c4009
	jne	L(jmp)
Packit 6c4009
	/* Return to the new context if return address valid.  */
Packit 6c4009
	pushq	%r10
Packit 6c4009
	ret
Packit 6c4009
Packit 6c4009
L(jmp):
Packit 6c4009
	/* Jump to the new context directly.  */
Packit 6c4009
	jmp	*%r10
Packit 6c4009
Packit 6c4009
L(no_shstk):
Packit 6c4009
#endif
Packit 6c4009
	/* The following ret should return to the address set with
Packit 6c4009
	getcontext.  Therefore push the address on the stack.  */
Packit 6c4009
	movq	oRIP(%rdx), %rcx
Packit 6c4009
	pushq	%rcx
Packit 6c4009
Packit 6c4009
	movq	oRSI(%rdx), %rsi
Packit 6c4009
	movq	oRDI(%rdx), %rdi
Packit 6c4009
	movq	oRCX(%rdx), %rcx
Packit 6c4009
	movq	oR8(%rdx), %r8
Packit 6c4009
	movq	oR9(%rdx), %r9
Packit 6c4009
Packit 6c4009
	/* Setup finally %rdx.  */
Packit 6c4009
	movq	oRDX(%rdx), %rdx
Packit 6c4009
Packit 6c4009
	/* End FDE here, we fall into another context.  */
Packit 6c4009
	cfi_endproc
Packit 6c4009
	cfi_startproc
Packit 6c4009
Packit 6c4009
	/* Clear rax to indicate success.  */
Packit 6c4009
	xorl	%eax, %eax
Packit 6c4009
	ret
Packit 6c4009
PSEUDO_END(__setcontext)
Packit 6c4009
Packit 6c4009
weak_alias (__setcontext, setcontext)