]>
Commit | Line | Data |
---|---|---|
99b306dc RM |
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 | ||
a1a9d215 RM |
39 | extern void __mach_init (void); |
40 | ||
99b306dc RM |
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. */ | |
a1a9d215 RM |
57 | _dl_argc = *argdata; |
58 | _dl_argv = (void *) &argdata[1]; | |
99b306dc RM |
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 | ||
a1a9d215 RM |
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 | ||
99b306dc RM |
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 | ||
a1a9d215 RM |
85 | /* Set up so we can do RPCs. */ |
86 | __mach_init (); | |
87 | ||
99b306dc RM |
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) |