Add to git.
[pthrlib.git] / src / pthr_context.c
1 /* Very Linux-specific context switching.
2  * Written by RWMJ with lots of help from GNU pth library.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: pthr_context.c,v 1.7 2003/02/25 17:07:23 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "pthr_context.h"
27
28 #ifdef HAVE_WORKING_SETCONTEXT
29
30 void
31 mctx_set (mctx_t *mctx,
32           void (*sf_addr) (void *), void *sf_arg,
33           void *sk_addr, int sk_size)
34 {
35   /* Fetch current context. */
36   getcontext (&mctx->uc);
37
38   /* Update to point to new stack. */
39   mctx->uc.uc_link = 0;
40   /* XXX I cheated and used code from pth. We should do proper
41    * automatic detection here instead.
42    */
43 #if defined(__i386__)
44   mctx->uc.uc_stack.ss_sp = sk_addr + sk_size - 4;
45   /* mctx->uc.uc_stack.ss_size = sk_size; */
46 #elif defined(__sparc__)
47   mctx->uc.uc_stack.ss_sp = sk_addr + sk_size - 8;
48   mctx->uc.uc_stack.ss_size = sk_size - 8;
49 #else
50 #error "Unsupported architecture"
51 #endif
52   mctx->uc.uc_stack.ss_flags = 0;
53
54   /* Make new context. */
55   makecontext (&mctx->uc, sf_addr, 1, sf_arg);
56 }
57
58 unsigned long
59 mctx_get_PC (mctx_t *mctx)
60 {
61 #if defined(__i386__) || defined(__i386)
62   return (unsigned long) mctx->uc.uc_mcontext.eip;
63 #elif defined(__sparc)
64   return (unsigned long) mctx->uc.uc_mcontext.gregs[REG_PC];
65 #else
66   return 0;                     /* This is a non-essential function. */
67 #endif
68 }
69
70 unsigned long
71 mctx_get_SP (mctx_t *mctx)
72 {
73   return (unsigned long) mctx->uc.uc_stack.ss_sp;
74 }
75
76 #else /* setjmp implementation */
77
78 #ifdef linux
79
80 #  if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(JB_PC) && defined(JB_SP)
81 #    define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[JB_PC] = (int) v
82 #    define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[JB_SP] = (int) v
83 #    define GET_PC(mctx) mctx->jb[0].__jmpbuf[JB_PC]
84 #    define GET_SP(mctx) mctx->jb[0].__jmpbuf[JB_SP]
85 #  elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(__mc68000__)
86 #    define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[0].__aregs[0] = (long int) v
87 #    define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[0].__sp = (int *) v
88 #    define GET_PC(mctx) mctx->jb[0].__jmpbuf[0].__aregs[0]
89 #    define GET_SP(mctx) mctx->jb[0].__jmpbuf[0].__sp
90 #  elif defined(__GNU_LIBRARY__) && defined(__i386__)
91 #    define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[0].__pc = (char *) v
92 #    define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[0].__sp = (void *) v
93 #    define GET_PC(mctx) mctx->jb[0].__jmpbuf[0].__pc
94 #    define GET_SP(mctx) mctx->jb[0].__jmpbuf[0].__sp
95 #  else
96 #    error "Unsupported Linux (g)libc version and/or platform"
97 #  endif
98
99 #elif defined(__OpenBSD__) && defined(__i386__)
100
101 #  define GET_PC(mctx) mctx->jb[0]
102 #  define GET_SP(mctx) mctx->jb[2]
103 #  define SET_PC(mctx,v) GET_PC(mctx) = (long) v
104 #  define SET_SP(mctx,v) GET_SP(mctx) = (long) v
105
106 #elif defined(__FreeBSD__) && defined(__i386__)
107
108 #  define GET_PC(mctx) mctx->jb[0]._jb[0]
109 #  define GET_SP(mctx) mctx->jb[0]._jb[2]
110 #  define SET_PC(mctx,v) GET_PC(mctx) = (int) v
111 #  define SET_SP(mctx,v) GET_SP(mctx) = (int) v
112
113 #else
114 #error "setjmp implementation has not been ported to your OS or architecture - please contact the maintainer"
115 #endif
116
117 void
118 mctx_set (mctx_t *mctx,
119           void (*sf_addr) (void *), void *sf_arg,
120           void *sk_addr, int sk_size)
121 {
122   int *stack_ptr = (int *) (sk_addr + sk_size);
123
124   /* Prevent gcc from allocating these variables in registers, to avoid
125    * a warning about variables being clobbered by setjmp/longjmp.
126    */
127   (void) &sk_addr;
128   (void) &stack_ptr;
129
130 #ifdef __i386__
131   /* Push function argument onto the stack. */
132   *--stack_ptr = (int) sf_arg;
133   /* Push return address onto the stack. */
134   *--stack_ptr = 0xfafafafa;
135 #if defined(__OpenBSD__) || defined(__FreeBSD__)
136   /* OpenBSD needs an extra word here for what? Frame pointer? */
137   *--stack_ptr = 0;
138 #endif
139 #else
140 #error "unsupported machine architecture"
141 #endif
142
143   mctx_save (mctx);
144   SET_PC (mctx, sf_addr);
145   SET_SP (mctx, stack_ptr);
146 }
147
148 unsigned long
149 mctx_get_PC (mctx_t *mctx)
150 {
151   return (unsigned) GET_PC (mctx);
152 }
153
154 unsigned long
155 mctx_get_SP (mctx_t *mctx)
156 {
157   return (unsigned long) GET_SP (mctx);
158 }
159
160 #endif /* ! USE_UCONTEXT */