load_elf.c revision 83321
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/boot/common/load_elf.c 83321 2001-09-11 01:09:24Z peter $
28 */
29
30#include <sys/param.h>
31#include <sys/exec.h>
32#include <sys/reboot.h>
33#include <sys/linker.h>
34#include <sys/module.h>
35#include <string.h>
36#include <machine/bootinfo.h>
37#include <machine/elf.h>
38#include <stand.h>
39#define FREEBSD_ELF
40#include <link.h>
41
42#include "bootstrap.h"
43
44#define COPYOUT(s,d,l)	archsw.arch_copyout((vm_offset_t)(s), d, l)
45
46
47typedef struct elf_file {
48    Elf_Phdr 	*ph;
49    Elf_Ehdr	*ehdr;
50    Elf_Sym	*symtab;
51    Elf_Off	*hashtab;
52    Elf_Off	nbuckets;
53    Elf_Off	nchains;
54    Elf_Off*	buckets;
55    Elf_Off*	chains;
56    char	*strtab;
57    size_t	strsz;
58    int		fd;
59    caddr_t	firstpage;
60    size_t	firstlen;
61    int		kernel;
62    vm_offset_t	off;
63} *elf_file_t;
64
65static int elf_loadimage(struct preloaded_file *mp, elf_file_t ef, vm_offset_t loadaddr);
66static int elf_lookup_symbol(struct preloaded_file *mp, elf_file_t ef, const char* name,	Elf_Sym* sym);
67static int elf_parse_modmetadata(struct preloaded_file *mp, elf_file_t ef);
68static char	*fake_modname(const char *name);
69
70const char	*elf_kerneltype = "elf kernel";
71const char	*elf_moduletype = "elf module";
72
73/*
74 * Attempt to load the file (file) as an ELF module.  It will be stored at
75 * (dest), and a pointer to a module structure describing the loaded object
76 * will be saved in (result).
77 */
78int
79elf_loadfile(char *filename, vm_offset_t dest, struct preloaded_file **result)
80{
81    struct preloaded_file	*fp, *kfp;
82    struct elf_file		ef;
83    Elf_Ehdr 			*ehdr;
84    int				err;
85    u_int			pad;
86    ssize_t			bytes_read;
87
88    fp = NULL;
89    bzero(&ef, sizeof(struct elf_file));
90
91    /*
92     * Open the image, read and validate the ELF header
93     */
94    if (filename == NULL)	/* can't handle nameless */
95	return(EFTYPE);
96    if ((ef.fd = open(filename, O_RDONLY)) == -1)
97	return(errno);
98    ef.firstpage = malloc(PAGE_SIZE);
99    if (ef.firstpage == NULL) {
100	close(ef.fd);
101	return(ENOMEM);
102    }
103    bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE);
104    ef.firstlen = (size_t)bytes_read;
105    if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) {
106	err = EFTYPE;		/* could be EIO, but may be small file */
107	goto oerr;
108    }
109    ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage;
110
111    /* Is it ELF? */
112    if (!IS_ELF(*ehdr)) {
113	err = EFTYPE;
114	goto oerr;
115    }
116    if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||	/* Layout ? */
117	ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
118	ehdr->e_ident[EI_VERSION] != EV_CURRENT ||	/* Version ? */
119	ehdr->e_version != EV_CURRENT ||
120	ehdr->e_machine != ELF_TARG_MACH) {		/* Machine ? */
121	err = EFTYPE;
122	goto oerr;
123    }
124
125
126    /*
127     * Check to see what sort of module we are.
128     */
129    kfp = file_findfile(NULL, NULL);
130    if (ehdr->e_type == ET_DYN) {
131	/* Looks like a kld module */
132	if (kfp == NULL) {
133	    printf("elf_loadfile: can't load module before kernel\n");
134	    err = EPERM;
135	    goto oerr;
136	}
137	if (strcmp(elf_kerneltype, kfp->f_type)) {
138	    printf("elf_loadfile: can't load module with kernel type '%s'\n", kfp->f_type);
139	    err = EPERM;
140	    goto oerr;
141	}
142	/* Looks OK, got ahead */
143	ef.kernel = 0;
144
145	/* Page-align the load address */
146	pad = (u_int)dest & PAGE_MASK;
147	if (pad != 0) {
148	    pad = PAGE_SIZE - pad;
149	    dest += pad;
150	}
151    } else if (ehdr->e_type == ET_EXEC) {
152	/* Looks like a kernel */
153	if (kfp != NULL) {
154	    printf("elf_loadfile: kernel already loaded\n");
155	    err = EPERM;
156	    goto oerr;
157	}
158	/*
159	 * Calculate destination address based on kernel entrypoint
160	 */
161	dest = (vm_offset_t) ehdr->e_entry;
162	if (dest == 0) {
163	    printf("elf_loadfile: not a kernel (maybe static binary?)\n");
164	    err = EPERM;
165	    goto oerr;
166	}
167	ef.kernel = 1;
168
169    } else {
170	err = EFTYPE;
171	goto oerr;
172    }
173
174    /*
175     * Ok, we think we should handle this.
176     */
177    fp = file_alloc();
178    if (fp == NULL) {
179	    printf("elf_loadfile: cannot allocate module info\n");
180	    err = EPERM;
181	    goto out;
182    }
183    if (ef.kernel)
184	setenv("kernelname", filename, 1);
185    fp->f_name = strdup(filename);
186    fp->f_type = strdup(ef.kernel ? elf_kerneltype : elf_moduletype);
187
188#ifdef ELF_VERBOSE
189    if (ef.kernel)
190	printf("%s entry at %p\n", filename, (void *) dest);
191#else
192    printf("%s ", filename);
193#endif
194
195    fp->f_size = elf_loadimage(fp, &ef, dest);
196    if (fp->f_size == 0 || fp->f_addr == 0)
197	goto ioerr;
198
199    /* save exec header as metadata */
200    file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr);
201
202    /* Load OK, return module pointer */
203    *result = (struct preloaded_file *)fp;
204    err = 0;
205    goto out;
206
207 ioerr:
208    err = EIO;
209 oerr:
210    file_discard(fp);
211 out:
212    if (ef.firstpage)
213	free(ef.firstpage);
214    close(ef.fd);
215    return(err);
216}
217
218/*
219 * With the file (fd) open on the image, and (ehdr) containing
220 * the Elf header, load the image at (off)
221 */
222static int
223elf_loadimage(struct preloaded_file *fp, elf_file_t ef, vm_offset_t off)
224{
225    int 	i;
226    u_int	j;
227    Elf_Ehdr	*ehdr;
228    Elf_Phdr	*phdr, *php;
229    Elf_Shdr	*shdr;
230    int		ret;
231    vm_offset_t firstaddr;
232    vm_offset_t lastaddr;
233    void	*buf;
234    size_t	resid, chunk;
235    ssize_t	result;
236    vm_offset_t	dest;
237    vm_offset_t	ssym, esym;
238    Elf_Dyn	*dp;
239    int		ndp;
240    int		symstrindex;
241    int		symtabindex;
242    long	size;
243    u_int	fpcopy;
244
245    dp = NULL;
246    shdr = NULL;
247    ret = 0;
248    firstaddr = lastaddr = 0;
249    ehdr = ef->ehdr;
250    if (ef->kernel) {
251#ifdef __i386__
252	off = - (off & 0xff000000u);	/* i386 relocates after locore */
253#else
254	off = 0;		/* alpha is direct mapped for kernels */
255#endif
256    }
257    ef->off = off;
258
259    if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) {
260	printf("elf_loadimage: program header not within first page\n");
261	goto out;
262    }
263    phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff);
264
265    for (i = 0; i < ehdr->e_phnum; i++) {
266	/* We want to load PT_LOAD segments only.. */
267	if (phdr[i].p_type != PT_LOAD)
268	    continue;
269
270#ifdef ELF_VERBOSE
271	printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
272	    (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
273	    (long)(phdr[i].p_vaddr + off),
274	    (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
275#else
276	if ((phdr[i].p_flags & PF_W) == 0) {
277	    printf("text=0x%lx ", (long)phdr[i].p_filesz);
278	} else {
279	    printf("data=0x%lx", (long)phdr[i].p_filesz);
280	    if (phdr[i].p_filesz < phdr[i].p_memsz)
281		printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
282	    printf(" ");
283	}
284#endif
285	fpcopy = 0;
286	if (ef->firstlen > phdr[i].p_offset) {
287	    fpcopy = ef->firstlen - phdr[i].p_offset;
288	    archsw.arch_copyin(ef->firstpage + phdr[i].p_offset,
289			       phdr[i].p_vaddr + off, fpcopy);
290	}
291	if (phdr[i].p_filesz > fpcopy) {
292	    if (lseek(ef->fd, (off_t)(phdr[i].p_offset + fpcopy),
293		      SEEK_SET) == -1) {
294		printf("\nelf_loadexec: cannot seek\n");
295		goto out;
296	    }
297	    if (archsw.arch_readin(ef->fd, phdr[i].p_vaddr + off + fpcopy,
298		phdr[i].p_filesz - fpcopy) != (ssize_t)(phdr[i].p_filesz - fpcopy)) {
299		printf("\nelf_loadexec: archsw.readin failed\n");
300		goto out;
301	    }
302	}
303	/* clear space from oversized segments; eg: bss */
304	if (phdr[i].p_filesz < phdr[i].p_memsz) {
305#ifdef ELF_VERBOSE
306	    printf(" (bss: 0x%lx-0x%lx)",
307		(long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
308		(long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
309#endif
310
311	    /* no archsw.arch_bzero */
312	    buf = malloc(PAGE_SIZE);
313	    if (buf == NULL) {
314		printf("\nelf_loadimage: malloc() failed\n");
315		goto out;
316	    }
317	    bzero(buf, PAGE_SIZE);
318	    resid = phdr[i].p_memsz - phdr[i].p_filesz;
319	    dest = phdr[i].p_vaddr + off + phdr[i].p_filesz;
320	    while (resid > 0) {
321		chunk = min(PAGE_SIZE, resid);
322		archsw.arch_copyin(buf, dest, chunk);
323		resid -= chunk;
324		dest += chunk;
325	    }
326	    free(buf);
327	}
328#ifdef ELF_VERBOSE
329	printf("\n");
330#endif
331
332	if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
333	    firstaddr = phdr[i].p_vaddr + off;
334	if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
335	    lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
336    }
337    lastaddr = roundup(lastaddr, sizeof(long));
338
339    /*
340     * Now grab the symbol tables.  This isn't easy if we're reading a
341     * .gz file.  I think the rule is going to have to be that you must
342     * strip a file to remove symbols before gzipping it so that we do not
343     * try to lseek() on it.
344     */
345    chunk = ehdr->e_shnum * ehdr->e_shentsize;
346    if (chunk == 0 || ehdr->e_shoff == 0)
347	goto nosyms;
348    shdr = malloc(chunk);
349    if (shdr == NULL)
350	goto nosyms;
351    if (lseek(ef->fd, (off_t)ehdr->e_shoff, SEEK_SET) == -1) {
352	printf("\nelf_loadimage: cannot lseek() to section headers");
353	goto nosyms;
354    }
355    result = read(ef->fd, shdr, chunk);
356    if (result < 0 || (size_t)result != chunk) {
357	printf("\nelf_loadimage: read section headers failed");
358	goto nosyms;
359    }
360    symtabindex = -1;
361    symstrindex = -1;
362    for (i = 0; i < ehdr->e_shnum; i++) {
363	if (shdr[i].sh_type != SHT_SYMTAB)
364	    continue;
365	for (j = 0; j < ehdr->e_phnum; j++) {
366	    if (phdr[j].p_type != PT_LOAD)
367		continue;
368	    if (shdr[i].sh_offset >= phdr[j].p_offset &&
369		(shdr[i].sh_offset + shdr[i].sh_size <=
370		 phdr[j].p_offset + phdr[j].p_filesz)) {
371		shdr[i].sh_offset = 0;
372		shdr[i].sh_size = 0;
373		break;
374	    }
375	}
376	if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
377	    continue;		/* alread loaded in a PT_LOAD above */
378	/* Save it for loading below */
379	symtabindex = i;
380	symstrindex = shdr[i].sh_link;
381    }
382    if (symtabindex < 0 || symstrindex < 0)
383	goto nosyms;
384
385    /* Ok, committed to a load. */
386#ifndef ELF_VERBOSE
387    printf("syms=[");
388#endif
389    ssym = lastaddr;
390    for (i = symtabindex; i >= 0; i = symstrindex) {
391#ifdef ELF_VERBOSE
392	char	*secname;
393
394	switch(shdr[i].sh_type) {
395	    case SHT_SYMTAB:		/* Symbol table */
396		secname = "symtab";
397		break;
398	    case SHT_STRTAB:		/* String table */
399		secname = "strtab";
400		break;
401	    default:
402		secname = "WHOA!!";
403		break;
404	}
405#endif
406
407	size = shdr[i].sh_size;
408	archsw.arch_copyin(&size, lastaddr, sizeof(size));
409	lastaddr += sizeof(long);
410
411#ifdef ELF_VERBOSE
412	printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname,
413	    shdr[i].sh_size, shdr[i].sh_offset,
414	    lastaddr, lastaddr + shdr[i].sh_size);
415#else
416	if (i == symstrindex)
417	    printf("+");
418	printf("0x%lx+0x%lx", (long)sizeof(size), size);
419#endif
420
421	if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) {
422	    printf("\nelf_loadimage: could not seek for symbols - skipped!");
423	    lastaddr = ssym;
424	    ssym = 0;
425	    goto nosyms;
426	}
427	result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size);
428	if (result < 0 || (size_t)result != shdr[i].sh_size) {
429	    printf("\nelf_loadimage: could not read symbols - skipped!");
430	    lastaddr = ssym;
431	    ssym = 0;
432	    goto nosyms;
433	}
434	/* Reset offsets relative to ssym */
435	lastaddr += shdr[i].sh_size;
436	lastaddr = roundup(lastaddr, sizeof(long));
437	if (i == symtabindex)
438	    symtabindex = -1;
439	else if (i == symstrindex)
440	    symstrindex = -1;
441    }
442    esym = lastaddr;
443#ifndef ELF_VERBOSE
444    printf("]");
445#endif
446
447    file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
448    file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);
449
450nosyms:
451    printf("\n");
452
453    ret = lastaddr - firstaddr;
454    fp->f_addr = firstaddr;
455
456    php = NULL;
457    for (i = 0; i < ehdr->e_phnum; i++) {
458	if (phdr[i].p_type == PT_DYNAMIC) {
459	    php = phdr + i;
460	    dp = (Elf_Dyn *)(php->p_vaddr);
461	    file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(dp), &dp);
462	    dp = NULL;
463	    break;
464	}
465    }
466
467    if (php == NULL)	/* this is bad, we cannot get to symbols or _DYNAMIC */
468	goto out;
469
470    ndp = php->p_filesz / sizeof(Elf_Dyn);
471    if (ndp == 0)
472	goto out;
473    dp = malloc(php->p_filesz);
474    if (dp == NULL)
475	goto out;
476    archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz);
477
478    ef->strsz = 0;
479    for (i = 0; i < ndp; i++) {
480	if (dp[i].d_tag == NULL)
481	    break;
482	switch (dp[i].d_tag) {
483	case DT_HASH:
484	    ef->hashtab = (Elf_Off*)(dp[i].d_un.d_ptr + off);
485	    break;
486	case DT_STRTAB:
487	    ef->strtab = (char *)(dp[i].d_un.d_ptr + off);
488	    break;
489	case DT_STRSZ:
490	    ef->strsz = dp[i].d_un.d_val;
491	    break;
492	case DT_SYMTAB:
493	    ef->symtab = (Elf_Sym*)(dp[i].d_un.d_ptr + off);
494	    break;
495	default:
496	    break;
497	}
498    }
499    if (ef->hashtab == NULL || ef->symtab == NULL ||
500	ef->strtab == NULL || ef->strsz == 0)
501	goto out;
502    COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets));
503    COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains));
504    ef->buckets = ef->hashtab + 2;
505    ef->chains = ef->buckets + ef->nbuckets;
506    if (elf_parse_modmetadata(fp, ef) == 0)
507	goto out;
508
509    if (ef->kernel)			/* kernel must not depend on anything */
510	goto out;
511
512out:
513    if (dp)
514	free(dp);
515    if (shdr)
516	free(shdr);
517    return ret;
518}
519
520static char invalid_name[] = "bad";
521
522char *
523fake_modname(const char *name)
524{
525    const char *sp, *ep;
526    char *fp;
527    size_t len;
528
529    sp = strrchr(name, '/');
530    if (sp)
531	sp++;
532    else
533	sp = name;
534    ep = strrchr(name, '.');
535    if (ep) {
536	    if (ep == name) {
537		sp = invalid_name;
538		ep = invalid_name + sizeof(invalid_name) - 1;
539	    }
540    } else
541	ep = name + strlen(name);
542    len = ep - sp;
543    fp = malloc(len + 1);
544    if (fp == NULL)
545	return NULL;
546    memcpy(fp, sp, len);
547    fp[len] = '\0';
548    return fp;
549}
550
551int
552elf_parse_modmetadata(struct preloaded_file *fp, elf_file_t ef)
553{
554    struct mod_metadata md;
555    struct mod_depend *mdepend;
556    struct mod_version mver;
557    Elf_Sym sym;
558    char *s, *v, **p, **p_stop;
559    int modcnt, minfolen;
560
561    if (elf_lookup_symbol(fp, ef, "__start_set_modmetadata_set", &sym) != 0)
562	return ENOENT;
563    p = (char **)(sym.st_value + ef->off);
564    if (elf_lookup_symbol(fp, ef, "__stop_set_modmetadata_set", &sym) != 0)
565	return ENOENT;
566    p_stop = (char **)(sym.st_value + ef->off);
567
568    modcnt = 0;
569    while (p < p_stop) {
570	COPYOUT(p++, &v, sizeof(v));
571	COPYOUT(v + ef->off, &md, sizeof(md));
572	switch(md.md_type) {
573	  case MDT_DEPEND:
574	    if (ef->kernel)		/* kernel must not depend on anything */
575	      break;
576	    s = strdupout((vm_offset_t)(md.md_cval + ef->off));
577	    minfolen = sizeof(*mdepend) + strlen(s) + 1;
578	    mdepend = malloc(minfolen);
579	    if (mdepend == NULL)
580		return ENOMEM;
581	    COPYOUT((vm_offset_t)(md.md_data + ef->off), mdepend, sizeof(*mdepend));
582	    strcpy((char*)(mdepend + 1), s);
583	    free(s);
584	    file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend);
585	    free(mdepend);
586	    break;
587	  case MDT_VERSION:
588	    s = strdupout((vm_offset_t)(md.md_cval + ef->off));
589	    COPYOUT((vm_offset_t)(md.md_data + ef->off), &mver, sizeof(mver));
590	    file_addmodule(fp, s, mver.mv_version, NULL);
591	    free(s);
592	    modcnt++;
593	    break;
594	}
595    }
596    if (modcnt == 0) {
597	s = fake_modname(fp->f_name);
598	file_addmodule(fp, s, 1, NULL);
599	free(s);
600    }
601    return 0;
602}
603
604static unsigned long
605elf_hash(const char *name)
606{
607    const unsigned char *p = (const unsigned char *) name;
608    unsigned long h = 0;
609    unsigned long g;
610
611    while (*p != '\0') {
612	h = (h << 4) + *p++;
613	if ((g = h & 0xf0000000) != 0)
614	    h ^= g >> 24;
615	h &= ~g;
616    }
617    return h;
618}
619
620static const char elf_bad_symtable[] = "elf_lookup_symbol: corrupt symbol table\n";
621int
622elf_lookup_symbol(struct preloaded_file *fp, elf_file_t ef, const char* name,
623		  Elf_Sym *symp)
624{
625    unsigned long symnum;
626    Elf_Sym sym;
627    char *strp;
628    unsigned long hash;
629
630    hash = elf_hash(name);
631    COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum));
632
633    while (symnum != STN_UNDEF) {
634	if (symnum >= ef->nchains) {
635	    printf(elf_bad_symtable);
636	    return ENOENT;
637	}
638
639	COPYOUT(ef->symtab + symnum, &sym, sizeof(sym));
640	if (sym.st_name == 0) {
641	    printf(elf_bad_symtable);
642	    return ENOENT;
643	}
644
645	strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name));
646	if (strcmp(name, strp) == 0) {
647	    free(strp);
648	    if (sym.st_shndx != SHN_UNDEF ||
649		(sym.st_value != 0 &&
650		 ELF_ST_TYPE(sym.st_info) == STT_FUNC)) {
651		*symp = sym;
652		return 0;
653	    }
654	    return ENOENT;
655	}
656	free(strp);
657	COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum));
658    }
659    return ENOENT;
660}
661