1999-07-27 Mark Kettenis <kettenis@gnu.org>
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / i386 / init-first.c
1 /* Initialization code run first thing by the ELF startup code.  For i386/Hurd.
2    Copyright (C) 1995, 96, 97, 98, 99 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <hurd.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <sysdep.h>
25 #include <set-hooks.h>
26 #include "hurdstartup.h"
27 #include "hurdmalloc.h"         /* XXX */
28
29 extern void __mach_init (void);
30 extern void __libc_init (int, char **, char **);
31 extern void __getopt_clean_environment (char **);
32 extern void __libc_global_ctors (void);
33
34 unsigned int __hurd_threadvar_max;
35 unsigned long int __hurd_threadvar_stack_offset;
36 unsigned long int __hurd_threadvar_stack_mask;
37
38 #ifndef PIC
39 int __libc_enable_secure;
40 #endif
41 int __libc_multiple_libcs = 1;
42
43 extern int __libc_argc;
44 extern char **__libc_argv;
45
46 void *(*_cthread_init_routine) (void); /* Returns new SP to use.  */
47 void (*_cthread_exit_routine) (int status) __attribute__ ((__noreturn__));
48
49 /* Things that want to be run before _hurd_init or much anything else.
50    Importantly, these are called before anything tries to use malloc.  */
51 DEFINE_HOOK (_hurd_preinit_hook, (void));
52
53
54 /* We call this once the Hurd magic is all set up and we are ready to be a
55    Posixoid program.  This does the same things the generic version does.  */
56 static void internal_function
57 posixland_init (int argc, char **argv)
58 {
59   __libc_init (argc, argv, __environ);
60
61   /* This is a hack to make the special getopt in GNU libc working.  */
62   __getopt_clean_environment (__environ);
63
64 #ifdef PIC
65   __libc_global_ctors ();
66 #endif
67 }
68
69
70 static void
71 init1 (int argc, char *arg0, ...)
72 {
73   char **argv = &arg0;
74   char **envp = &argv[argc + 1];
75   struct hurd_startup_data *d;
76
77   __libc_argc = argc;
78   __libc_argv = argv;
79   __environ = envp;
80
81   while (*envp)
82     ++envp;
83   d = (void *) ++envp;
84
85   /* If we are the bootstrap task started by the kernel,
86      then after the environment pointers there is no Hurd
87      data block; the argument strings start there.  */
88   if ((void *) d != argv[0])
89     {
90       _hurd_init_dtable = d->dtable;
91       _hurd_init_dtablesize = d->dtablesize;
92
93       {
94         /* Check if the stack we are now on is different from
95            the one described by _hurd_stack_{base,size}.  */
96
97         char dummy;
98         const vm_address_t newsp = (vm_address_t) &dummy;
99
100         if (d->stack_size != 0 && (newsp < d->stack_base ||
101                                    newsp - d->stack_base > d->stack_size))
102           /* The new stack pointer does not intersect with the
103              stack the exec server set up for us, so free that stack.  */
104           __vm_deallocate (__mach_task_self (), d->stack_base, d->stack_size);
105       }
106     }
107
108   if ((void *) d != argv[0] && (d->portarray || d->intarray))
109     /* Initialize library data structures, start signal processing, etc.  */
110     _hurd_init (d->flags, argv,
111                 d->portarray, d->portarraysize,
112                 d->intarray, d->intarraysize);
113
114 #ifndef PIC
115   __libc_enable_secure = d->flags & EXEC_SECURE;
116 #else
117   posixland_init(argc, argv);
118 #endif
119 }
120
121
122 static inline void
123 init (int *data)
124 {
125   int argc = *data;
126   char **argv = (void *) (data + 1);
127   char **envp = &argv[argc + 1];
128   struct hurd_startup_data *d;
129   unsigned long int threadvars[_HURD_THREADVAR_MAX];
130
131   /* Provide temporary storage for thread-specific variables on the startup
132      stack so the cthreads initialization code can use them for malloc et al,
133   or so we can use malloc below for the real threadvars array.  */
134   memset (threadvars, 0, sizeof threadvars);
135   __hurd_threadvar_stack_offset = (unsigned long int) threadvars;
136
137   __environ = envp;
138   while (*envp)
139     ++envp;
140   d = (void *) ++envp;
141
142   /* The user might have defined a value for this, to get more variables.
143      Otherwise it will be zero on startup.  We must make sure it is set
144      properly before before cthreads initialization, so cthreads can know
145      how much space to leave for thread variables.  */
146   if (__hurd_threadvar_max < _HURD_THREADVAR_MAX)
147     __hurd_threadvar_max = _HURD_THREADVAR_MAX;
148
149
150   /* After possibly switching stacks, call `init1' (above) with the user
151      code as the return address, and the argument data immediately above
152      that on the stack.  */
153
154   if (_cthread_init_routine)
155     {
156       /* Initialize cthreads, which will allocate us a new stack to run on.  */
157       void *newsp = (*_cthread_init_routine) ();
158       struct hurd_startup_data *od;
159
160       void switch_stacks (void);
161
162       /* Copy per-thread variables from that temporary
163          area onto the new cthread stack.  */
164       memcpy (__hurd_threadvar_location_from_sp (0, newsp),
165               threadvars, sizeof threadvars);
166
167       /* Copy the argdata from the old stack to the new one.  */
168       newsp = memcpy (newsp - ((char *) &d[1] - (char *) data), data,
169                       (char *) d - (char *) data);
170
171       /* Set up the Hurd startup data block immediately following
172          the argument and environment pointers on the new stack.  */
173       od = (newsp + ((char *) d - (char *) data));
174       if ((void *) argv[0] == d)
175         /* We were started up by the kernel with arguments on the stack.
176            There is no Hurd startup data, so zero the block.  */
177         memset (od, 0, sizeof *od);
178       else
179         /* Copy the Hurd startup data block to the new stack.  */
180         *od = *d;
181
182       /* Push the user code address on the top of the new stack.  It will
183          be the return address for `init1'; we will jump there with NEWSP
184          as the stack pointer.  */
185       *--(int *) newsp = data[-1];
186       ((void **) data)[-1] = switch_stacks;
187       /* Force NEWSP into %ecx and &init1 into %eax, which are not restored
188          by function return.  */
189       asm volatile ("# a %0 c %1" : : "a" (newsp), "c" (&init1));
190     }
191   else
192     {
193       /* We are not using cthreads, so we will have just a single allocated
194          area for the per-thread variables of the main user thread.  */
195       unsigned long int *array;
196       unsigned int i;
197       int usercode;
198
199       void call_init1 (void);
200
201       array = malloc (__hurd_threadvar_max * sizeof (unsigned long int));
202       if (array == NULL)
203         __libc_fatal ("Can't allocate single-threaded thread variables.");
204
205       /* Copy per-thread variables from the temporary array into the
206          newly malloc'd space.  */
207       memcpy (array, threadvars, sizeof threadvars);
208       __hurd_threadvar_stack_offset = (unsigned long int) array;
209       for (i = _HURD_THREADVAR_MAX; i < __hurd_threadvar_max; ++i)
210         array[i] = 0;
211
212       /* The argument data is just above the stack frame we will unwind by
213          returning.  Mutate our own return address to run the code below.  */
214       usercode = data[-1];
215       ((void **) data)[-1] = call_init1;
216       /* Force USERCODE into %eax and &init1 into %ecx, which are not
217          restored by function return.  */
218       asm volatile ("# a %0 c %1" : : "a" (usercode), "c" (&init1));
219     }
220 }
221
222 /* These bits of inline assembler used to be located inside `init'.
223    However they were optimized away by gcc 2.95.  */
224
225 /* The return address of `init' above, was redirected to here, so at
226    this point our stack is unwound and callers' registers restored.
227    Only %ecx and %eax are call-clobbered and thus still have the
228    values we set just above.  Fetch from there the new stack pointer
229    we will run on, and jmp to the run-time address of `init1'; when it
230    returns, it will run the user code with the argument data at the
231    top of the stack.  */
232 asm ("
233  switch_stacks:
234   movl %eax, %esp
235   jmp *%ecx
236 ");
237
238 /* As in the stack-switching case, at this point our stack is unwound
239    and callers' registers restored, and only %ecx and %eax communicate
240    values from the lines above.  In this case we have stashed in %eax
241    the user code return address.  Push it on the top of the stack so
242    it acts as init1's return address, and then jump there.  */
243 asm ("
244   call_init1:
245   push %eax
246   jmp *%ecx
247 ");
248
249
250 #ifdef PIC
251 /* This function is called to initialize the shared C library.
252    It is called just before the user _start code from i386/elf/start.S,
253    with the stack set up as that code gets it.  */
254
255 /* NOTE!  The linker notices the magical name `_init' and sets the DT_INIT
256    pointer in the dynamic section based solely on that.  It is convention
257    for this function to be in the `.init' section, but the symbol name is
258    the only thing that really matters!!  */
259 void
260 _init (int argc, ...)
261 {
262   /* Initialize data structures so we can do RPCs.  */
263   __mach_init ();
264
265   RUN_HOOK (_hurd_preinit_hook, ());
266
267   init (&argc);
268 }
269 #endif
270
271
272 void
273 __libc_init_first (int argc, char **argv, char **envp)
274 #ifdef PIC
275 {
276   /* Everything was done in the shared library initializer, _init.  */
277 }
278 #else
279 {
280   posixland_init(argc, argv);
281 }
282
283 /* XXX This is all a crock and I am not happy with it.
284    This poorly-named function is called by static-start.S,
285    which should not exist at all.  */
286 void
287 _hurd_stack_setup (int argc __attribute__ ((unused)), ...)
288 {
289   void doinit (int *data)
290     {
291       /* This function gets called with the argument data at TOS.  */
292       void doinit1 (int argc, ...)
293         {
294           init (&argc);
295         }
296
297       /* Push the user return address after the argument data, and then
298          jump to `doinit1' (above), so it is as if __libc_init_first's
299          caller had called `doinit1' with the argument data already on the
300       stack.  */
301       *--data = (&argc)[-1];
302       asm volatile ("movl %0, %%esp\n" /* Switch to new outermost stack.  */
303                     "movl $0, %%ebp\n" /* Clear outermost frame pointer.  */
304                     "jmp *%1" : : "r" (data), "r" (&doinit1));
305       /* NOTREACHED */
306     }
307
308   /* Initialize data structures so we can do RPCs.  */
309   __mach_init ();
310
311   RUN_HOOK (_hurd_preinit_hook, ());
312
313   _hurd_startup ((void **) &argc, &doinit);
314 }
315 #endif
316
317
318 /* This function is defined here so that if this file ever gets into
319    ld.so we will get a link error.  Having this file silently included
320    in ld.so causes disaster, because the _init definition above will
321    cause ld.so to gain an init function, which is not a cool thing. */
322
323 void
324 _dl_start (void)
325 {
326   abort ();
327 }