Blame sysdeps/unix/sysv/linux/x86_64/makecontext.c

Packit 6c4009
/* Create new 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 <stdarg.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <ucontext.h>
Packit 6c4009
#if SHSTK_ENABLED
Packit 6c4009
# include <pthread.h>
Packit 6c4009
# include <libc-pointer-arith.h>
Packit 6c4009
# include <sys/prctl.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include "ucontext_i.h"
Packit 6c4009
Packit 6c4009
/* This implementation can handle any ARGC value but only
Packit 6c4009
   normal integer parameters.
Packit 6c4009
   makecontext sets up a stack and the registers for the
Packit 6c4009
   user context. The stack looks like this:
Packit 6c4009
               +-----------------------+
Packit 6c4009
               | next context          |
Packit 6c4009
               +-----------------------+
Packit 6c4009
               | parameter 7-n         |
Packit 6c4009
	       +-----------------------+
Packit 6c4009
	       | trampoline address    |
Packit 6c4009
    %rsp ->    +-----------------------+
Packit 6c4009
Packit 6c4009
   The registers are set up like this:
Packit 6c4009
     %rdi,%rsi,%rdx,%rcx,%r8,%r9: parameter 1 to 6
Packit 6c4009
     %rbx   : address of next context
Packit 6c4009
     %rsp   : stack pointer.
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
/* XXX: This implementation currently only handles integer arguments.
Packit 6c4009
   To handle long int and pointer arguments the va_arg arguments needs
Packit 6c4009
   to be changed to long and also the stdlib/tst-setcontext.c file needs
Packit 6c4009
   to be changed to pass long arguments to makecontext.  */
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
Packit 6c4009
{
Packit 6c4009
  extern void __start_context (void) attribute_hidden;
Packit 6c4009
  extern void __push___start_context (ucontext_t *)
Packit 6c4009
    attribute_hidden;
Packit 6c4009
  greg_t *sp;
Packit 6c4009
  unsigned int idx_uc_link;
Packit 6c4009
  va_list ap;
Packit 6c4009
  int i;
Packit 6c4009
Packit 6c4009
  /* Generate room on stack for parameter if needed and uc_link.  */
Packit 6c4009
  sp = (greg_t *) ((uintptr_t) ucp->uc_stack.ss_sp
Packit 6c4009
		   + ucp->uc_stack.ss_size);
Packit 6c4009
  sp -= (argc > 6 ? argc - 6 : 0) + 1;
Packit 6c4009
  /* Align stack and make space for trampoline address.  */
Packit 6c4009
  sp = (greg_t *) ((((uintptr_t) sp) & -16L) - 8);
Packit 6c4009
Packit 6c4009
  idx_uc_link = (argc > 6 ? argc - 6 : 0) + 1;
Packit 6c4009
Packit 6c4009
  /* Setup context ucp.  */
Packit 6c4009
  /* Address to jump to.  */
Packit 6c4009
  ucp->uc_mcontext.gregs[REG_RIP] = (uintptr_t) func;
Packit 6c4009
  /* Setup rbx.*/
Packit 6c4009
  ucp->uc_mcontext.gregs[REG_RBX] = (uintptr_t) &sp[idx_uc_link];
Packit 6c4009
  ucp->uc_mcontext.gregs[REG_RSP] = (uintptr_t) sp;
Packit 6c4009
Packit 6c4009
  /* Setup stack.  */
Packit 6c4009
#if SHSTK_ENABLED
Packit 6c4009
  struct pthread *self = THREAD_SELF;
Packit 6c4009
  unsigned int feature_1 = THREAD_GETMEM (self, header.feature_1);
Packit 6c4009
  /* NB: We must check feature_1 before accessing __ssp since caller
Packit 6c4009
	 may be compiled against ucontext_t without __ssp.  */
Packit 6c4009
  if ((feature_1 & X86_FEATURE_1_SHSTK) != 0)
Packit 6c4009
    {
Packit 6c4009
      /* Shadow stack is enabled.  We need to allocate a new shadow
Packit 6c4009
         stack.  */
Packit 6c4009
      unsigned long ssp_size = (((uintptr_t) sp
Packit 6c4009
				 - (uintptr_t) ucp->uc_stack.ss_sp)
Packit 6c4009
				>> STACK_SIZE_TO_SHADOW_STACK_SIZE_SHIFT);
Packit 6c4009
      /* Align shadow stack to 8 bytes.  */
Packit 6c4009
      ssp_size = ALIGN_UP (ssp_size, 8);
Packit 6c4009
Packit 6c4009
      ucp->__ssp[1] = ssp_size;
Packit 6c4009
      ucp->__ssp[2] = ssp_size;
Packit 6c4009
Packit 6c4009
      /* Call __push___start_context to allocate a new shadow stack,
Packit 6c4009
	 push __start_context onto the new stack as well as the new
Packit 6c4009
	 shadow stack.  NB: After __push___start_context returns,
Packit 6c4009
	   ucp->__ssp[0]: The new shadow stack pointer.
Packit 6c4009
	   ucp->__ssp[1]: The base address of the new shadow stack.
Packit 6c4009
	   ucp->__ssp[2]: The size of the new shadow stack.
Packit 6c4009
       */
Packit 6c4009
      __push___start_context (ucp);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
#endif
Packit 6c4009
    sp[0] = (uintptr_t) &__start_context;
Packit 6c4009
  sp[idx_uc_link] = (uintptr_t) ucp->uc_link;
Packit 6c4009
Packit 6c4009
  va_start (ap, argc);
Packit 6c4009
  /* Handle arguments.
Packit 6c4009
Packit 6c4009
     The standard says the parameters must all be int values.  This is
Packit 6c4009
     an historic accident and would be done differently today.  For
Packit 6c4009
     x86-64 all integer values are passed as 64-bit values and
Packit 6c4009
     therefore extending the API to copy 64-bit values instead of
Packit 6c4009
     32-bit ints makes sense.  It does not break existing
Packit 6c4009
     functionality and it does not violate the standard which says
Packit 6c4009
     that passing non-int values means undefined behavior.  */
Packit 6c4009
  for (i = 0; i < argc; ++i)
Packit 6c4009
    switch (i)
Packit 6c4009
      {
Packit 6c4009
      case 0:
Packit 6c4009
	ucp->uc_mcontext.gregs[REG_RDI] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      case 1:
Packit 6c4009
	ucp->uc_mcontext.gregs[REG_RSI] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      case 2:
Packit 6c4009
	ucp->uc_mcontext.gregs[REG_RDX] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      case 3:
Packit 6c4009
	ucp->uc_mcontext.gregs[REG_RCX] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      case 4:
Packit 6c4009
	ucp->uc_mcontext.gregs[REG_R8] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      case 5:
Packit 6c4009
	ucp->uc_mcontext.gregs[REG_R9] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      default:
Packit 6c4009
	/* Put value on stack.  */
Packit 6c4009
	sp[i - 5] = va_arg (ap, greg_t);
Packit 6c4009
	break;
Packit 6c4009
      }
Packit 6c4009
  va_end (ap);
Packit 6c4009
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
weak_alias (__makecontext, makecontext)