Wed May 10 21:00:47 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 int _dl_argc;
40 extern char **_dl_argv;
41 extern char **_environ;
42
43 struct hurd_startup_data *_dl_hurd_data;
44
45 Elf32_Addr
46 _dl_sysdep_start (void **start_argptr,
47                   void (*dl_main) (const Elf32_Phdr *phdr, Elf32_Word phent,
48                                    Elf32_Addr *user_entry))
49 {
50   void go (int *argdata)
51     {
52       char **p;
53
54       /* Cache the information in various global variables.  */
55       _dl_argc = *argdata++;
56       _dl_argv = (void *) argdata;
57       _environ = &_dl_argv[_dl_argc + 1];
58       for (p = _environ; *p; ++p);
59       _dl_hurd_data = (void *) ++p;
60
61       _dl_secure = _dl_hurd_data->flags & EXEC_SECURE;
62
63       /* Call elf/rtld.c's main program.  It will set everything
64          up and leave us to transfer control to USER_ENTRY.  */
65       (*dl_main) ((const Elf32_Phdr *) _dl_hurd_data->phdr,
66                   _dl_hurd_data->phdrsz / sizeof (Elf32_Phdr),
67                   &_dl_hurd_data->user_entry);
68
69       {
70         extern void _dl_start_user (void);
71         /* Unwind the stack to ARGDATA and simulate a return from _dl_start
72            to the RTLD_START code which will run the user's entry point.  */
73         RETURN_TO (argdata, &_dl_start_user, _dl_hurd_data->user_entry);
74       }
75     }
76
77   /* See hurd/hurdstartup.c; this deals with getting information
78      from the exec server and slicing up the arguments.
79      Then it will call `go', above.  */
80   _hurd_startup (start_argptr, &go);
81
82   LOSE;
83 }
84
85 /* This is called when all other dynamic linking is finished, before the
86    dynamic linker re-relocates itself when ld.so itself appears in a
87    DT_NEEDED entry.  It is called whether of not ld.so is being linked in.
88
89    We take this opportunity to deallocate the reply port and task-self send
90    right user reference we have acquired, since they will not be used again
91    before the library and user code runs.  The C library will acquire its
92    own ports in its initialization.  */
93
94 void
95 _dl_sysdep_prepare_for_ld_reloc (void)
96 {
97   __mig_dealloc_reply_port (__mig_get_reply_port ());
98   __mach_port_deallocate (__mach_task_self (), __mach_task_self ());
99 }
100 \f
101 int
102 _dl_sysdep_open_zero_fill (void)
103 {
104   return (int) MACH_PORT_NULL;
105 }
106
107
108 void
109 _dl_sysdep_fatal (const char *msg, ...)
110 {
111   extern __typeof (__io_write) __hurd_intr_rpc_io_write;
112   va_list ap;
113
114   va_start (ap, msg);
115   do
116     {
117       size_t len = strlen (msg);
118       mach_msg_type_number_t nwrote;
119       do
120         {
121           if (__hurd_intr_rpc_io_write (_hurd_init_dtable[2],
122                                         msg, len, -1, &nwrote))
123             break;
124           len -= nwrote;
125           msg += nwrote;
126         } while (nwrote > 0);
127       msg = va_arg (ap, const char *);
128     } while (msg);
129   va_end (ap);
130
131   _exit (127);
132 }
133
134
135 /* Minimal open/close/mmap implementation sufficient for initial loading of
136    shared libraries.  These are weak definitions so that when the
137    dynamic linker re-relocates itself to be user-visible (for -ldl),
138    it will get the user's definition (i.e. usually libc's).  */
139
140 int
141 open (const char *file_name, int mode, ...)
142 {
143   extern __typeof (__dir_lookup) __hurd_intr_rpc_dir_lookup;
144   extern __typeof (__io_reauthenticate) __hurd_intr_rpc_io_reauthenticate;
145   enum retry_type doretry;
146   char retryname[1024];         /* XXX string_t LOSES! */
147   file_t startdir, newpt, fileport;
148   int dealloc_dir;
149   int nloops;
150
151
152   assert (mode == O_RDONLY);
153
154
155   startdir = _dl_hurd_data->portarray[file_name[0] == '/' ?
156                                       INIT_PORT_CRDIR : INIT_PORT_CWDIR];
157
158   while (file_name[0] == '/')
159     file_name++;
160
161   if (errno = __hurd_intr_rpc_dir_lookup (startdir, file_name, mode, 0,
162                                           &doretry, retryname, &fileport))
163     return -1;
164
165   dealloc_dir = 0;
166   nloops = 0;
167   errno = 0;
168   
169   while (1)
170     {
171       if (dealloc_dir)
172         __mach_port_deallocate (__mach_task_self (), startdir);
173       if (errno)
174         return -1;
175
176       switch (doretry)
177         {
178         case FS_RETRY_REAUTH:
179           {
180             mach_port_t ref = __mach_reply_port ();
181             errno = __hurd_intr_rpc_io_reauthenticate
182               (fileport, ref, MACH_MSG_TYPE_MAKE_SEND);
183             if (! errno)
184               errno = __auth_user_authenticate
185                 (_dl_hurd_data->portarray[INIT_PORT_AUTH],
186                  fileport,
187                  ref, MACH_MSG_TYPE_MAKE_SEND,
188                  &newpt);
189             __mach_port_destroy (__mach_task_self (), ref);
190           }
191           __mach_port_deallocate (__mach_task_self (), fileport);
192           if (errno)
193             return -1;
194           fileport = newpt;
195           /* Fall through.  */
196
197         case FS_RETRY_NORMAL:
198 #ifdef SYMLOOP_MAX
199           if (nloops++ >= SYMLOOP_MAX)
200             {
201               errno = ELOOP;
202               return -1;
203             }
204 #endif
205
206           /* An empty RETRYNAME indicates we have the final port.  */
207           if (retryname[0] == '\0')
208             {
209               mach_port_t memobj_rd, memobj_wr;
210               extern __typeof (__io_map) __hurd_intr_rpc_io_map;
211
212               dealloc_dir = 1;
213
214             opened:
215               /* We have the file open.  Now map it.  */
216               errno = __hurd_intr_rpc_io_map (fileport,
217                                               &memobj_rd, &memobj_wr);
218               if (dealloc_dir)
219                 __mach_port_deallocate (__mach_task_self (), fileport);
220               if (errno)
221                 return -1;
222               if (memobj_wr != MACH_PORT_NULL)
223                 __mach_port_deallocate (__mach_task_self (), memobj_wr);
224
225               return (int) memobj_rd;
226             }
227
228           startdir = fileport;
229           dealloc_dir = 1;
230           file_name = retryname;
231           break;
232
233         case FS_RETRY_MAGICAL:
234           switch (retryname[0])
235             {
236             case '/':
237               startdir = _dl_hurd_data->portarray[INIT_PORT_CRDIR];
238               dealloc_dir = 0;
239               if (fileport != MACH_PORT_NULL)
240                 __mach_port_deallocate (__mach_task_self (), fileport);
241               file_name = &retryname[1];
242               break;
243
244             case 'f':
245               if (retryname[1] == 'd' && retryname[2] == '/')
246                 {
247                   int fd;
248                   char *end;
249                   errno = 0;
250                   fd = (int) strtol (retryname, &end, 10);
251                   if (end == NULL || errno || /* Malformed number.  */
252                       /* Check for excess text after the number.  A slash
253                          is valid; it ends the component.  Anything else
254                          does not name a numeric file descriptor.  */
255                       (*end != '/' && *end != '\0'))
256                     {
257                       errno = ENOENT;
258                       return -1;
259                     }
260                   if (fd < 0 || fd >= _dl_hurd_data->dtablesize ||
261                       _dl_hurd_data->dtable[fd] == MACH_PORT_NULL)
262                     {
263                       /* If the name was a proper number, but the file
264                          descriptor does not exist, we return EBADF instead
265                          of ENOENT.  */
266                       errno = EBADF;
267                       return -1;
268                     }
269                   fileport = _dl_hurd_data->dtable[fd];
270                   if (*end == '\0')
271                     {
272                       /* This descriptor is the file port we want.  */
273                       dealloc_dir = 0;
274                       goto opened;
275                     }
276                   else
277                     {
278                       /* Do a normal retry on the remaining components.  */
279                       startdir = fileport;
280                       dealloc_dir = 1;
281                       file_name = end + 1; /* Skip the slash.  */
282                       break;
283                     }
284                 }
285               else
286                 goto bad_magic;
287               break;
288
289             case 'm':
290               if (retryname[1] == 'a' && retryname[2] == 'c' &&
291                   retryname[3] == 'h' && retryname[4] == 't' &&
292                   retryname[5] == 'y' && retryname[6] == 'p' &&
293                   retryname[7] == 'e')
294                 {
295                   error_t err;
296                   struct host_basic_info hostinfo;
297                   mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
298                   char *p;
299                   if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
300                                          (natural_t *) &hostinfo,
301                                          &hostinfocnt))
302                     return err;
303                   if (hostinfocnt != HOST_BASIC_INFO_COUNT)
304                     return EGRATUITOUS;
305                   p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
306                   *--p = '/';
307                   p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
308                   if (p < retryname)
309                     abort ();   /* XXX write this right if this ever happens */
310                   if (p > retryname)
311                     strcpy (retryname, p);
312                   startdir = fileport;
313                   dealloc_dir = 1;
314                 }
315               else
316                 goto bad_magic;
317               break;
318
319             case 't':
320               if (retryname[1] == 't' && retryname[2] == 'y')
321                 switch (retryname[3])
322                   {
323                     error_t opentty (file_t *result)
324                       {
325                         error_t err;
326                         file_t unauth;
327                         err = __termctty_open_terminal
328                           (_dl_hurd_data->portarray[INIT_PORT_CTTYID],
329                            mode, &unauth);
330                         if (! err)
331                           {
332                             mach_port_t ref = __mach_reply_port ();
333                             err = __hurd_intr_rpc_io_reauthenticate
334                               (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
335                             if (! err)
336                               err = __auth_user_authenticate
337                                 (_dl_hurd_data->portarray[INIT_PORT_AUTH],
338                                  unauth,
339                                  ref, MACH_MSG_TYPE_MAKE_SEND,
340                                  result);
341                             __mach_port_deallocate (__mach_task_self (),
342                                                     unauth);
343                             __mach_port_destroy (__mach_task_self (), ref);
344                           }
345                         return err;
346                       }
347
348                   case '\0':
349                     if (errno = opentty (&fileport))
350                       return -1;
351                     dealloc_dir = 1;
352                     goto opened;
353                   case '/':
354                     if (errno = opentty (&startdir))
355                       return -1;
356                     dealloc_dir = 1;
357                     strcpy (retryname, &retryname[4]);
358                     break;
359                   default:
360                     goto bad_magic;
361                   }
362               else
363                 goto bad_magic;
364               break;
365
366             default:
367             bad_magic:
368               errno = EGRATUITOUS;
369               return -1;
370             }
371           break;                
372
373         default:
374           errno = EGRATUITOUS;
375           return -1;
376         }
377
378       errno = __hurd_intr_rpc_dir_lookup (startdir, file_name, mode, 0,
379                                           &doretry, retryname, &fileport);
380     }
381 }
382
383 int
384 close (int fd)
385 {
386   if (fd != (int) MACH_PORT_NULL)
387     __mach_port_deallocate (__mach_task_self (), (mach_port_t) fd);
388   return 0;
389 }
390
391 caddr_t
392 mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
393 {
394   vm_prot_t vmprot;
395   vm_address_t mapaddr;
396
397   vmprot = VM_PROT_NONE;
398   if (prot & PROT_READ)
399     vmprot |= VM_PROT_READ;
400   if (prot & PROT_WRITE)
401     vmprot |= VM_PROT_WRITE;
402   if (prot & PROT_EXEC)
403     vmprot |= VM_PROT_EXECUTE;
404
405   mapaddr = (vm_address_t) addr;
406   errno = __vm_map (__mach_task_self (),
407                     &mapaddr, (vm_size_t) len, (vm_address_t) 0,
408                     !(flags & MAP_FIXED),
409                     (mach_port_t) fd, (vm_offset_t) offset,
410                     flags & (MAP_COPY|MAP_PRIVATE),
411                     vmprot, VM_PROT_ALL,
412                     (flags & MAP_INHERIT) ? VM_INHERIT_COPY : VM_INHERIT_NONE);
413   return errno ? (caddr_t) -1 : (caddr_t) mapaddr;
414 }
415
416 void
417 _exit (int status)
418 {
419   extern __typeof (__proc_mark_exit) __hurd_intr_rpc_proc_mark_exit;
420   __hurd_intr_rpc_proc_mark_exit (_dl_hurd_data->portarray[INIT_PORT_PROC],
421                                   W_EXITCODE (status, 0));
422   while (__task_terminate (__mach_task_self ()))
423     __mach_task_self_ = (__mach_task_self) ();
424 }
425
426 weak_symbol (_exit)
427 weak_symbol (open)
428 weak_symbol (close)
429 weak_symbol (mmap)
430 \f
431 /* Minimal `malloc' allocator for use while loading shared libraries.
432    Only small blocks are allocated, and none are ever freed.  */
433
434 void *
435 malloc (size_t n)
436 {
437   static vm_address_t ptr, end;
438   void *block;
439
440   if (end == 0)
441     {
442       /* Consume any unused space in the last page of our data segment.  */
443       extern char _end;
444       ptr = (vm_address_t) &_end;
445       end = round_page (ptr);
446     }
447
448   /* Make sure the allocation pointer is ideally aligned.  */
449   ptr += sizeof (double) - 1;
450   ptr &= ~(sizeof (double) - 1);
451
452   if (ptr + n >= end)
453     {
454       /* Insufficient space left; allocate another page.  */
455       vm_address_t page;
456       assert (n <= __vm_page_size);
457       __vm_allocate (__mach_task_self (), &page, __vm_page_size, 1);
458       if (page != end)
459         ptr = page;
460       end = page + __vm_page_size;
461     }
462
463   block = (void *) ptr;
464   ptr += n;
465   return block;
466 }
467
468 weak_symbol (malloc)
469
470 /* These should never be called.  */
471 void *realloc (void *ptr, size_t n) { ptr += n; abort (); }
472 void free (void *ptr) { ptr = ptr; abort (); }
473 weak_symbol (realloc)
474 weak_symbol (free)
475 \f
476 /* Avoid signal frobnication in setjmp/longjmp.  */
477
478 int __sigjmp_save (sigjmp_buf env, int savemask) 
479 { env[0].__mask_was_saved = savemask; return 0; }
480 weak_symbol (__sigjmp_save)
481
482 void
483 longjmp (jmp_buf env, int val) { __longjmp (env[0].__jmpbuf, val); }
484 weak_symbol (longjmp)
485
486
487 /* Stub out this function that is called by interruptible RPC stubs.  It
488    should never get called during initial dynamic linking, because we use
489    only the raw MiG stub functions __hurd_intr_rpc_*.  Since this defn is
490    weak, the real defn in libc.so will override it if we are linked into
491    the user program (-ldl).  */
492 struct hurd_sigstate *
493 _hurd_thread_sigstate (thread_t thread) { thread = thread; abort (); }
494 weak_symbol (_hurd_thread_sigstate)