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