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