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