ec53e2f36e612d7d8f6fb29f5067821fd7acd2c4
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / dl-sysdep.c
1 /* Operating system support for run-time dynamic linker.  Hurd version.
2 Copyright (C) 1995, 1996 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
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA.  */
19
20 #include <hurd.h>
21 #include <link.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <sys/mman.h>
26 #include <sys/wait.h>
27 #include <assert.h>
28 #include <sysdep.h>
29 #include <mach/mig_support.h>
30 #include "hurdstartup.h"
31 #include <mach/host_info.h>
32 #include "../stdio-common/_itoa.h"
33 #include <hurd/auth.h>
34 #include <hurd/term.h>
35 #include <stdarg.h>
36 #include <ctype.h>
37
38 #include "dl-machine.h"
39
40 extern void __mach_init (void);
41
42 extern int _dl_argc;
43 extern char **_dl_argv;
44 extern char **_environ;
45
46 struct hurd_startup_data *_dl_hurd_data;
47
48 unsigned int __hurd_threadvar_max = _HURD_THREADVAR_MAX;
49 static unsigned long int threadvars[_HURD_THREADVAR_MAX];
50 unsigned long int __hurd_threadvar_stack_offset
51   = (unsigned long int) &threadvars;
52 unsigned long int __hurd_sigthread_stack_base;
53 unsigned long int __hurd_sigthread_stack_end;
54 unsigned long int *__hurd_sigthread_variables;
55 unsigned long int __hurd_threadvar_stack_mask;
56
57
58 /* XXX loser kludge for vm_map kernel bug */
59 static vm_address_t fmha;
60 static vm_size_t fmhs;
61 static void unfmh(void){
62 __vm_deallocate(__mach_task_self(),fmha,fmhs);}
63 static void fmh(void) {
64     error_t err;int x;mach_port_t p;
65     vm_address_t a=0x08000000U,max=VM_MAX_ADDRESS;
66     while (!(err=__vm_region(__mach_task_self(),&a,&fmhs,&x,&x,&x,&x,&p,&x))){
67       __mach_port_deallocate(__mach_task_self(),p);
68       if (a+fmhs>=0x80000000U){
69         max=a; break;}
70       fmha=a+=fmhs;}
71     if (err) assert(err==KERN_NO_SPACE);
72     if (!fmha)fmhs=0;else{
73     fmhs=max-fmha;
74     err = __vm_map (__mach_task_self (),
75                     &fmha, fmhs, 0, 0, MACH_PORT_NULL, 0, 1,
76                     VM_PROT_NONE, VM_PROT_NONE, VM_INHERIT_COPY);
77     assert_perror(err);}
78   }
79 /* XXX loser kludge for vm_map kernel bug */
80
81
82
83 Elf32_Addr
84 _dl_sysdep_start (void **start_argptr,
85                   void (*dl_main) (const Elf32_Phdr *phdr, Elf32_Word phent,
86                                    Elf32_Addr *user_entry))
87 {
88   extern void _start ();
89
90   void go (int *argdata)
91     {
92       extern unsigned int _dl_skip_args; /* rtld.c */
93       char **p;
94
95       /* Cache the information in various global variables.  */
96       _dl_argc = *argdata;
97       _dl_argv = 1 + (char **) argdata;
98       _environ = &_dl_argv[_dl_argc + 1];
99       for (p = _environ; *p++;); /* Skip environ pointers and terminator.  */
100
101       if ((void *) p == _dl_argv[0])
102         {
103           static struct hurd_startup_data nodata;
104           _dl_hurd_data = &nodata;
105           nodata.user_entry = (vm_address_t) &_start;
106         }
107       else
108         _dl_hurd_data = (void *) p;
109
110       _dl_secure = _dl_hurd_data->flags & EXEC_SECURE;
111
112       if (_dl_hurd_data->flags & EXEC_STACK_ARGS &&
113           _dl_hurd_data->user_entry == 0)
114         _dl_hurd_data->user_entry = (vm_address_t) &_start;
115
116 unfmh();                        /* XXX */
117
118       if (_dl_hurd_data->user_entry == (vm_address_t) &_start)
119         /* We were invoked as a command, not as the program interpreter.
120            The generic ld.so code supports this: it will parse the args
121            as "ld.so PROGRAM [ARGS...]".  For booting the Hurd, we
122            support an additional special syntax:
123              ld.so [-LIBS...] PROGRAM [ARGS...]
124            Each LIBS word consists of "FILENAME=MEMOBJ";
125            for example "-/lib/libc.so=123" says that the contents of
126            /lib/libc.so are found in a memory object whose port name
127            in our task is 123.  */
128         while (_dl_argc > 2 && _dl_argv[1][0] == '-' && _dl_argv[1][1] != '-')
129           {
130             char *lastslash, *memobjname, *p;
131             struct link_map *l;
132             mach_port_t memobj;
133             error_t err;
134
135             ++_dl_skip_args;
136             --_dl_argc;
137             p = _dl_argv++[1] + 1;
138
139             memobjname = strchr (p, '=');
140             if (! memobjname)
141               _dl_sysdep_fatal ("Bogus library spec: ", p, "\n", NULL);
142             *memobjname++ = '\0';
143             memobj = 0;
144             while (*memobjname != '\0')
145               memobj = (memobj * 10) + (*memobjname++ - '0');
146
147             /* Add a user reference on the memory object port, so we will
148                still have one after _dl_map_object_from_fd calls our
149                `close'.  */
150             err = __mach_port_mod_refs (__mach_task_self (), memobj,
151                                         MACH_PORT_RIGHT_SEND, +1);
152             assert_perror (err);
153
154             lastslash = strrchr (p, '/');
155             l = _dl_map_object_from_fd (lastslash ? lastslash + 1 : p,
156                                         memobj, strdup (p));
157
158             /* Squirrel away the memory object port where it
159                can be retrieved by the program later.  */
160             l->l_info[DT_NULL] = (void *) memobj;
161           }
162
163       /* Call elf/rtld.c's main program.  It will set everything
164          up and leave us to transfer control to USER_ENTRY.  */
165       (*dl_main) ((const Elf32_Phdr *) _dl_hurd_data->phdr,
166                   _dl_hurd_data->phdrsz / sizeof (Elf32_Phdr),
167                   &_dl_hurd_data->user_entry);
168
169       if (_dl_skip_args && _dl_argv[-_dl_skip_args] == (char *) p)
170         {
171           /* We are ignoring the first few arguments, but we have no Hurd
172              startup data.  It is magical convention that ARGV[0] == P in
173              this case.  The startup code in init-first.c will get confused
174              if this is not the case, so we must rearrange things to make
175              it so.  Overwrite the original ARGV[0] at P with
176              ARGV[_dl_skip_args].  */
177           assert ((char *) p < _dl_argv[0]);
178           _dl_argv[0] = strcpy ((char *) p, _dl_argv[0]);
179         }
180
181       {
182         extern void _dl_start_user (void);
183         /* Unwind the stack to ARGDATA and simulate a return from _dl_start
184            to the RTLD_START code which will run the user's entry point.  */
185         RETURN_TO (argdata, &_dl_start_user, _dl_hurd_data->user_entry);
186       }
187     }
188
189   /* Set up so we can do RPCs.  */
190   __mach_init ();
191
192 fmh();                          /* XXX */
193
194   /* See hurd/hurdstartup.c; this deals with getting information
195      from the exec server and slicing up the arguments.
196      Then it will call `go', above.  */
197   _hurd_startup (start_argptr, &go);
198
199   LOSE;
200   abort ();
201 }
202
203 void
204 _dl_sysdep_start_cleanup (void)
205 {
206   /* Deallocate the reply port and task port rights acquired by
207      __mach_init.  We are done with them now, and the user will
208      reacquire them for himself when he wants them.  */
209   __mig_dealloc_reply_port (MACH_PORT_NULL);
210   __mach_port_deallocate (__mach_task_self (), __mach_task_self_);
211 }
212 \f
213 int
214 _dl_sysdep_open_zero_fill (void)
215 {
216   /* The minimal mmap below uses the fd as a memory object port.
217      The real mmap used for dlopen ignores the fd for MAP_ANON.  */
218   return (int) MACH_PORT_NULL;
219 }
220
221
222 void
223 _dl_sysdep_fatal (const char *msg, ...)
224 {
225   va_list ap;
226
227   va_start (ap, msg);
228   do
229     {
230       size_t len = strlen (msg);
231       mach_msg_type_number_t nwrote;
232       do
233         {
234           if (__io_write (_hurd_init_dtable[2], msg, len, -1, &nwrote))
235             break;
236           len -= nwrote;
237           msg += nwrote;
238         } while (nwrote > 0);
239       msg = va_arg (ap, const char *);
240     } while (msg);
241   va_end (ap);
242
243   _exit (127);
244 }
245
246
247 void
248 _dl_sysdep_message (const char *msg, ...)
249 {
250   va_list ap;
251
252   va_start (ap, msg);
253   do
254     {
255       size_t len = strlen (msg);
256       mach_msg_type_number_t nwrote;
257       do
258         {
259           if (__io_write (_hurd_init_dtable[1], msg, len, -1, &nwrote))
260             break;
261           len -= nwrote;
262           msg += nwrote;
263         } while (nwrote > 0);
264       msg = va_arg (ap, const char *);
265     } while (msg);
266   va_end (ap);
267 }
268 \f
269 /* Minimal open/close/mmap implementation sufficient for initial loading of
270    shared libraries.  These are weak definitions so that when the
271    dynamic linker re-relocates itself to be user-visible (for -ldl),
272    it will get the user's definition (i.e. usually libc's).  */
273
274 int
275 __open (const char *file_name, int mode, ...)
276 {
277   enum retry_type doretry;
278   char retryname[1024];         /* XXX string_t LOSES! */
279   file_t startdir, newpt, fileport;
280   int dealloc_dir;
281   int nloops;
282   error_t err;
283
284   assert (mode == O_RDONLY);
285
286   startdir = _dl_hurd_data->portarray[file_name[0] == '/' ?
287                                       INIT_PORT_CRDIR : INIT_PORT_CWDIR];
288
289   while (file_name[0] == '/')
290     file_name++;
291
292   if (err = __dir_lookup (startdir, file_name, mode, 0,
293                           &doretry, retryname, &fileport))
294     return __hurd_fail (err);
295
296   dealloc_dir = 0;
297   nloops = 0;
298
299   while (1)
300     {
301       if (dealloc_dir)
302         __mach_port_deallocate (__mach_task_self (), startdir);
303       if (err)
304         return __hurd_fail (err);
305
306       switch (doretry)
307         {
308         case FS_RETRY_REAUTH:
309           {
310             mach_port_t ref = __mach_reply_port ();
311             err = __io_reauthenticate (fileport, ref, MACH_MSG_TYPE_MAKE_SEND);
312             if (! err)
313               err = __auth_user_authenticate
314                 (_dl_hurd_data->portarray[INIT_PORT_AUTH],
315                  ref, MACH_MSG_TYPE_MAKE_SEND,
316                  &newpt);
317             __mach_port_destroy (__mach_task_self (), ref);
318           }
319           __mach_port_deallocate (__mach_task_self (), fileport);
320           if (err)
321             return __hurd_fail (err);
322           fileport = newpt;
323           /* Fall through.  */
324
325         case FS_RETRY_NORMAL:
326 #ifdef SYMLOOP_MAX
327           if (nloops++ >= SYMLOOP_MAX)
328             return __hurd_fail (ELOOP);
329 #endif
330
331           /* An empty RETRYNAME indicates we have the final port.  */
332           if (retryname[0] == '\0')
333             {
334               mach_port_t memobj_rd, memobj_wr;
335
336               dealloc_dir = 1;
337
338             opened:
339               /* We have the file open.  Now map it.  */
340               err = __io_map (fileport, &memobj_rd, &memobj_wr);
341               if (dealloc_dir)
342                 __mach_port_deallocate (__mach_task_self (), fileport);
343               if (err)
344                 return __hurd_fail (err);
345               if (memobj_wr != MACH_PORT_NULL)
346                 __mach_port_deallocate (__mach_task_self (), memobj_wr);
347
348               return (int) memobj_rd;
349             }
350
351           startdir = fileport;
352           dealloc_dir = 1;
353           file_name = retryname;
354           break;
355
356         case FS_RETRY_MAGICAL:
357           switch (retryname[0])
358             {
359             case '/':
360               startdir = _dl_hurd_data->portarray[INIT_PORT_CRDIR];
361               dealloc_dir = 0;
362               if (fileport != MACH_PORT_NULL)
363                 __mach_port_deallocate (__mach_task_self (), fileport);
364               file_name = &retryname[1];
365               break;
366
367             case 'f':
368               if (retryname[1] == 'd' && retryname[2] == '/' &&
369                   isdigit (retryname[3]))
370                 {
371                   /* We can't use strtol for the decoding here
372                      because it brings in hairy locale bloat.  */
373                   char *p;
374                   int fd = 0;
375                   for (p = &retryname[3]; isdigit (*p); ++p)
376                     fd = (fd * 10) + (*p - '0');
377                   /* Check for excess text after the number.  A slash is
378                      valid; it ends the component.  Anything else does not
379                      name a numeric file descriptor.  */
380                   if (*p != '/' && *p != '\0')
381                     return __hurd_fail (ENOENT);
382                   if (fd < 0 || fd >= _dl_hurd_data->dtablesize ||
383                       _dl_hurd_data->dtable[fd] == MACH_PORT_NULL)
384                     /* If the name was a proper number, but the file
385                        descriptor does not exist, we return EBADF instead
386                        of ENOENT.  */
387                     return __hurd_fail (EBADF);
388                   fileport = _dl_hurd_data->dtable[fd];
389                   if (*p == '\0')
390                     {
391                       /* This descriptor is the file port we want.  */
392                       dealloc_dir = 0;
393                       goto opened;
394                     }
395                   else
396                     {
397                       /* Do a normal retry on the remaining components.  */
398                       startdir = fileport;
399                       dealloc_dir = 1;
400                       file_name = p + 1; /* Skip the slash.  */
401                       break;
402                     }
403                 }
404               else
405                 goto bad_magic;
406               break;
407
408             case 'm':
409               if (retryname[1] == 'a' && retryname[2] == 'c' &&
410                   retryname[3] == 'h' && retryname[4] == 't' &&
411                   retryname[5] == 'y' && retryname[6] == 'p' &&
412                   retryname[7] == 'e')
413                 {
414                   error_t err;
415                   struct host_basic_info hostinfo;
416                   mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
417                   char *p;
418                   if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
419                                          (natural_t *) &hostinfo,
420                                          &hostinfocnt))
421                     return err;
422                   if (hostinfocnt != HOST_BASIC_INFO_COUNT)
423                     return EGRATUITOUS;
424                   p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
425                   *--p = '/';
426                   p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
427                   if (p < retryname)
428                     abort ();   /* XXX write this right if this ever happens */
429                   if (p > retryname)
430                     strcpy (retryname, p);
431                   startdir = fileport;
432                   dealloc_dir = 1;
433                 }
434               else
435                 goto bad_magic;
436               break;
437
438             case 't':
439               if (retryname[1] == 't' && retryname[2] == 'y')
440                 switch (retryname[3])
441                   {
442                     error_t opentty (file_t *result)
443                       {
444                         error_t err;
445                         file_t unauth;
446                         err = __termctty_open_terminal
447                           (_dl_hurd_data->portarray[INIT_PORT_CTTYID],
448                            mode, &unauth);
449                         if (! err)
450                           {
451                             mach_port_t ref = __mach_reply_port ();
452                             err = __io_reauthenticate
453                               (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
454                             if (! err)
455                               err = __auth_user_authenticate
456                                 (_dl_hurd_data->portarray[INIT_PORT_AUTH],
457                                  ref, MACH_MSG_TYPE_MAKE_SEND,
458                                  result);
459                             __mach_port_deallocate (__mach_task_self (),
460                                                     unauth);
461                             __mach_port_destroy (__mach_task_self (), ref);
462                           }
463                         return err;
464                       }
465
466                   case '\0':
467                     if (err = opentty (&fileport))
468                       return __hurd_fail (err);
469                     dealloc_dir = 1;
470                     goto opened;
471                   case '/':
472                     if (err = opentty (&startdir))
473                       return __hurd_fail (err);
474                     dealloc_dir = 1;
475                     strcpy (retryname, &retryname[4]);
476                     break;
477                   default:
478                     goto bad_magic;
479                   }
480               else
481                 goto bad_magic;
482               break;
483
484             default:
485             bad_magic:
486               return __hurd_fail (EGRATUITOUS);
487             }
488           break;
489
490         default:
491           return __hurd_fail (EGRATUITOUS);
492         }
493
494       err = __dir_lookup (startdir, file_name, mode, 0,
495                           &doretry, retryname, &fileport);
496     }
497 }
498
499 int
500 __close (int fd)
501 {
502   if (fd != (int) MACH_PORT_NULL)
503     __mach_port_deallocate (__mach_task_self (), (mach_port_t) fd);
504   return 0;
505 }
506
507 caddr_t
508 __mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
509 {
510   error_t err;
511   vm_prot_t vmprot;
512   vm_address_t mapaddr;
513
514   vmprot = VM_PROT_NONE;
515   if (prot & PROT_READ)
516     vmprot |= VM_PROT_READ;
517   if (prot & PROT_WRITE)
518     vmprot |= VM_PROT_WRITE;
519   if (prot & PROT_EXEC)
520     vmprot |= VM_PROT_EXECUTE;
521
522   mapaddr = (vm_address_t) addr;
523   err = __vm_map (__mach_task_self (),
524                   &mapaddr, (vm_size_t) len, 0 /*ELF_MACHINE_USER_ADDRESS_MASK*/,
525                   !(flags & MAP_FIXED),
526                   (mach_port_t) fd, (vm_offset_t) offset,
527                   flags & (MAP_COPY|MAP_PRIVATE),
528                   vmprot, VM_PROT_ALL,
529                   (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY);
530   if (err == KERN_NO_SPACE && (flags & MAP_FIXED))
531     {
532       /* XXX this is not atomic as it is in unix! */
533       /* The region is already allocated; deallocate it first.  */
534       err = __vm_deallocate (__mach_task_self (), mapaddr, len);
535       if (! err)
536         err = __vm_map (__mach_task_self (),
537                         &mapaddr, (vm_size_t) len, 0 /*ELF_MACHINE_USER_ADDRESS_MASK*/,
538                         !(flags & MAP_FIXED),
539                         (mach_port_t) fd, (vm_offset_t) offset,
540                         flags & (MAP_COPY|MAP_PRIVATE),
541                         vmprot, VM_PROT_ALL,
542                         (flags & MAP_SHARED)
543                         ? VM_INHERIT_SHARE : VM_INHERIT_COPY);
544     }
545
546   return err ? (caddr_t) __hurd_fail (err) : (caddr_t) mapaddr;
547 }
548
549 void
550 _exit (int status)
551 {
552   __proc_mark_exit (_dl_hurd_data->portarray[INIT_PORT_PROC],
553                     W_EXITCODE (status, 0), 0);
554   while (__task_terminate (__mach_task_self ()))
555     __mach_task_self_ = (__mach_task_self) ();
556 }
557
558 weak_symbol (_exit)
559 weak_symbol (__open)
560 weak_symbol (__close)
561 weak_symbol (__mmap)
562 \f
563
564 /* This function is called by interruptible RPC stubs.  For initial
565    dynamic linking, just use the normal mach_msg.  Since this defn is
566    weak, the real defn in libc.so will override it if we are linked into
567    the user program (-ldl).  */
568
569 error_t
570 _hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
571                          mach_msg_option_t option,
572                          mach_msg_size_t send_size,
573                          mach_msg_size_t rcv_size,
574                          mach_port_t rcv_name,
575                          mach_msg_timeout_t timeout,
576                          mach_port_t notify)
577 {
578   return __mach_msg (msg, option, send_size, rcv_size, rcv_name,
579                      timeout, notify);
580 }
581 weak_symbol (_hurd_intr_rpc_mach_msg)