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