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