]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/mach/hurd/dl-sysdep.c
1999-01-23 Roland McGrath <roland@baalperazim.frob.com>
[thirdparty/glibc.git] / sysdeps / mach / hurd / dl-sysdep.c
1 /* Operating system support for run-time dynamic linker. Hurd version.
2 Copyright (C) 1995, 96, 97, 98, 99 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 not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, 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 <elf/ldsodefs.h>
27 #include <sys/wait.h>
28 #include <assert.h>
29 #include <sysdep.h>
30 #include <mach/mig_support.h>
31 #include "hurdstartup.h"
32 #include <mach/host_info.h>
33 #include <stdio-common/_itoa.h>
34 #include <hurd/auth.h>
35 #include <hurd/term.h>
36 #include <stdarg.h>
37 #include <ctype.h>
38 #include <sys/stat.h>
39
40 #include <entry.h>
41 #include <dl-machine.h>
42 #include <dl-procinfo.h>
43
44 extern void __mach_init (void);
45
46 extern int _dl_argc;
47 extern char **_dl_argv;
48 extern char **_environ;
49 extern void ENTRY_POINT (void);
50
51 int __libc_enable_secure;
52 int __libc_multiple_libcs; /* Defining this here avoids the inclusion
53 of init-first. */
54 /* This variable containts the lowest stack address ever used. */
55 void *__libc_stack_end;
56 unsigned long int _dl_hwcap_mask = HWCAP_IMPORTANT;
57
58
59 struct hurd_startup_data *_dl_hurd_data;
60
61 /* Defining these variables here avoids the inclusion of hurdsig.c. */
62 unsigned long int __hurd_sigthread_stack_base;
63 unsigned long int __hurd_sigthread_stack_end;
64 unsigned long int *__hurd_sigthread_variables;
65
66 /* Defining these variables here avoids the inclusion of init-first.c.
67 We need to provide temporary storage for the per-thread variables
68 of the main user thread here, since it is used for storing the
69 `errno' variable. Note that this information is lost once we
70 relocate the dynamic linker. */
71 static unsigned long int threadvars[_HURD_THREADVAR_MAX];
72 unsigned long int __hurd_threadvar_stack_offset
73 = (unsigned long int) &threadvars;
74 unsigned long int __hurd_threadvar_stack_mask;
75
76 /* XXX loser kludge for vm_map kernel bug */
77 static vm_address_t fmha;
78 static vm_size_t fmhs;
79 static void unfmh(void){
80 __vm_deallocate(__mach_task_self(),fmha,fmhs);}
81 static void fmh(void) {
82 error_t err;int x;mach_port_t p;
83 vm_address_t a=0x08000000U,max=VM_MAX_ADDRESS;
84 while (!(err=__vm_region(__mach_task_self(),&a,&fmhs,&x,&x,&x,&x,&p,&x))){
85 __mach_port_deallocate(__mach_task_self(),p);
86 if (a+fmhs>=0x80000000U){
87 max=a; break;}
88 fmha=a+=fmhs;}
89 if (err) assert(err==KERN_NO_SPACE);
90 if (!fmha)fmhs=0;else{
91 fmhs=max-fmha;
92 err = __vm_map (__mach_task_self (),
93 &fmha, fmhs, 0, 0, MACH_PORT_NULL, 0, 1,
94 VM_PROT_NONE, VM_PROT_NONE, VM_INHERIT_COPY);
95 assert_perror(err);}
96 }
97 /* XXX loser kludge for vm_map kernel bug */
98
99
100
101 Elf32_Addr
102 _dl_sysdep_start (void **start_argptr,
103 void (*dl_main) (const Elf32_Phdr *phdr, Elf32_Word phent,
104 Elf32_Addr *user_entry))
105 {
106 void go (int *argdata)
107 {
108 extern unsigned int _dl_skip_args; /* rtld.c */
109 char **p;
110
111 /* Cache the information in various global variables. */
112 _dl_argc = *argdata;
113 _dl_argv = 1 + (char **) argdata;
114 _environ = &_dl_argv[_dl_argc + 1];
115 for (p = _environ; *p++;); /* Skip environ pointers and terminator. */
116
117 if ((void *) p == _dl_argv[0])
118 {
119 static struct hurd_startup_data nodata;
120 _dl_hurd_data = &nodata;
121 nodata.user_entry = (vm_address_t) &ENTRY_POINT;
122 }
123 else
124 _dl_hurd_data = (void *) p;
125
126 __libc_enable_secure = _dl_hurd_data->flags & EXEC_SECURE;
127
128 if (_dl_hurd_data->flags & EXEC_STACK_ARGS &&
129 _dl_hurd_data->user_entry == 0)
130 _dl_hurd_data->user_entry = (vm_address_t) &ENTRY_POINT;
131
132 unfmh(); /* XXX */
133
134 #if 0 /* XXX make this work for real someday... */
135 if (_dl_hurd_data->user_entry == (vm_address_t) &ENTRY_POINT)
136 /* We were invoked as a command, not as the program interpreter.
137 The generic ld.so code supports this: it will parse the args
138 as "ld.so PROGRAM [ARGS...]". For booting the Hurd, we
139 support an additional special syntax:
140 ld.so [-LIBS...] PROGRAM [ARGS...]
141 Each LIBS word consists of "FILENAME=MEMOBJ";
142 for example "-/lib/libc.so=123" says that the contents of
143 /lib/libc.so are found in a memory object whose port name
144 in our task is 123. */
145 while (_dl_argc > 2 && _dl_argv[1][0] == '-' && _dl_argv[1][1] != '-')
146 {
147 char *lastslash, *memobjname, *p;
148 struct link_map *l;
149 mach_port_t memobj;
150 error_t err;
151
152 ++_dl_skip_args;
153 --_dl_argc;
154 p = _dl_argv++[1] + 1;
155
156 memobjname = strchr (p, '=');
157 if (! memobjname)
158 _dl_sysdep_fatal ("Bogus library spec: ", p, "\n", NULL);
159 *memobjname++ = '\0';
160 memobj = 0;
161 while (*memobjname != '\0')
162 memobj = (memobj * 10) + (*memobjname++ - '0');
163
164 /* Add a user reference on the memory object port, so we will
165 still have one after _dl_map_object_from_fd calls our
166 `close'. */
167 err = __mach_port_mod_refs (__mach_task_self (), memobj,
168 MACH_PORT_RIGHT_SEND, +1);
169 assert_perror (err);
170
171 lastslash = strrchr (p, '/');
172 l = _dl_map_object_from_fd (lastslash ? lastslash + 1 : p,
173 memobj, strdup (p));
174
175 /* Squirrel away the memory object port where it
176 can be retrieved by the program later. */
177 l->l_info[DT_NULL] = (void *) memobj;
178 }
179 #endif
180
181 /* Call elf/rtld.c's main program. It will set everything
182 up and leave us to transfer control to USER_ENTRY. */
183 (*dl_main) ((const Elf32_Phdr *) _dl_hurd_data->phdr,
184 _dl_hurd_data->phdrsz / sizeof (Elf32_Phdr),
185 &_dl_hurd_data->user_entry);
186
187 if (_dl_skip_args && _dl_argv[-_dl_skip_args] == (char *) p)
188 {
189 /* We are ignoring the first few arguments, but we have no Hurd
190 startup data. It is magical convention that ARGV[0] == P in
191 this case. The startup code in init-first.c will get confused
192 if this is not the case, so we must rearrange things to make
193 it so. Overwrite the original ARGV[0] at P with
194 ARGV[_dl_skip_args]. */
195 assert ((char *) p < _dl_argv[0]);
196 _dl_argv[0] = strcpy ((char *) p, _dl_argv[0]);
197 }
198
199 {
200 extern void _dl_start_user (void);
201 /* Unwind the stack to ARGDATA and simulate a return from _dl_start
202 to the RTLD_START code which will run the user's entry point. */
203 RETURN_TO (argdata, &_dl_start_user, _dl_hurd_data->user_entry);
204 }
205 }
206
207 /* Set up so we can do RPCs. */
208 __mach_init ();
209
210 /* Initialize frequently used global variable. */
211 _dl_pagesize = __getpagesize ();
212
213 fmh(); /* XXX */
214
215 /* See hurd/hurdstartup.c; this deals with getting information
216 from the exec server and slicing up the arguments.
217 Then it will call `go', above. */
218 _hurd_startup (start_argptr, &go);
219
220 LOSE;
221 abort ();
222 }
223
224 void
225 _dl_sysdep_start_cleanup (void)
226 {
227 /* Deallocate the reply port and task port rights acquired by
228 __mach_init. We are done with them now, and the user will
229 reacquire them for himself when he wants them. */
230 __mig_dealloc_reply_port (MACH_PORT_NULL);
231 __mach_port_deallocate (__mach_task_self (), __mach_task_self_);
232 }
233 \f
234 /* Minimal open/close/mmap implementation sufficient for initial loading of
235 shared libraries. These are weak definitions so that when the
236 dynamic linker re-relocates itself to be user-visible (for -ldl),
237 it will get the user's definition (i.e. usually libc's). */
238
239 /* Open FILE_NAME and return a Hurd I/O for it in *PORT, or
240 return an error. If STAT is non-zero, stat the file into that stat buffer. */
241 static error_t
242 open_file (const char *file_name, int mode,
243 mach_port_t *port, struct stat *stat)
244 {
245 enum retry_type doretry;
246 char retryname[1024]; /* XXX string_t LOSES! */
247 file_t startdir, newpt, fileport;
248 int dealloc_dir;
249 int nloops;
250 error_t err;
251
252 assert (!(mode & ~O_READ));
253
254 startdir = _dl_hurd_data->portarray[file_name[0] == '/' ?
255 INIT_PORT_CRDIR : INIT_PORT_CWDIR];
256
257 while (file_name[0] == '/')
258 file_name++;
259
260 if (err = __dir_lookup (startdir, (char *)file_name, mode, 0,
261 &doretry, retryname, &fileport))
262 return err;
263
264 dealloc_dir = 0;
265 nloops = 0;
266
267 while (1)
268 {
269 if (dealloc_dir)
270 __mach_port_deallocate (__mach_task_self (), startdir);
271 if (err)
272 return err;
273
274 switch (doretry)
275 {
276 case FS_RETRY_REAUTH:
277 {
278 mach_port_t ref = __mach_reply_port ();
279 err = __io_reauthenticate (fileport, ref, MACH_MSG_TYPE_MAKE_SEND);
280 if (! err)
281 err = __auth_user_authenticate
282 (_dl_hurd_data->portarray[INIT_PORT_AUTH],
283 ref, MACH_MSG_TYPE_MAKE_SEND,
284 &newpt);
285 __mach_port_destroy (__mach_task_self (), ref);
286 }
287 __mach_port_deallocate (__mach_task_self (), fileport);
288 if (err)
289 return err;
290 fileport = newpt;
291 /* Fall through. */
292
293 case FS_RETRY_NORMAL:
294 #ifdef SYMLOOP_MAX
295 if (nloops++ >= SYMLOOP_MAX)
296 return ELOOP;
297 #endif
298
299 /* An empty RETRYNAME indicates we have the final port. */
300 if (retryname[0] == '\0')
301 {
302 dealloc_dir = 1;
303 opened:
304 /* We have the file open. Now map it. */
305 if (stat)
306 err = __io_stat (fileport, stat);
307
308 if (err)
309 {
310 if (dealloc_dir)
311 __mach_port_deallocate (__mach_task_self (), fileport);
312 }
313 else
314 {
315 if (!dealloc_dir)
316 __mach_port_mod_refs (__mach_task_self (), fileport,
317 MACH_PORT_RIGHT_SEND, 1);
318 *port = fileport;
319 }
320
321 return err;
322 }
323
324 startdir = fileport;
325 dealloc_dir = 1;
326 file_name = retryname;
327 break;
328
329 case FS_RETRY_MAGICAL:
330 switch (retryname[0])
331 {
332 case '/':
333 startdir = _dl_hurd_data->portarray[INIT_PORT_CRDIR];
334 dealloc_dir = 0;
335 if (fileport != MACH_PORT_NULL)
336 __mach_port_deallocate (__mach_task_self (), fileport);
337 file_name = &retryname[1];
338 break;
339
340 case 'f':
341 if (retryname[1] == 'd' && retryname[2] == '/' &&
342 isdigit (retryname[3]))
343 {
344 /* We can't use strtol for the decoding here
345 because it brings in hairy locale bloat. */
346 char *p;
347 int fd = 0;
348 for (p = &retryname[3]; isdigit (*p); ++p)
349 fd = (fd * 10) + (*p - '0');
350 /* Check for excess text after the number. A slash is
351 valid; it ends the component. Anything else does not
352 name a numeric file descriptor. */
353 if (*p != '/' && *p != '\0')
354 return ENOENT;
355 if (fd < 0 || fd >= _dl_hurd_data->dtablesize ||
356 _dl_hurd_data->dtable[fd] == MACH_PORT_NULL)
357 /* If the name was a proper number, but the file
358 descriptor does not exist, we return EBADF instead
359 of ENOENT. */
360 return EBADF;
361 fileport = _dl_hurd_data->dtable[fd];
362 if (*p == '\0')
363 {
364 /* This descriptor is the file port we want. */
365 dealloc_dir = 0;
366 goto opened;
367 }
368 else
369 {
370 /* Do a normal retry on the remaining components. */
371 startdir = fileport;
372 dealloc_dir = 1;
373 file_name = p + 1; /* Skip the slash. */
374 break;
375 }
376 }
377 else
378 goto bad_magic;
379 break;
380
381 case 'm':
382 if (retryname[1] == 'a' && retryname[2] == 'c' &&
383 retryname[3] == 'h' && retryname[4] == 't' &&
384 retryname[5] == 'y' && retryname[6] == 'p' &&
385 retryname[7] == 'e')
386 {
387 error_t err;
388 struct host_basic_info hostinfo;
389 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
390 char *p;
391 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
392 (natural_t *) &hostinfo,
393 &hostinfocnt))
394 return err;
395 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
396 return EGRATUITOUS;
397 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
398 *--p = '/';
399 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
400 if (p < retryname)
401 abort (); /* XXX write this right if this ever happens */
402 if (p > retryname)
403 strcpy (retryname, p);
404 startdir = fileport;
405 dealloc_dir = 1;
406 }
407 else
408 goto bad_magic;
409 break;
410
411 case 't':
412 if (retryname[1] == 't' && retryname[2] == 'y')
413 switch (retryname[3])
414 {
415 error_t opentty (file_t *result)
416 {
417 error_t err;
418 file_t unauth;
419 err = __termctty_open_terminal
420 (_dl_hurd_data->portarray[INIT_PORT_CTTYID],
421 mode, &unauth);
422 if (! err)
423 {
424 mach_port_t ref = __mach_reply_port ();
425 err = __io_reauthenticate
426 (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
427 if (! err)
428 err = __auth_user_authenticate
429 (_dl_hurd_data->portarray[INIT_PORT_AUTH],
430 ref, MACH_MSG_TYPE_MAKE_SEND,
431 result);
432 __mach_port_deallocate (__mach_task_self (),
433 unauth);
434 __mach_port_destroy (__mach_task_self (), ref);
435 }
436 return err;
437 }
438
439 case '\0':
440 if (err = opentty (&fileport))
441 return err;
442 dealloc_dir = 1;
443 goto opened;
444 case '/':
445 if (err = opentty (&startdir))
446 return err;
447 dealloc_dir = 1;
448 strcpy (retryname, &retryname[4]);
449 break;
450 default:
451 goto bad_magic;
452 }
453 else
454 goto bad_magic;
455 break;
456
457 default:
458 bad_magic:
459 return EGRATUITOUS;
460 }
461 break;
462
463 default:
464 return EGRATUITOUS;
465 }
466
467 err = __dir_lookup (startdir, (char *)file_name, mode, 0,
468 &doretry, retryname, &fileport);
469 }
470 }
471
472 int weak_function
473 __open (const char *file_name, int mode, ...)
474 {
475 mach_port_t port;
476 error_t err = open_file (file_name, mode, &port, 0);
477 if (err)
478 return __hurd_fail (err);
479 else
480 return (int)port;
481 }
482
483 int weak_function
484 __close (int fd)
485 {
486 if (fd != (int) MACH_PORT_NULL)
487 __mach_port_deallocate (__mach_task_self (), (mach_port_t) fd);
488 return 0;
489 }
490
491 __ssize_t weak_function
492 __libc_read (int fd, void *buf, size_t nbytes)
493 {
494 error_t err;
495 char *data;
496 mach_msg_type_number_t nread;
497
498 data = buf;
499 err = __io_read ((mach_port_t) fd, &data, &nread, -1, nbytes);
500 if (err)
501 return __hurd_fail (err);
502
503 if (data != buf)
504 {
505 memcpy (buf, data, nread);
506 __vm_deallocate (__mach_task_self (), (vm_address_t) data, nread);
507 }
508
509 return nread;
510 }
511
512 __ssize_t weak_function
513 __libc_write (int fd, const void *buf, size_t nbytes)
514 {
515 error_t err;
516 mach_msg_type_number_t nwrote;
517
518 assert (fd < _hurd_init_dtablesize);
519
520 err = __io_write (_hurd_init_dtable[fd], buf, nbytes, -1, &nwrote);
521 if (err)
522 return __hurd_fail (err);
523
524 return nwrote;
525 }
526
527 off_t weak_function
528 __lseek (int fd, off_t offset, int whence)
529 {
530 error_t err;
531
532 err = __io_seek ((mach_port_t) fd, offset, whence, &offset);
533 if (err)
534 return __hurd_fail (err);
535
536 return offset;
537 }
538
539 __ptr_t weak_function
540 __mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
541 {
542 error_t err;
543 vm_prot_t vmprot;
544 vm_address_t mapaddr;
545 mach_port_t memobj_rd, memobj_wr;
546
547 vmprot = VM_PROT_NONE;
548 if (prot & PROT_READ)
549 vmprot |= VM_PROT_READ;
550 if (prot & PROT_WRITE)
551 vmprot |= VM_PROT_WRITE;
552 if (prot & PROT_EXEC)
553 vmprot |= VM_PROT_EXECUTE;
554
555 if (flags & MAP_ANON)
556 memobj_rd = MACH_PORT_NULL;
557 else
558 {
559 assert (!(flags & MAP_SHARED));
560 err = __io_map ((mach_port_t) fd, &memobj_rd, &memobj_wr);
561 if (err)
562 return (__ptr_t) __hurd_fail (err);
563 __mach_port_deallocate (__mach_task_self (), memobj_wr);
564 }
565
566 mapaddr = (vm_address_t) addr;
567 err = __vm_map (__mach_task_self (),
568 &mapaddr, (vm_size_t) len, 0 /*ELF_MACHINE_USER_ADDRESS_MASK*/,
569 !(flags & MAP_FIXED),
570 memobj_rd,
571 (vm_offset_t) offset,
572 flags & (MAP_COPY|MAP_PRIVATE),
573 vmprot, VM_PROT_ALL,
574 (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY);
575 if (err == KERN_NO_SPACE && (flags & MAP_FIXED))
576 {
577 /* XXX this is not atomic as it is in unix! */
578 /* The region is already allocated; deallocate it first. */
579 err = __vm_deallocate (__mach_task_self (), mapaddr, len);
580 if (! err)
581 err = __vm_map (__mach_task_self (),
582 &mapaddr, (vm_size_t) len, 0 /*ELF_MACHINE_USER_ADDRESS_MASK*/,
583 !(flags & MAP_FIXED),
584 memobj_rd, (vm_offset_t) offset,
585 flags & (MAP_COPY|MAP_PRIVATE),
586 vmprot, VM_PROT_ALL,
587 (flags & MAP_SHARED)
588 ? VM_INHERIT_SHARE : VM_INHERIT_COPY);
589 }
590
591 if ((flags & MAP_ANON) == 0)
592 __mach_port_deallocate (__mach_task_self (), memobj_rd);
593
594 return err ? (__ptr_t) __hurd_fail (err) : (__ptr_t) mapaddr;
595 }
596
597 int weak_function
598 __fxstat (int vers, int fd, struct stat *buf)
599 {
600 error_t err;
601
602 assert (vers == _STAT_VER);
603
604 err = __io_stat ((mach_port_t) fd, buf);
605 if (err)
606 return __hurd_fail (err);
607
608 return 0;
609 }
610
611 int weak_function
612 __xstat (int vers, const char *file, struct stat *buf)
613 {
614 error_t err;
615 mach_port_t port;
616
617 assert (vers == _STAT_VER);
618
619 err = open_file (file, 0, &port, buf);
620 if (err)
621 return __hurd_fail (err);
622
623 __mach_port_deallocate (__mach_task_self (), port);
624
625 return 0;
626 }
627
628 pid_t weak_function
629 __getpid ()
630 {
631 pid_t pid, ppid;
632 int orphaned;
633
634 if (__proc_getpids (_dl_hurd_data->portarray[INIT_PORT_PROC],
635 &pid, &ppid, &orphaned))
636 return -1;
637
638 return pid;
639 }
640
641 /* This is called only in some strange cases trying to guess a value
642 for $ORIGIN for the executable. The dynamic linker copes with
643 getcwd failing (dl-object.c), and it's too much hassle to include
644 the functionality here. (We could, it just requires duplicating or
645 reusing getcwd.c's code but using our special lookup function as in
646 `open', above.) */
647 char *
648 weak_function
649 __getcwd (char *buf, size_t size)
650 {
651 errno = ENOSYS;
652 return NULL;
653 }
654
655 void weak_function
656 _exit (int status)
657 {
658 __proc_mark_exit (_dl_hurd_data->portarray[INIT_PORT_PROC],
659 W_EXITCODE (status, 0), 0);
660 while (__task_terminate (__mach_task_self ()))
661 __mach_task_self_ = (__mach_task_self) ();
662 }
663
664 /* Try to get a machine dependent instruction which will make the
665 program crash. This is used in case everything else fails. */
666 #include <abort-instr.h>
667 #ifndef ABORT_INSTRUCTION
668 /* No such instruction is available. */
669 # define ABORT_INSTRUCTION
670 #endif
671
672 void weak_function
673 abort (void)
674 {
675 /* Try to abort using the system specific command. */
676 ABORT_INSTRUCTION;
677
678 /* If the abort instruction failed, exit. */
679 _exit (127);
680
681 /* If even this fails, make sure we never return. */
682 while (1)
683 /* Try for ever and ever. */
684 ABORT_INSTRUCTION;
685 }
686 \f
687 /* This function is called by interruptible RPC stubs. For initial
688 dynamic linking, just use the normal mach_msg. Since this defn is
689 weak, the real defn in libc.so will override it if we are linked into
690 the user program (-ldl). */
691
692 error_t weak_function
693 _hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
694 mach_msg_option_t option,
695 mach_msg_size_t send_size,
696 mach_msg_size_t rcv_size,
697 mach_port_t rcv_name,
698 mach_msg_timeout_t timeout,
699 mach_port_t notify)
700 {
701 return __mach_msg (msg, option, send_size, rcv_size, rcv_name,
702 timeout, notify);
703 }
704
705
706 void
707 internal_function
708 _dl_show_auxv (void)
709 {
710 /* There is nothing to print. Hurd has no auxiliary vector. */
711 }
712
713
714 /* Return an array of useful/necessary hardware capability names. */
715 const struct r_strlenpair *
716 internal_function
717 _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
718 size_t *max_capstrlen)
719 {
720 struct r_strlenpair *result;
721
722 /* Return an empty array. Hurd has no hardware capabilities. */
723 result = (struct r_strlenpair *) malloc (sizeof (*result));
724 if (result == NULL)
725 _dl_signal_error (ENOMEM, NULL, "cannot create capability list");
726
727 result[0].str = (char *) result; /* Does not really matter. */
728 result[0].len = 0;
729
730 *sz = 1;
731 return result;
732 }