makecontext.c revision 89177
1106716Smarcel/* 2106716Smarcel * Copyright (c) 2001 Daniel M. Eischen <deischen@freebsd.org> 3106716Smarcel * All rights reserved. 4106716Smarcel * 5106716Smarcel * Redistribution and use in source and binary forms, with or without 6106716Smarcel * modification, are permitted provided that the following conditions 7106716Smarcel * are met: 8106716Smarcel * 1. Redistributions of source code must retain the above copyright 9106716Smarcel * notice, this list of conditions and the following disclaimer. 10106716Smarcel * 2. Neither the name of the author nor the names of its contributors 11106716Smarcel * may be used to endorse or promote products derived from this software 12106716Smarcel * without specific prior written permission. 13106716Smarcel * 14106716Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15106716Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16106716Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17106716Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18106716Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19106716Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20106716Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21106716Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22106716Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23106716Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24106716Smarcel * SUCH DAMAGE. 25106716Smarcel */ 26106716Smarcel 27106716Smarcel#include <sys/cdefs.h> 28106716Smarcel__FBSDID("$FreeBSD: head/lib/libc/i386/gen/makecontext.c 89177 2002-01-10 02:40:59Z deischen $"); 29106716Smarcel 30106716Smarcel#include <sys/param.h> 31106716Smarcel#include <sys/signal.h> 32106716Smarcel 33106716Smarcel#include <errno.h> 34106716Smarcel#include <stdarg.h> 35106716Smarcel#include <ucontext.h> 36106716Smarcel#include <unistd.h> 37106716Smarcel 38111176Sru/* Prototypes */ 39106716Smarcelextern void _ctx_start(ucontext_t *, int argc, ...); 40106716Smarcel 41106716Smarcel 42106716Smarcel__weak_reference(__makecontext, makecontext); 43106716Smarcel 44106716Smarcelvoid 45106716Smarcel_ctx_done (ucontext_t *ucp) 46168569Sdelphij{ 47106716Smarcel if (ucp->uc_link == NULL) 48106716Smarcel exit(0); 49106716Smarcel else { 50106716Smarcel /* 51106716Smarcel * Since this context has finished, don't allow it 52106716Smarcel * to be restarted without being reinitialized (via 53106716Smarcel * setcontext or swapcontext). 54106716Smarcel */ 55106716Smarcel ucp->uc_mcontext.mc_flags = 0; 56106716Smarcel 57106716Smarcel /* Set context to next one in link */ 58106716Smarcel /* XXX - what to do for error, abort? */ 59106716Smarcel setcontext((const ucontext_t *)ucp->uc_link); 60106716Smarcel abort(); /* should never get here */ 61106716Smarcel } 62106716Smarcel} 63106716Smarcel 64106716Smarcelvoid 65106716Smarcel__makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) 66106716Smarcel{ 67106716Smarcel va_list ap; 68106716Smarcel char *stack_top; 69106716Smarcel intptr_t *argp; 70106716Smarcel int i; 71106716Smarcel 72106716Smarcel if (ucp == NULL) 73106716Smarcel return; 74106716Smarcel else if ((ucp->uc_stack.ss_sp == NULL) || 75106716Smarcel (ucp->uc_stack.ss_size < MINSIGSTKSZ)) { 76106716Smarcel /* 77106716Smarcel * This should really return -1 with errno set to ENOMEM 78106716Smarcel * or something, but the spec says that makecontext is 79106716Smarcel * a void function. At least make sure that the context 80106716Smarcel * isn't valid so it can't be used without an error. 81106716Smarcel */ 82106716Smarcel ucp->uc_mcontext.mc_flags = 0; 83106716Smarcel } 84106716Smarcel /* XXX - Do we want to sanity check argc? */ 85106716Smarcel else if ((argc < 0) || (argc > NCARGS)) { 86106716Smarcel ucp->uc_mcontext.mc_flags = 0; 87106716Smarcel } 88106716Smarcel /* Make sure the context is valid. */ 89106716Smarcel else if ((ucp->uc_mcontext.mc_flags & __UC_MC_VALID) != 0) { 90106716Smarcel /* 91106716Smarcel * Arrange the stack as follows: 92106716Smarcel * 93106716Smarcel * _ctx_start() - context start wrapper 94106716Smarcel * start() - user start routine 95106716Smarcel * arg1 96106716Smarcel * ... 97106716Smarcel * argn 98106716Smarcel * ucp - this context, %ebp points here 99106716Smarcel * 100106716Smarcel * When the context is started, control will return to 101106716Smarcel * the context start wrapper which will pop the user 102106716Smarcel * start routine from the top of the stack. After that, 103106716Smarcel * the top of the stack will be setup with all arguments 104106716Smarcel * necessary for calling the start routine. When the 105106716Smarcel * start routine returns, the context wrapper then sets 106106716Smarcel * the stack pointer to %ebp which was setup to point to 107106716Smarcel * the base of the stack (and where ucp is stored). It 108106716Smarcel * will then call _ctx_done() to swap in the next context 109106716Smarcel * (uc_link != 0) or exit the program (uc_link == 0). 110106716Smarcel */ 111106716Smarcel stack_top = (char *)(ucp->uc_stack.ss_sp + 112106716Smarcel ucp->uc_stack.ss_size - sizeof(double)); 113106716Smarcel stack_top = (char *)ALIGN(stack_top); 114106716Smarcel 115106716Smarcel /* 116118367Smarcel * Adjust top of stack to allow for 3 pointers (return 117106716Smarcel * address, _ctx_start, and ucp) and argc arguments. 118106716Smarcel * We allow the arguments to be pointers also. 119168569Sdelphij */ 120106716Smarcel stack_top = stack_top - (sizeof(intptr_t) * (3 + argc)); 121106716Smarcel argp = (intptr_t *)stack_top; 122168569Sdelphij 123106716Smarcel /* 124106716Smarcel * Setup the top of the stack with the user start routine 125106716Smarcel * followed by all of its aguments and the pointer to the 126118367Smarcel * ucontext. We need to leave a spare spot at the top of 127106716Smarcel * the stack because setcontext will move eip to the top 128106716Smarcel * of the stack before returning. 129106716Smarcel */ 130106716Smarcel *argp = (intptr_t)_ctx_start; /* overwritten with same value */ 131106716Smarcel argp++; 132106716Smarcel *argp = (intptr_t)start; 133115084Smarcel argp++; 134118367Smarcel 135118367Smarcel /* Add all the arguments: */ 136106716Smarcel va_start(ap, argc); 137106716Smarcel for (i = 0; i < argc; i++) { 138106716Smarcel *argp = va_arg(ap, intptr_t); 139106716Smarcel argp++; 140106716Smarcel } 141106716Smarcel va_end(ap); 142106716Smarcel 143106716Smarcel /* The ucontext is placed at the bottom of the stack. */ 144106716Smarcel *argp = (intptr_t)ucp; 145106716Smarcel 146106716Smarcel /* 147106716Smarcel * Set the machine context to point to the top of the 148106716Smarcel * stack and the program counter to the context start 149168569Sdelphij * wrapper. Note that setcontext() pushes the return 150106716Smarcel * address onto the top of the stack, so allow for this 151106716Smarcel * by adjusting the stack downward 1 slot. Also set 152106716Smarcel * %ebp to point to the base of the stack where ucp 153106716Smarcel * is stored. 154106716Smarcel */ 155106716Smarcel ucp->uc_mcontext.mc_ebp = (int)argp; 156118367Smarcel ucp->uc_mcontext.mc_esp = (int)stack_top + sizeof(caddr_t); 157106716Smarcel ucp->uc_mcontext.mc_eip = (int)_ctx_start; 158106716Smarcel } 159106716Smarcel} 160106716Smarcel