makecontext.c revision 272461
177957Sbenno/* 277957Sbenno * Copyright (c) 2001 Daniel M. Eischen <deischen@freebsd.org> 377957Sbenno * All rights reserved. 477957Sbenno * 577957Sbenno * Redistribution and use in source and binary forms, with or without 677957Sbenno * modification, are permitted provided that the following conditions 777957Sbenno * are met: 877957Sbenno * 1. Redistributions of source code must retain the above copyright 977957Sbenno * notice, this list of conditions and the following disclaimer. 1077957Sbenno * 2. Neither the name of the author nor the names of its contributors 1177957Sbenno * may be used to endorse or promote products derived from this software 1277957Sbenno * without specific prior written permission. 1377957Sbenno * 1477957Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1577957Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1677957Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1777957Sbenno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1877957Sbenno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1977957Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2077957Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2177957Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2277957Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2377957Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2477957Sbenno * SUCH DAMAGE. 2577957Sbenno */ 2677957Sbenno 2777957Sbenno#include <sys/cdefs.h> 2877957Sbenno__FBSDID("$FreeBSD: releng/10.1/lib/libc/i386/gen/makecontext.c 138403 2004-12-05 21:22:08Z deischen $"); 2977957Sbenno 3077957Sbenno#include <sys/param.h> 3177957Sbenno#include <sys/signal.h> 3277957Sbenno#include <sys/ucontext.h> 3377957Sbenno 3477957Sbenno#include <errno.h> 3577957Sbenno#include <stdarg.h> 3677957Sbenno#include <stdlib.h> 3777957Sbenno#include <unistd.h> 38176770Sraj 39176770Sraj/* Prototypes */ 4077957Sbennoextern void _ctx_start(ucontext_t *, int argc, ...); 4177957Sbenno 4277957Sbenno 4377957Sbenno__weak_reference(__makecontext, makecontext); 4477957Sbenno 45152310Sgrehanvoid 4677957Sbenno_ctx_done (ucontext_t *ucp) 47152310Sgrehan{ 48152310Sgrehan if (ucp->uc_link == NULL) 4977957Sbenno exit(0); 5090643Sbenno else { 5190643Sbenno /* 5290643Sbenno * Since this context has finished, don't allow it 5390643Sbenno * to be restarted without being reinitialized (via 54152310Sgrehan * setcontext or swapcontext). 55152310Sgrehan */ 56152310Sgrehan ucp->uc_mcontext.mc_len = 0; 57152310Sgrehan 58152310Sgrehan /* Set context to next one in link */ 59152310Sgrehan /* XXX - what to do for error, abort? */ 60152310Sgrehan setcontext((const ucontext_t *)ucp->uc_link); 61152310Sgrehan abort(); /* should never get here */ 62152310Sgrehan } 63152310Sgrehan} 64152310Sgrehan 6577957Sbennovoid 66152310Sgrehan__makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) 67152310Sgrehan{ 68152310Sgrehan va_list ap; 6977957Sbenno char *stack_top; 7077957Sbenno intptr_t *argp; 7177957Sbenno int i; 7277957Sbenno 7377957Sbenno if (ucp == NULL) 7477957Sbenno return; 7577957Sbenno else if ((ucp->uc_stack.ss_sp == NULL) || 7677957Sbenno (ucp->uc_stack.ss_size < MINSIGSTKSZ)) { 7777957Sbenno /* 7877957Sbenno * This should really return -1 with errno set to ENOMEM 7977957Sbenno * or something, but the spec says that makecontext is 8077957Sbenno * a void function. At least make sure that the context 8177957Sbenno * isn't valid so it can't be used without an error. 8277957Sbenno */ 8377957Sbenno ucp->uc_mcontext.mc_len = 0; 8490643Sbenno } 8590643Sbenno /* XXX - Do we want to sanity check argc? */ 8690643Sbenno else if ((argc < 0) || (argc > NCARGS)) { 8790643Sbenno ucp->uc_mcontext.mc_len = 0; 8890643Sbenno } 8990643Sbenno /* Make sure the context is valid. */ 9077957Sbenno else if (ucp->uc_mcontext.mc_len == sizeof(mcontext_t)) { 9196250Sbenno /* 9296250Sbenno * Arrange the stack as follows: 93152310Sgrehan * 94152310Sgrehan * _ctx_start() - context start wrapper 95152310Sgrehan * start() - user start routine 96152310Sgrehan * arg1 - first argument, aligned(16) 97152310Sgrehan * ... 98152310Sgrehan * argn 99152310Sgrehan * ucp - this context, %ebp points here 100152310Sgrehan * 101152310Sgrehan * When the context is started, control will return to 102152310Sgrehan * the context start wrapper which will pop the user 103152310Sgrehan * start routine from the top of the stack. After that, 104152310Sgrehan * the top of the stack will be setup with all arguments 105152310Sgrehan * necessary for calling the start routine. When the 106152310Sgrehan * start routine returns, the context wrapper then sets 107152310Sgrehan * the stack pointer to %ebp which was setup to point to 108152310Sgrehan * the base of the stack (and where ucp is stored). It 109152310Sgrehan * will then call _ctx_done() to swap in the next context 110152310Sgrehan * (uc_link != 0) or exit the program (uc_link == 0). 111152310Sgrehan */ 112152310Sgrehan stack_top = (char *)(ucp->uc_stack.ss_sp + 113152310Sgrehan ucp->uc_stack.ss_size - sizeof(intptr_t)); 114152310Sgrehan 115152310Sgrehan /* 116152310Sgrehan * Adjust top of stack to allow for 3 pointers (return 117152310Sgrehan * address, _ctx_start, and ucp) and argc arguments. 118152310Sgrehan * We allow the arguments to be pointers also. The first 119152310Sgrehan * argument to the user function must be properly aligned. 120152310Sgrehan */ 121152310Sgrehan stack_top = stack_top - (sizeof(intptr_t) * (1 + argc)); 12277957Sbenno stack_top = (char *)((unsigned)stack_top & ~15); 12377957Sbenno stack_top = stack_top - (2 * sizeof(intptr_t)); 124152310Sgrehan argp = (intptr_t *)stack_top; 12577957Sbenno 12677957Sbenno /* 12777957Sbenno * Setup the top of the stack with the user start routine 12877957Sbenno * followed by all of its aguments and the pointer to the 12977957Sbenno * ucontext. We need to leave a spare spot at the top of 13077957Sbenno * the stack because setcontext will move eip to the top 13177957Sbenno * of the stack before returning. 13277957Sbenno */ 13377957Sbenno *argp = (intptr_t)_ctx_start; /* overwritten with same value */ 13477957Sbenno argp++; 13577957Sbenno *argp = (intptr_t)start; 13677957Sbenno argp++; 13777957Sbenno 13877957Sbenno /* Add all the arguments: */ 13977957Sbenno va_start(ap, argc); 14077957Sbenno for (i = 0; i < argc; i++) { 14177957Sbenno *argp = va_arg(ap, intptr_t); 14277957Sbenno argp++; 14377957Sbenno } 14477957Sbenno va_end(ap); 14577957Sbenno 14677957Sbenno /* The ucontext is placed at the bottom of the stack. */ 14777957Sbenno *argp = (intptr_t)ucp; 14877957Sbenno 14977957Sbenno /* 15077957Sbenno * Set the machine context to point to the top of the 15177957Sbenno * stack and the program counter to the context start 15277957Sbenno * wrapper. Note that setcontext() pushes the return 15377957Sbenno * address onto the top of the stack, so allow for this 15477957Sbenno * by adjusting the stack downward 1 slot. Also set 15577957Sbenno * %esi to point to the base of the stack where ucp 15677957Sbenno * is stored. 15777957Sbenno */ 15892842Salfred ucp->uc_mcontext.mc_esi = (int)argp; 15977957Sbenno ucp->uc_mcontext.mc_ebp = 0; 16077957Sbenno ucp->uc_mcontext.mc_esp = (int)stack_top + sizeof(caddr_t); 161176770Sraj ucp->uc_mcontext.mc_eip = (int)_ctx_start; 162176770Sraj } 163176770Sraj} 164176770Sraj