initial import
[glibc.git] / sysdeps / mach / hurd / start.c
blob91be7eaefed3219ca9e4f49b5dc72e766656e192
1 /* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C 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.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA. */
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <hurd.h>
24 #include <hurd/exec.h>
25 #include <sysdep.h>
26 #include <hurd/threadvar.h>
27 #include "set-hooks.h"
28 #include "hurdmalloc.h" /* XXX */
30 /* The first piece of initialized data. */
31 int __data_start = 0;
32 weak_alias (__data_start, data_start)
34 mach_port_t *_hurd_init_dtable;
35 mach_msg_type_number_t _hurd_init_dtablesize;
37 unsigned int __hurd_threadvar_max;
38 unsigned long int __hurd_threadvar_stack_mask;
39 unsigned long int __hurd_threadvar_stack_offset;
41 /* These are set up by _hurdsig_init. */
42 unsigned long int __hurd_sigthread_stack_base;
43 unsigned long int __hurd_sigthread_stack_end;
44 unsigned long int *__hurd_sigthread_variables;
46 vm_address_t _hurd_stack_base;
47 vm_size_t _hurd_stack_size;
49 char **__environ;
50 weak_alias (__environ, environ)
52 /* Things that want to be run before _hurd_init or much anything else.
53 Importantly, these are called before anything tries to use malloc. */
54 DEFINE_HOOK (_hurd_preinit_hook, (void));
56 extern void __mach_init (void);
57 extern void __libc_init (int argc, char **argv, char **envp);
58 extern int main (int argc, char **argv, char **envp);
60 void *(*_cthread_init_routine) (void); /* Returns new SP to use. */
61 void (*_cthread_exit_routine) (int status) __attribute__ ((__noreturn__));
63 int _hurd_split_args (char *, size_t, char **);
65 /* These communicate values from _start to start1,
66 where we cannot use the stack for anything. */
67 static char *args, *env;
68 static mach_port_t *portarray;
69 static int *intarray;
70 static mach_msg_type_number_t argslen, envlen, portarraysize, intarraysize;
71 static int flags;
72 static char **argv, **envp;
73 static int argc;
76 static void start1 (void) __attribute__ ((__noreturn__));
79 /* Entry point. This is the first thing in the text segment.
81 The exec server started the initial thread in our task with this spot the
82 PC, and a stack that is presumably big enough. We do basic Mach
83 initialization so mig-generated stubs work, and then do an exec_startup
84 RPC on our bootstrap port, to which the exec server responds with the
85 information passed in the exec call, as well as our original bootstrap
86 port, and the base address and size of the preallocated stack.
88 If using cthreads, we are given a new stack by cthreads initialization and
89 deallocate the stack set up by the exec server. On the new stack we call
90 `start1' (above) to do the rest of the startup work. Since the stack may
91 disappear out from under us in a machine-dependent way, we use a pile of
92 static variables to communicate the information from exec_startup to start1.
93 This is unfortunate but preferable to machine-dependent frobnication to copy
94 the state from the old stack to the new one. */
96 #ifndef START_ARGS
97 #define START_ARGS void
98 #endif
99 #ifdef START_MACHDEP
100 START_MACHDEP
101 #define _start _start0
102 #endif
104 void
105 _start (START_ARGS)
107 error_t err;
108 mach_port_t in_bootstrap;
110 /* Basic Mach initialization, must be done before RPCs can be done. */
111 __mach_init ();
113 /* Run things that want to do initialization as soon as possible. We do
114 this before exec_startup so that no out of line data arrives and
115 clutters up the address space before brk initialization. */
117 RUN_HOOK (_hurd_preinit_hook, ());
119 if (err = __task_get_special_port (__mach_task_self (), TASK_BOOTSTRAP_PORT,
120 &in_bootstrap))
121 LOSE;
123 if (in_bootstrap != MACH_PORT_NULL)
125 /* Call the exec server on our bootstrap port and
126 get all our standard information from it. */
128 argslen = envlen = 0;
129 _hurd_init_dtablesize = portarraysize = intarraysize = 0;
131 err = __exec_startup (in_bootstrap,
132 &_hurd_stack_base, &_hurd_stack_size,
133 &flags,
134 &args, &argslen, &env, &envlen,
135 &_hurd_init_dtable, &_hurd_init_dtablesize,
136 &portarray, &portarraysize,
137 &intarray, &intarraysize);
138 __mach_port_deallocate (__mach_task_self (), in_bootstrap);
141 if (err || in_bootstrap == MACH_PORT_NULL)
143 /* Either we have no bootstrap port, or the RPC to the exec server
144 failed. Try to snarf the args in the canonical Mach way.
145 Hopefully either they will be on the stack as expected, or the
146 stack will be zeros so we don't crash. Set all our other
147 variables to have empty information. */
149 /* SNARF_ARGS (ARGC, ARGV, ENVP) snarfs the arguments and environment
150 from the stack, assuming they were put there by the microkernel. */
151 SNARF_ARGS (argc, argv, envp);
153 flags = 0;
154 args = env = NULL;
155 argslen = envlen = 0;
156 _hurd_init_dtable = NULL;
157 _hurd_init_dtablesize = 0;
158 portarray = NULL;
159 portarraysize = 0;
160 intarray = NULL;
161 intarraysize = 0;
163 else
164 argv = envp = NULL;
167 /* The user might have defined a value for this, to get more variables.
168 Otherwise it will be zero on startup. We must make sure it is set
169 properly before before cthreads initialization, so cthreads can know
170 how much space to leave for thread variables. */
171 if (__hurd_threadvar_max < _HURD_THREADVAR_MAX)
172 __hurd_threadvar_max = _HURD_THREADVAR_MAX;
174 /* Do cthreads initialization and switch to the cthread stack. */
176 if (_cthread_init_routine != NULL)
177 CALL_WITH_SP (start1, (*_cthread_init_routine) ());
178 else
179 start1 ();
181 /* Should never get here. */
182 LOSE;
186 static void
187 start1 (void)
189 register int envc = 0;
192 /* Check if the stack we are now on is different from
193 the one described by _hurd_stack_{base,size}. */
195 char dummy;
196 const vm_address_t newsp = (vm_address_t) &dummy;
198 if (_hurd_stack_size != 0 && (newsp < _hurd_stack_base ||
199 newsp - _hurd_stack_base > _hurd_stack_size))
200 /* The new stack pointer does not intersect with the
201 stack the exec server set up for us, so free that stack. */
202 __vm_deallocate (__mach_task_self (),
203 _hurd_stack_base, _hurd_stack_size);
206 if (__hurd_threadvar_stack_mask == 0)
208 /* We are not using cthreads, so we will have just a single allocated
209 area for the per-thread variables of the main user thread. */
210 unsigned long int i;
211 __hurd_threadvar_stack_offset
212 = (unsigned long int) malloc (__hurd_threadvar_max *
213 sizeof (unsigned long int));
214 if (__hurd_threadvar_stack_offset == 0)
215 __libc_fatal ("Can't allocate single-threaded per-thread variables.");
216 for (i = 0; i < __hurd_threadvar_max; ++i)
217 ((unsigned long int *) __hurd_threadvar_stack_offset)[i] = 0;
221 /* Turn the block of null-separated strings we were passed for the
222 arguments and environment into vectors of pointers to strings. */
224 if (! argv)
226 if (args)
227 /* Count up the arguments so we can allocate ARGV. */
228 argc = _hurd_split_args (args, argslen, NULL);
229 if (! args || argc == 0)
231 /* No arguments passed; set argv to { NULL }. */
232 argc = 0;
233 args = NULL;
234 argv = (char **) &args;
238 if (! envp)
240 if (env)
241 /* Count up the environment variables so we can allocate ENVP. */
242 envc = _hurd_split_args (env, envlen, NULL);
243 if (! env || envc == 0)
245 /* No environment passed; set __environ to { NULL }. */
246 env = NULL;
247 envp = (char **) &env;
251 if (! argv)
253 /* There were some arguments.
254 Allocate space for the vectors of pointers and fill them in. */
255 argv = __alloca ((argc + 1) * sizeof (char *));
256 _hurd_split_args (args, argslen, argv);
259 if (! envp)
261 /* There was some environment.
262 Allocate space for the vectors of pointers and fill them in. */
263 envp = __alloca ((envc + 1) * sizeof (char *));
264 _hurd_split_args (env, envlen, envp);
267 __environ = envp;
269 if (portarray || intarray)
270 /* Initialize library data structures, start signal processing, etc. */
271 _hurd_init (flags, argv, portarray, portarraysize, intarray, intarraysize);
273 /* Random library initialization. These functions may assume that
274 _hurd_init has already run (if it is going to), and POSIX.1 facilities
275 are initialized and available. */
276 __libc_init (argc, argv, __environ);
278 /* Finally, run the user program. */
279 (_cthread_exit_routine != NULL ? *_cthread_exit_routine : exit)
280 (main (argc, argv, __environ));
282 /* Should never get here. */
283 LOSE;
286 /* Split ARGSLEN bytes at ARGS into words, breaking at NUL characters. If
287 ARGV is not a null pointer, store a pointer to the start of each word in
288 ARGV[n], and null-terminate ARGV. Return the number of words split. */
291 _hurd_split_args (char *args, size_t argslen, char **argv)
293 char *p = args;
294 size_t n = argslen;
295 int argc = 0;
297 while (n > 0)
299 char *end = memchr (p, '\0', n);
301 if (argv)
302 argv[argc] = p;
303 ++argc;
305 if (end == NULL)
306 /* The last argument is unterminated. */
307 break;
309 n -= end + 1 - p;
310 p = end + 1;
313 if (argv)
314 argv[argc] = NULL;
315 return argc;