Formerly mach/hurd/start.c.~33~
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / start.c
1 /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
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.
8
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.
13
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.  */
18
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <hurd.h>
23 #include <hurd/exec.h>
24 #include <sysdep.h>
25
26 /* The first piece of initialized data.  */
27 int __data_start = 0;
28
29 struct _hurd_port *_hurd_ports;
30 mode_t _hurd_umask;
31
32 mach_port_t *_hurd_init_dtable;
33 mach_msg_type_number_t _hurd_init_dtablesize;
34
35 vm_address_t _hurd_stack_base;
36 vm_size_t _hurd_stack_size;
37
38 volatile int errno;             /* XXX wants to be per-thread */
39
40 char **__environ;
41
42 extern void __mach_init (void);
43 extern void __libc_init (int argc, char **argv, char **envp);
44 extern int main (int argc, char **argv, char **envp);
45
46 void *(*_cthread_init_routine) (void); /* Returns new SP to use.  */
47 __NORETURN void (*_cthread_exit_routine) (int status);
48
49 static int split_args (char *, size_t, char **);
50
51
52 /* These communicate values from _start to start1,
53    where we cannot use the stack for anything.  */
54 static char *args, *env;
55 static mach_port_t *portarray;
56 static int *intarray;
57 static mach_msg_type_number_t argslen, envlen, portarraysize, intarraysize;
58 static int flags;
59 static char **argv, **envp;
60 static int argc;
61
62
63 static volatile void
64 start1 (void)
65 {
66   register int envc = 0;
67
68   {
69     /* Check if the stack we are now on is different from
70        the one described by _hurd_stack_{base,size}.  */
71
72     char dummy;
73     const vm_address_t newsp = (vm_address_t) &dummy;
74
75     if (_hurd_stack_size != 0 && (newsp < _hurd_stack_base ||
76                                   newsp - _hurd_stack_base > _hurd_stack_size))
77       /* The new stack pointer does not intersect with the
78          stack the exec server set up for us, so free that stack.  */
79       __vm_deallocate (__mach_task_self (),
80                        _hurd_stack_base, _hurd_stack_size);
81   }
82
83
84   /* Turn the block of null-separated strings we were passed for the
85      arguments and environment into vectors of pointers to strings.  */
86       
87   if (! argv)
88     {
89       if (! args)
90         {
91           /* No arguments passed; set argv to { NULL }.  */
92           argc = 0;
93           argv = (char **) &args;
94         }
95       else
96         argc = split_args (args, argslen, NULL);
97     }
98
99   if (! envp)
100     {
101       if (! env)
102         /* No environment passed; set __environ to { NULL }.  */
103         envp = (char **) &env;
104       else
105         envc = split_args (env, envlen, NULL);
106     }
107
108   if (! argv && ! envp && argc + envc > 0)
109     {
110       /* There were some arguments or environment.
111          Allocate space for the vectors of pointers and fill them in.  */
112
113       argv = __alloca ((argc + 1) * sizeof (char *));
114       envp = __alloca ((envc + 1) * sizeof (char *));
115       
116       split_args (args, argslen, argv);
117       split_args (env, envlen, envp);
118     }
119
120   if (portarray || intarray)
121     /* Initialize library data structures, start signal processing, etc.  */
122     _hurd_init (flags, argv, portarray, portarraysize, intarray, intarraysize);
123
124
125   /* Random library initialization.  */
126   __libc_init (argc, argv, __environ);
127
128
129   /* Finally, run the user program.  */
130   (_cthread_exit_routine != NULL ? *_cthread_exit_routine : exit)
131     (main (argc, argv, __environ));
132
133   /* Should never get here.  */
134   LOSE;
135 }
136
137 static int
138 split_args (char *args, size_t argslen, char **argv)
139 {
140   char *p = args;
141   size_t n = argslen;
142   int argc = 0;
143
144   while (n > 0)
145     {
146       char *end = memchr (p, '\0', n);
147
148       if (argv)
149         argv[argc] = p;
150       ++argc;
151
152       if (end == NULL)
153         /* The last argument is unterminated.  */
154         break;
155
156       n -= end + 1 - p;
157       p = end + 1;
158     }
159
160   if (argv)
161     argv[argc] = NULL;
162   return argc;
163 }
164
165
166 /* Entry point.  The exec server started the initial thread in our task with
167    this spot the PC, and a stack that is presumably big enough.  We do basic
168    Mach initialization so mig-generated stubs work, and then do an exec_startup
169    RPC on our bootstrap port, to which the exec server responds with the
170    information passed in the exec call, as well as our original bootstrap port,
171    and the base address and size of the preallocated stack.
172
173    If using cthreads, we are given a new stack by cthreads initialization and
174    deallocate the stack set up by the exec server.  On the new stack we call
175    `start1' (above) to do the rest of the startup work.  Since the stack may
176    disappear out from under us in a machine-dependent way, we use a pile of
177    static variables to communicate the information from exec_startup to start1.
178    This is unfortunate but preferable to machine-dependent frobnication to copy
179    the state from the old stack to the new one.  */
180
181 void
182 _start (void)
183 {
184   error_t err;
185   mach_port_t in_bootstrap;
186   vm_address_t stack_pointer, stack_base;
187   vm_size_t stack_size;
188
189   /* GET_SP (SP) should put the stack pointer in SP.  */
190
191 #ifndef GET_SP
192 #error GET_SP not defined by sysdeps/mach/hurd/MACHINE/sysdep.h
193 #endif
194   GET_SP (stack_pointer);
195
196   /* Basic Mach initialization, must be done before RPCs can be done.  */
197   __mach_init ();
198
199   if (err = __task_get_special_port (__mach_task_self (), TASK_BOOTSTRAP_PORT,
200                                      &in_bootstrap))
201     LOSE;
202
203   if (in_bootstrap != MACH_PORT_NULL)
204     {
205       /* Call the exec server on our bootstrap port and
206          get all our standard information from it.  */
207
208       argslen = envlen = 0;
209       _hurd_init_dtablesize = portarraysize = intarraysize = 0;
210
211       err = __exec_startup (in_bootstrap,
212                             &stack_base, &stack_size,
213                             &flags,
214                             &args, &argslen, &env, &envlen,
215                             &_hurd_init_dtable, &_hurd_init_dtablesize,
216                             &portarray, &portarraysize,
217                             &intarray, &intarraysize);
218       __mach_port_deallocate (__mach_task_self (), in_bootstrap);
219     }
220
221   if (err || in_bootstrap == MACH_PORT_NULL)
222     {
223       /* Either we have no bootstrap port, or the RPC to the exec server
224          failed.  Try to snarf the args in the canonical Mach way.
225          Hopefully either they will be on the stack as expected, or the
226          stack will be zeros so we don't crash.  Set all our other
227          variables to have empty information.  */
228
229       /* SNARF_ARGS (ARGC, ARGV, ENVP) snarfs the arguments and environment
230          from the stack, assuming they were put there by the microkernel.  */
231       SNARF_ARGS (argc, argv, envp);
232
233       flags = 0;
234       args = env = NULL;
235       argslen = envlen = 0;
236       _hurd_init_dtable = NULL;
237       _hurd_init_dtablesize = 0;
238       portarray = NULL;
239       portarraysize = 0;
240       intarray = NULL;
241       intarraysize = 0;
242     }
243   else
244     argv = envp = NULL;
245
246
247   /* Do cthreads initialization and switch to the cthread stack.  */
248
249   if (_cthread_init_routine != NULL)
250     CALL_WITH_SP (start1, (*_cthread_init_routine) ());
251   else
252     start1 ();
253
254   /* Should never get here.  */
255   LOSE;
256 }