link_elf.c revision 59751
138514Sdfr/*-
259603Sdfr * Copyright (c) 1998-2000 Doug Rabson
338514Sdfr * All rights reserved.
438514Sdfr *
538514Sdfr * Redistribution and use in source and binary forms, with or without
638514Sdfr * modification, are permitted provided that the following conditions
738514Sdfr * are met:
838514Sdfr * 1. Redistributions of source code must retain the above copyright
938514Sdfr *    notice, this list of conditions and the following disclaimer.
1038514Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1138514Sdfr *    notice, this list of conditions and the following disclaimer in the
1238514Sdfr *    documentation and/or other materials provided with the distribution.
1338514Sdfr *
1438514Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538514Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638514Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738514Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838514Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938514Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038514Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138514Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238514Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338514Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438514Sdfr * SUCH DAMAGE.
2538514Sdfr *
2650477Speter * $FreeBSD: head/sys/kern/link_elf.c 59751 2000-04-29 13:19:31Z peter $
2738514Sdfr */
2838514Sdfr
2959603Sdfr#include "opt_ddb.h"
3059603Sdfr
3138514Sdfr#include <sys/param.h>
3238514Sdfr#include <sys/kernel.h>
3338514Sdfr#include <sys/systm.h>
3438514Sdfr#include <sys/malloc.h>
3538514Sdfr#include <sys/proc.h>
3638514Sdfr#include <sys/namei.h>
3738514Sdfr#include <sys/fcntl.h>
3838514Sdfr#include <sys/vnode.h>
3938514Sdfr#include <sys/linker.h>
4038514Sdfr#include <machine/elf.h>
4138514Sdfr
4239071Sdfr#include <vm/vm.h>
4339071Sdfr#include <vm/vm_param.h>
4454655Seivind#include <vm/vm_zone.h>
4539071Sdfr#include <sys/lock.h>
4652128Speter#ifdef SPARSE_MAPPING
4739071Sdfr#include <vm/vm_object.h>
4839071Sdfr#include <vm/vm_kern.h>
4939071Sdfr#include <vm/vm_extern.h>
5052128Speter#endif
5139071Sdfr#include <vm/pmap.h>
5239071Sdfr#include <vm/vm_map.h>
5359603Sdfr#include <link.h>
5439071Sdfr
5559603Sdfr#include "linker_if.h"
5638514Sdfr
5738514Sdfrtypedef struct elf_file {
5859603Sdfr    struct linker_file	lf;		/* Common fields */
5959603Sdfr    int			preloaded;	/* Was file pre-loaded */
6039071Sdfr    caddr_t		address;	/* Relocation address */
6139071Sdfr#ifdef SPARSE_MAPPING
6239071Sdfr    vm_object_t		object;		/* VM object to hold file pages */
6339071Sdfr#endif
6459603Sdfr    Elf_Dyn*		dynamic;	/* Symbol table etc. */
6538514Sdfr    Elf_Off		nbuckets;	/* DT_HASH info */
6638514Sdfr    Elf_Off		nchains;
6738514Sdfr    const Elf_Off*	buckets;
6838514Sdfr    const Elf_Off*	chains;
6938514Sdfr    caddr_t		hash;
7038514Sdfr    caddr_t		strtab;		/* DT_STRTAB */
7140254Speter    int			strsz;		/* DT_STRSZ */
7239071Sdfr    const Elf_Sym*	symtab;		/* DT_SYMTAB */
7339071Sdfr    Elf_Addr*		got;		/* DT_PLTGOT */
7439071Sdfr    const Elf_Rel*	pltrel;		/* DT_JMPREL */
7539071Sdfr    int			pltrelsize;	/* DT_PLTRELSZ */
7639071Sdfr    const Elf_Rela*	pltrela;	/* DT_JMPREL */
7739071Sdfr    int			pltrelasize;	/* DT_PLTRELSZ */
7839071Sdfr    const Elf_Rel*	rel;		/* DT_REL */
7939071Sdfr    int			relsize;	/* DT_RELSZ */
8039071Sdfr    const Elf_Rela*	rela;		/* DT_RELA */
8139071Sdfr    int			relasize;	/* DT_RELASZ */
8240254Speter    caddr_t		modptr;
8340254Speter    const Elf_Sym*	ddbsymtab;	/* The symbol table we are using */
8440254Speter    long		ddbsymcnt;	/* Number of symbols */
8540254Speter    caddr_t		ddbstrtab;	/* String table */
8640254Speter    long		ddbstrcnt;	/* number of bytes in string table */
8740292Speter    caddr_t		symbase;	/* malloc'ed symbold base */
8840292Speter    caddr_t		strbase;	/* malloc'ed string base */
8959603Sdfr#ifdef DDB
9059603Sdfr    struct link_map	gdb;		/* hooks for gdb */
9159603Sdfr#endif
9238514Sdfr} *elf_file_t;
9338514Sdfr
9459751Speterstatic int	link_elf_link_preload(linker_class_t cls,
9559751Speter				      const char*, linker_file_t*);
9659751Speterstatic int	link_elf_link_preload_finish(linker_file_t);
9759751Speterstatic int	link_elf_load_file(linker_class_t, const char*, linker_file_t*);
9859603Sdfrstatic int	link_elf_lookup_symbol(linker_file_t, const char*,
9959603Sdfr				       c_linker_sym_t*);
10059603Sdfrstatic int	link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*);
10159603Sdfrstatic int	link_elf_search_symbol(linker_file_t, caddr_t value,
10259603Sdfr				       c_linker_sym_t* sym, long* diffp);
10338514Sdfr
10459603Sdfrstatic void	link_elf_unload_file(linker_file_t);
10559751Speterstatic void	link_elf_unload_preload(linker_file_t);
10659603Sdfr
10759603Sdfrstatic kobj_method_t link_elf_methods[] = {
10859603Sdfr    KOBJMETHOD(linker_lookup_symbol,	link_elf_lookup_symbol),
10959603Sdfr    KOBJMETHOD(linker_symbol_values,	link_elf_symbol_values),
11059603Sdfr    KOBJMETHOD(linker_search_symbol,	link_elf_search_symbol),
11159603Sdfr    KOBJMETHOD(linker_unload,		link_elf_unload_file),
11259751Speter    KOBJMETHOD(linker_load_file,	link_elf_load_file),
11359751Speter    KOBJMETHOD(linker_link_preload,	link_elf_link_preload),
11459751Speter    KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish),
11559603Sdfr    { 0, 0 }
11659603Sdfr};
11759603Sdfr
11859603Sdfrstatic struct linker_class link_elf_class = {
11959603Sdfr#if ELF_TARG_CLASS == ELFCLASS32
12059603Sdfr    "elf32",
12159603Sdfr#else
12259603Sdfr    "elf64",
12359603Sdfr#endif
12459603Sdfr    link_elf_methods, sizeof(struct elf_file)
12559603Sdfr};
12659603Sdfr
12759603Sdfrstatic int		parse_dynamic(elf_file_t ef);
12859603Sdfrstatic int		relocate_file(elf_file_t ef);
12959751Speterstatic int		link_elf_preload_parse_symbols(elf_file_t ef);
13059603Sdfr
13159603Sdfr#ifdef DDB
13259603Sdfr
13338514Sdfr/*
13459603Sdfr * A list of loaded modules for GDB to use for loading symbols.
13559603Sdfr */
13659603Sdfrstruct r_debug r_debug;
13759603Sdfr
13859603Sdfr#define GDB_STATE(s)	r_debug.r_state = s; r_debug_state();
13959603Sdfr
14059603Sdfr/*
14159603Sdfr * Function for the debugger to set a breakpoint on to gain control.
14259603Sdfr */
14359603Sdfrvoid
14459603Sdfrr_debug_state(void)
14559603Sdfr{
14659603Sdfr}
14759603Sdfr
14859603Sdfr#endif
14959603Sdfr
15059603Sdfr/*
15138514Sdfr * The kernel symbol table starts here.
15238514Sdfr */
15338514Sdfrextern struct _dynamic _DYNAMIC;
15438514Sdfr
15538514Sdfrstatic void
15638514Sdfrlink_elf_init(void* arg)
15738514Sdfr{
15840156Speter#ifdef __ELF__
15940156Speter    Elf_Dyn	*dp;
16040156Speter    caddr_t	modptr, baseptr, sizeptr;
16140156Speter    elf_file_t	ef;
16240156Speter    char	*modname;
16340156Speter#endif
16438514Sdfr
16559603Sdfr    linker_add_class(&link_elf_class);
16638514Sdfr
16740156Speter#ifdef __ELF__
16840156Speter    dp = (Elf_Dyn*) &_DYNAMIC;
16938514Sdfr    if (dp) {
17040156Speter	modname = NULL;
17140156Speter	modptr = preload_search_by_type("elf kernel");
17240156Speter	if (modptr)
17340156Speter	    modname = (char *)preload_search_info(modptr, MODINFO_NAME);
17440156Speter	if (modname == NULL)
17540156Speter	    modname = "kernel";
17659603Sdfr	linker_kernel_file = linker_make_file(modname, &link_elf_class);
17738514Sdfr	if (linker_kernel_file == NULL)
17838514Sdfr	    panic("link_elf_init: Can't create linker structures for kernel");
17959603Sdfr
18059603Sdfr	ef = (elf_file_t) linker_kernel_file;
18159751Speter	ef->preloaded = 1;
18259603Sdfr	ef->address = 0;
18359603Sdfr#ifdef SPARSE_MAPPING
18459603Sdfr	ef->object = 0;
18559603Sdfr#endif
18659603Sdfr	ef->dynamic = dp;
18759603Sdfr
18859603Sdfr	parse_dynamic(ef);
18946694Speter	linker_kernel_file->address = (caddr_t) KERNBASE;
19050275Sbde	linker_kernel_file->size = -(intptr_t)linker_kernel_file->address;
19140156Speter
19240156Speter	if (modptr) {
19340254Speter	    ef->modptr = modptr;
19440156Speter	    baseptr = preload_search_info(modptr, MODINFO_ADDR);
19540156Speter	    if (baseptr)
19640156Speter		linker_kernel_file->address = *(caddr_t *)baseptr;
19740156Speter	    sizeptr = preload_search_info(modptr, MODINFO_SIZE);
19840156Speter	    if (sizeptr)
19940156Speter		linker_kernel_file->size = *(size_t *)sizeptr;
20040156Speter	}
20159751Speter	(void)link_elf_preload_parse_symbols(ef);
20259603Sdfr
20359603Sdfr#ifdef DDB
20459603Sdfr	ef->gdb.l_addr = linker_kernel_file->address;
20559603Sdfr	ef->gdb.l_name = modname;
20659603Sdfr	ef->gdb.l_ld = dp;
20759603Sdfr	ef->gdb.l_prev = 0;
20859603Sdfr	ef->gdb.l_next = 0;
20959603Sdfr
21059603Sdfr	r_debug.r_map = &ef->gdb;
21159603Sdfr	r_debug.r_brk = r_debug_state;
21259603Sdfr	r_debug.r_state = RT_CONSISTENT;
21359603Sdfr
21459603Sdfr	r_debug_state();		/* say hello to gdb! */
21559603Sdfr#endif
21659603Sdfr
21738514Sdfr    }
21840156Speter#endif
21938514Sdfr}
22038514Sdfr
22140156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
22238514Sdfr
22338514Sdfrstatic int
22459751Speterlink_elf_preload_parse_symbols(elf_file_t ef)
22540254Speter{
22640254Speter    caddr_t	pointer;
22740254Speter    caddr_t	ssym, esym, base;
22840254Speter    caddr_t	strtab;
22940254Speter    int		strcnt;
23040254Speter    Elf_Sym*	symtab;
23140254Speter    int		symcnt;
23240254Speter
23340292Speter    if (ef->modptr == NULL)
23440292Speter	return 0;
23540254Speter    pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM);
23640254Speter    if (pointer == NULL)
23740254Speter	return 0;
23840254Speter    ssym = *(caddr_t *)pointer;
23940254Speter    pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM);
24040254Speter    if (pointer == NULL)
24140254Speter	return 0;
24240254Speter    esym = *(caddr_t *)pointer;
24340254Speter
24440254Speter    base = ssym;
24540254Speter
24640254Speter    symcnt = *(long *)base;
24740254Speter    base += sizeof(long);
24840254Speter    symtab = (Elf_Sym *)base;
24940254Speter    base += roundup(symcnt, sizeof(long));
25040254Speter
25140254Speter    if (base > esym || base < ssym) {
25240254Speter	printf("Symbols are corrupt!\n");
25340254Speter	return EINVAL;
25440254Speter    }
25540254Speter
25640254Speter    strcnt = *(long *)base;
25740254Speter    base += sizeof(long);
25840254Speter    strtab = base;
25940254Speter    base += roundup(strcnt, sizeof(long));
26040254Speter
26140254Speter    if (base > esym || base < ssym) {
26240254Speter	printf("Symbols are corrupt!\n");
26340254Speter	return EINVAL;
26440254Speter    }
26540254Speter
26640254Speter    ef->ddbsymtab = symtab;
26740254Speter    ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
26840254Speter    ef->ddbstrtab = strtab;
26940254Speter    ef->ddbstrcnt = strcnt;
27040254Speter
27140254Speter    return 0;
27240254Speter}
27340254Speter
27440254Speterstatic int
27559603Sdfrparse_dynamic(elf_file_t ef)
27638514Sdfr{
27759603Sdfr    Elf_Dyn *dp;
27839071Sdfr    int plttype = DT_REL;
27938514Sdfr
28038514Sdfr    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
28138514Sdfr	switch (dp->d_tag) {
28238514Sdfr	case DT_HASH:
28338514Sdfr	{
28438514Sdfr	    /* From src/libexec/rtld-elf/rtld.c */
28538514Sdfr	    const Elf_Off *hashtab = (const Elf_Off *)
28638514Sdfr		(ef->address + dp->d_un.d_ptr);
28738514Sdfr	    ef->nbuckets = hashtab[0];
28838514Sdfr	    ef->nchains = hashtab[1];
28938514Sdfr	    ef->buckets = hashtab + 2;
29038514Sdfr	    ef->chains = ef->buckets + ef->nbuckets;
29138514Sdfr	    break;
29238514Sdfr	}
29338514Sdfr	case DT_STRTAB:
29439071Sdfr	    ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr);
29538514Sdfr	    break;
29640254Speter	case DT_STRSZ:
29740254Speter	    ef->strsz = dp->d_un.d_val;
29840254Speter	    break;
29938514Sdfr	case DT_SYMTAB:
30039071Sdfr	    ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr);
30138514Sdfr	    break;
30238514Sdfr	case DT_SYMENT:
30338514Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Sym))
30438514Sdfr		return ENOEXEC;
30539071Sdfr	    break;
30639071Sdfr	case DT_PLTGOT:
30739071Sdfr	    ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr);
30839071Sdfr	    break;
30939071Sdfr	case DT_REL:
31039071Sdfr	    ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr);
31139071Sdfr	    break;
31239071Sdfr	case DT_RELSZ:
31339071Sdfr	    ef->relsize = dp->d_un.d_val;
31439071Sdfr	    break;
31539071Sdfr	case DT_RELENT:
31639071Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Rel))
31739071Sdfr		return ENOEXEC;
31839071Sdfr	    break;
31939071Sdfr	case DT_JMPREL:
32039071Sdfr	    ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr);
32139071Sdfr	    break;
32239071Sdfr	case DT_PLTRELSZ:
32339071Sdfr	    ef->pltrelsize = dp->d_un.d_val;
32439071Sdfr	    break;
32539071Sdfr	case DT_RELA:
32639071Sdfr	    ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr);
32739071Sdfr	    break;
32839071Sdfr	case DT_RELASZ:
32939071Sdfr	    ef->relasize = dp->d_un.d_val;
33039071Sdfr	    break;
33139071Sdfr	case DT_RELAENT:
33239071Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Rela))
33339071Sdfr		return ENOEXEC;
33439071Sdfr	    break;
33539071Sdfr	case DT_PLTREL:
33639071Sdfr	    plttype = dp->d_un.d_val;
33739071Sdfr	    if (plttype != DT_REL && plttype != DT_RELA)
33839071Sdfr		return ENOEXEC;
33939071Sdfr	    break;
34059603Sdfr#ifdef DDB
34159603Sdfr	case DT_DEBUG:
34259603Sdfr	    dp->d_un.d_ptr = (Elf_Addr) &r_debug;
34359603Sdfr	    break;
34459603Sdfr#endif
34538514Sdfr	}
34638514Sdfr    }
34739071Sdfr
34839071Sdfr    if (plttype == DT_RELA) {
34939071Sdfr	ef->pltrela = (const Elf_Rela *) ef->pltrel;
35039071Sdfr	ef->pltrel = NULL;
35139071Sdfr	ef->pltrelasize = ef->pltrelsize;
35239071Sdfr	ef->pltrelsize = 0;
35339071Sdfr    }
35439071Sdfr
35540254Speter    ef->ddbsymtab = ef->symtab;
35640254Speter    ef->ddbsymcnt = ef->nchains;
35740254Speter    ef->ddbstrtab = ef->strtab;
35840254Speter    ef->ddbstrcnt = ef->strsz;
35940254Speter
36038514Sdfr    return 0;
36138514Sdfr}
36238514Sdfr
36339071Sdfrstatic void
36439071Sdfrlink_elf_error(const char *s)
36539071Sdfr{
36639071Sdfr    printf("kldload: %s\n", s);
36739071Sdfr}
36839071Sdfr
36959603Sdfr#ifdef DDB
37059603Sdfr
37159603Sdfrstatic void
37259603Sdfrlink_elf_add_gdb(struct link_map *l)
37359603Sdfr{
37459603Sdfr    struct link_map *prev;
37559603Sdfr
37659603Sdfr    /*
37759603Sdfr     * Scan to the end of the list.
37859603Sdfr     */
37959603Sdfr    for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next)
38059603Sdfr	;
38159603Sdfr
38259603Sdfr    /* Link in the new entry. */
38359603Sdfr    l->l_prev = prev;
38459603Sdfr    l->l_next = prev->l_next;
38559603Sdfr    prev->l_next = l;
38659603Sdfr}
38759603Sdfr
38859603Sdfrstatic void
38959603Sdfrlink_elf_delete_gdb(struct link_map *l)
39059603Sdfr{
39159603Sdfr    if (l->l_prev == NULL) {
39259603Sdfr	if ((r_debug.r_map = l->l_next) != NULL)
39359603Sdfr	    l->l_next->l_prev = NULL;
39459603Sdfr	return;
39559603Sdfr    }
39659603Sdfr
39759603Sdfr    if ((l->l_prev->l_next = l->l_next) != NULL)
39859603Sdfr	l->l_next->l_prev = l->l_prev;
39959603Sdfr}
40059603Sdfr
40159603Sdfr#endif /* DDB */
40259603Sdfr
40338514Sdfrstatic int
40459751Speterlink_elf_link_preload(linker_class_t cls,
40559751Speter		      const char* filename, linker_file_t *result)
40640156Speter{
40740156Speter    caddr_t		modptr, baseptr, sizeptr, dynptr;
40840156Speter    char		*type;
40940156Speter    elf_file_t		ef;
41040156Speter    linker_file_t	lf;
41140156Speter    int			error;
41240156Speter    vm_offset_t		dp;
41340156Speter
41459751Speter    /* Look to see if we have the file preloaded */
41540156Speter    modptr = preload_search_by_name(filename);
41640156Speter    if (modptr == NULL)
41759751Speter	return ENOENT;
41840156Speter
41940156Speter    type = (char *)preload_search_info(modptr, MODINFO_TYPE);
42040156Speter    baseptr = preload_search_info(modptr, MODINFO_ADDR);
42140156Speter    sizeptr = preload_search_info(modptr, MODINFO_SIZE);
42240156Speter    dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC);
42340156Speter    if (type == NULL || strcmp(type, "elf module") != 0)
42440156Speter	return (EFTYPE);
42540156Speter    if (baseptr == NULL || sizeptr == NULL || dynptr == NULL)
42640156Speter	return (EINVAL);
42740156Speter
42859603Sdfr    lf = linker_make_file(filename, &link_elf_class);
42959603Sdfr    if (lf == NULL) {
43059603Sdfr	return ENOMEM;
43159603Sdfr    }
43259603Sdfr
43359603Sdfr    ef = (elf_file_t) lf;
43459603Sdfr    ef->preloaded = 1;
43540292Speter    ef->modptr = modptr;
43640156Speter    ef->address = *(caddr_t *)baseptr;
43740156Speter#ifdef SPARSE_MAPPING
43840156Speter    ef->object = 0;
43940156Speter#endif
44040156Speter    dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr;
44140156Speter    ef->dynamic = (Elf_Dyn *)dp;
44240156Speter    lf->address = ef->address;
44340156Speter    lf->size = *(size_t *)sizeptr;
44440156Speter
44559603Sdfr    error = parse_dynamic(ef);
44640156Speter    if (error) {
44740156Speter	linker_file_unload(lf);
44840156Speter	return error;
44940156Speter    }
45059751Speter    *result = lf;
45159751Speter    return (0);
45259751Speter}
45359751Speter
45459751Speterstatic int
45559751Speterlink_elf_link_preload_finish(linker_file_t lf)
45659751Speter{
45759751Speter    elf_file_t		ef;
45859751Speter    int error;
45959751Speter
46059751Speter    ef = (elf_file_t) lf;
46159751Speter#if 0	/* this will be more trouble than it's worth for now */
46259751Speter    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
46359751Speter	if (dp->d_tag != DT_NEEDED)
46459751Speter	    continue;
46559751Speter	modname = ef->strtab + dp->d_un.d_val;
46659751Speter	error = linker_load_module(modname, lf);
46759751Speter	if (error)
46859751Speter	    goto out;
46940156Speter    }
47059751Speter#endif
47159603Sdfr    error = relocate_file(ef);
47259751Speter    if (error)
47340156Speter	return error;
47459751Speter    (void)link_elf_preload_parse_symbols(ef);
47559603Sdfr
47659603Sdfr#ifdef DDB
47759603Sdfr    GDB_STATE(RT_ADD);
47859603Sdfr    ef->gdb.l_addr = lf->address;
47959751Speter    ef->gdb.l_name = lf->filename;
48059603Sdfr    ef->gdb.l_ld = ef->dynamic;
48159603Sdfr    link_elf_add_gdb(&ef->gdb);
48259603Sdfr    GDB_STATE(RT_CONSISTENT);
48359603Sdfr#endif
48459603Sdfr
48540156Speter    return (0);
48640156Speter}
48740156Speter
48840156Speterstatic int
48959751Speterlink_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result)
49038514Sdfr{
49138514Sdfr    struct nameidata nd;
49238514Sdfr    struct proc* p = curproc;	/* XXX */
49340962Speter    Elf_Ehdr *hdr;
49440962Speter    caddr_t firstpage;
49539071Sdfr    int nbytes, i;
49639071Sdfr    Elf_Phdr *phdr;
49739071Sdfr    Elf_Phdr *phlimit;
49839071Sdfr    Elf_Phdr *segs[2];
49939071Sdfr    int nsegs;
50039071Sdfr    Elf_Phdr *phdyn;
50139071Sdfr    Elf_Phdr *phphdr;
50239071Sdfr    caddr_t mapbase;
50339071Sdfr    size_t mapsize;
50439071Sdfr    Elf_Off base_offset;
50539071Sdfr    Elf_Addr base_vaddr;
50639071Sdfr    Elf_Addr base_vlimit;
50738514Sdfr    int error = 0;
50838514Sdfr    int resid;
50938514Sdfr    elf_file_t ef;
51038514Sdfr    linker_file_t lf;
51140292Speter    Elf_Shdr *shdr;
51240292Speter    int symtabindex;
51340292Speter    int symstrindex;
51440292Speter    int symcnt;
51540292Speter    int strcnt;
51638514Sdfr
51740292Speter    shdr = NULL;
51840292Speter    lf = NULL;
51940292Speter
52059751Speter    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p);
52138514Sdfr    error = vn_open(&nd, FREAD, 0);
52238514Sdfr    if (error)
52338514Sdfr	return error;
52454655Seivind    NDFREE(&nd, NDF_ONLY_PNBUF);
52538514Sdfr
52638514Sdfr    /*
52739071Sdfr     * Read the elf header from the file.
52838514Sdfr     */
52940962Speter    firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
53040962Speter    if (firstpage == NULL) {
53140962Speter	error = ENOMEM;
53240962Speter	goto out;
53340962Speter    }
53440962Speter    hdr = (Elf_Ehdr *)firstpage;
53540962Speter    error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0,
53638514Sdfr		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
53740962Speter    nbytes = PAGE_SIZE - resid;
53838514Sdfr    if (error)
53938514Sdfr	goto out;
54038514Sdfr
54140962Speter    if (!IS_ELF(*hdr)) {
54239071Sdfr	error = ENOEXEC;
54338514Sdfr	goto out;
54439071Sdfr    }
54538514Sdfr
54640962Speter    if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
54740962Speter      || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
54839071Sdfr	link_elf_error("Unsupported file layout");
54939071Sdfr	error = ENOEXEC;
55039071Sdfr	goto out;
55139071Sdfr    }
55240962Speter    if (hdr->e_ident[EI_VERSION] != EV_CURRENT
55340962Speter      || hdr->e_version != EV_CURRENT) {
55439071Sdfr	link_elf_error("Unsupported file version");
55539071Sdfr	error = ENOEXEC;
55639071Sdfr	goto out;
55739071Sdfr    }
55840962Speter    if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
55939071Sdfr	link_elf_error("Unsupported file type");
56039071Sdfr	error = ENOEXEC;
56139071Sdfr	goto out;
56239071Sdfr    }
56340962Speter    if (hdr->e_machine != ELF_TARG_MACH) {
56439071Sdfr	link_elf_error("Unsupported machine");
56539071Sdfr	error = ENOEXEC;
56639071Sdfr	goto out;
56739071Sdfr    }
56839071Sdfr
56938514Sdfr    /*
57039071Sdfr     * We rely on the program header being in the first page.  This is
57139071Sdfr     * not strictly required by the ABI specification, but it seems to
57239071Sdfr     * always true in practice.  And, it simplifies things considerably.
57338514Sdfr     */
57440962Speter    if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) &&
57540962Speter	  (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) &&
57640962Speter	  (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes)))
57739071Sdfr	link_elf_error("Unreadable program headers");
57839071Sdfr
57938514Sdfr    /*
58039071Sdfr     * Scan the program header entries, and save key information.
58139071Sdfr     *
58239071Sdfr     * We rely on there being exactly two load segments, text and data,
58339071Sdfr     * in that order.
58438514Sdfr     */
58540962Speter    phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff);
58640962Speter    phlimit = phdr + hdr->e_phnum;
58739071Sdfr    nsegs = 0;
58839071Sdfr    phdyn = NULL;
58939071Sdfr    phphdr = NULL;
59039071Sdfr    while (phdr < phlimit) {
59139071Sdfr	switch (phdr->p_type) {
59239071Sdfr
59339071Sdfr	case PT_LOAD:
59439071Sdfr	    if (nsegs == 2) {
59539071Sdfr		link_elf_error("Too many sections");
59639071Sdfr		error = ENOEXEC;
59739071Sdfr		goto out;
59839071Sdfr	    }
59939071Sdfr	    segs[nsegs] = phdr;
60039071Sdfr	    ++nsegs;
60139071Sdfr	    break;
60239071Sdfr
60339071Sdfr	case PT_PHDR:
60439071Sdfr	    phphdr = phdr;
60539071Sdfr	    break;
60639071Sdfr
60739071Sdfr	case PT_DYNAMIC:
60839071Sdfr	    phdyn = phdr;
60939071Sdfr	    break;
61039071Sdfr	}
61139071Sdfr
61239071Sdfr	++phdr;
61339071Sdfr    }
61439071Sdfr    if (phdyn == NULL) {
61539071Sdfr	link_elf_error("Object is not dynamically-linked");
61639071Sdfr	error = ENOEXEC;
61738514Sdfr	goto out;
61839071Sdfr    }
61938514Sdfr
62038514Sdfr    /*
62139071Sdfr     * Allocate the entire address space of the object, to stake out our
62239071Sdfr     * contiguous region, and to establish the base address for relocation.
62338514Sdfr     */
62439071Sdfr    base_offset = trunc_page(segs[0]->p_offset);
62539071Sdfr    base_vaddr = trunc_page(segs[0]->p_vaddr);
62639071Sdfr    base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz);
62739071Sdfr    mapsize = base_vlimit - base_vaddr;
62839071Sdfr
62959603Sdfr    lf = linker_make_file(filename, &link_elf_class);
63059603Sdfr    if (!lf) {
63159603Sdfr	error = ENOMEM;
63259603Sdfr	goto out;
63359603Sdfr    }
63459603Sdfr
63559603Sdfr    ef = (elf_file_t) lf;
63639071Sdfr#ifdef SPARSE_MAPPING
63739071Sdfr    ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT);
63839071Sdfr    if (ef->object == NULL) {
63938514Sdfr	free(ef, M_LINKER);
64039071Sdfr	error = ENOMEM;
64138514Sdfr	goto out;
64238514Sdfr    }
64339071Sdfr    vm_object_reference(ef->object);
64439071Sdfr    ef->address = (caddr_t) vm_map_min(kernel_map);
64539071Sdfr    error = vm_map_find(kernel_map, ef->object, 0,
64639071Sdfr			(vm_offset_t *) &ef->address,
64739071Sdfr			mapsize, 1,
64839071Sdfr			VM_PROT_ALL, VM_PROT_ALL, 0);
64939071Sdfr    if (error) {
65039071Sdfr	vm_object_deallocate(ef->object);
65159603Sdfr	ef->object = 0;
65239071Sdfr	goto out;
65339071Sdfr    }
65439071Sdfr#else
65539071Sdfr    ef->address = malloc(mapsize, M_LINKER, M_WAITOK);
65659603Sdfr    if (!ef->address) {
65759603Sdfr	error = ENOMEM;
65859603Sdfr	goto out;
65959603Sdfr    }
66039071Sdfr#endif
66139071Sdfr    mapbase = ef->address;
66238514Sdfr
66339071Sdfr    /*
66439071Sdfr     * Read the text and data sections and zero the bss.
66539071Sdfr     */
66639071Sdfr    for (i = 0; i < 2; i++) {
66739071Sdfr	caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
66839071Sdfr	error = vn_rdwr(UIO_READ, nd.ni_vp,
66939071Sdfr			segbase, segs[i]->p_filesz, segs[i]->p_offset,
67039071Sdfr			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
67139071Sdfr	if (error) {
67239071Sdfr	    goto out;
67339071Sdfr	}
67439071Sdfr	bzero(segbase + segs[i]->p_filesz,
67539071Sdfr	      segs[i]->p_memsz - segs[i]->p_filesz);
67639071Sdfr
67739071Sdfr#ifdef SPARSE_MAPPING
67839071Sdfr	/*
67939071Sdfr	 * Wire down the pages
68039071Sdfr	 */
68139071Sdfr	vm_map_pageable(kernel_map,
68239071Sdfr			(vm_offset_t) segbase,
68339071Sdfr			(vm_offset_t) segbase + segs[i]->p_memsz,
68439071Sdfr			FALSE);
68539071Sdfr#endif
68639071Sdfr    }
68739071Sdfr
68859603Sdfr    ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
68939071Sdfr
69038514Sdfr    lf->address = ef->address;
69139071Sdfr    lf->size = mapsize;
69238514Sdfr
69359603Sdfr    error = parse_dynamic(ef);
69440292Speter    if (error)
69538514Sdfr	goto out;
69659751Speter    error = linker_load_dependancies(lf);
69740292Speter    if (error)
69840156Speter	goto out;
69959751Speter#if 0	/* this will be more trouble than it's worth for now */
70059751Speter    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
70159751Speter	if (dp->d_tag != DT_NEEDED)
70259751Speter	    continue;
70359751Speter	modname = ef->strtab + dp->d_un.d_val;
70459751Speter	error = linker_load_module(modname, lf);
70559751Speter	if (error)
70659751Speter	    goto out;
70759751Speter    }
70859751Speter#endif
70959603Sdfr    error = relocate_file(ef);
71040292Speter    if (error)
71140156Speter	goto out;
71240292Speter
71340292Speter    /* Try and load the symbol table if it's present.  (you can strip it!) */
71440962Speter    nbytes = hdr->e_shnum * hdr->e_shentsize;
71540962Speter    if (nbytes == 0 || hdr->e_shoff == 0)
71640292Speter	goto nosyms;
71740292Speter    shdr = malloc(nbytes, M_LINKER, M_WAITOK);
71840292Speter    if (shdr == NULL) {
71940292Speter	error = ENOMEM;
72040292Speter	goto out;
72140156Speter    }
72240397Speter    bzero(shdr, nbytes);
72340292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
72440962Speter		    (caddr_t)shdr, nbytes, hdr->e_shoff,
72540292Speter		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
72640292Speter    if (error)
72740292Speter	goto out;
72840292Speter    symtabindex = -1;
72940292Speter    symstrindex = -1;
73040962Speter    for (i = 0; i < hdr->e_shnum; i++) {
73140292Speter	if (shdr[i].sh_type == SHT_SYMTAB) {
73240292Speter	    symtabindex = i;
73340292Speter	    symstrindex = shdr[i].sh_link;
73440292Speter	}
73540292Speter    }
73640292Speter    if (symtabindex < 0 || symstrindex < 0)
73740292Speter	goto nosyms;
73840156Speter
73940292Speter    symcnt = shdr[symtabindex].sh_size;
74040292Speter    ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK);
74140292Speter    strcnt = shdr[symstrindex].sh_size;
74240292Speter    ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK);
74340292Speter
74440292Speter    if (ef->symbase == NULL || ef->strbase == NULL) {
74540292Speter	error = ENOMEM;
74640292Speter	goto out;
74740292Speter    }
74840292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
74940292Speter		    ef->symbase, symcnt, shdr[symtabindex].sh_offset,
75040292Speter		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
75140292Speter    if (error)
75240292Speter	goto out;
75340292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
75440292Speter		    ef->strbase, strcnt, shdr[symstrindex].sh_offset,
75540292Speter		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
75640292Speter    if (error)
75740292Speter	goto out;
75840292Speter
75940292Speter    ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
76040292Speter    ef->ddbsymtab = (const Elf_Sym *)ef->symbase;
76140292Speter    ef->ddbstrcnt = strcnt;
76240292Speter    ef->ddbstrtab = ef->strbase;
76340292Speter
76459603Sdfr#ifdef DDB
76559603Sdfr    GDB_STATE(RT_ADD);
76659603Sdfr    ef->gdb.l_addr = lf->address;
76759751Speter    ef->gdb.l_name = filename;
76859603Sdfr    ef->gdb.l_ld = ef->dynamic;
76959603Sdfr    link_elf_add_gdb(&ef->gdb);
77059603Sdfr    GDB_STATE(RT_CONSISTENT);
77159603Sdfr#endif
77259603Sdfr
77340292Speternosyms:
77440292Speter
77538514Sdfr    *result = lf;
77638514Sdfr
77738514Sdfrout:
77840292Speter    if (error && lf)
77940292Speter	linker_file_unload(lf);
78040292Speter    if (shdr)
78140292Speter	free(shdr, M_LINKER);
78240962Speter    if (firstpage)
78340962Speter	free(firstpage, M_LINKER);
78438514Sdfr    VOP_UNLOCK(nd.ni_vp, 0, p);
78538514Sdfr    vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
78638514Sdfr
78738514Sdfr    return error;
78838514Sdfr}
78938514Sdfr
79038514Sdfrstatic void
79140156Speterlink_elf_unload_file(linker_file_t file)
79238514Sdfr{
79359603Sdfr    elf_file_t ef = (elf_file_t) file;
79438514Sdfr
79559603Sdfr#ifdef DDB
79659603Sdfr    if (ef->gdb.l_ld) {
79759603Sdfr	GDB_STATE(RT_DELETE);
79859603Sdfr	link_elf_delete_gdb(&ef->gdb);
79959603Sdfr	GDB_STATE(RT_CONSISTENT);
80059603Sdfr    }
80159603Sdfr#endif
80259603Sdfr
80359603Sdfr    if (ef->preloaded) {
80459751Speter	link_elf_unload_preload(file);
80559603Sdfr	return;
80659603Sdfr    }
80739071Sdfr#ifdef SPARSE_MAPPING
80859603Sdfr    if (ef->object) {
80959603Sdfr	vm_map_remove(kernel_map, (vm_offset_t) ef->address,
81059603Sdfr		      (vm_offset_t) ef->address
81159603Sdfr		      + (ef->object->size << PAGE_SHIFT));
81259603Sdfr	vm_object_deallocate(ef->object);
81359603Sdfr    }
81439071Sdfr#else
81559603Sdfr    if (ef->address)
81659603Sdfr	free(ef->address, M_LINKER);
81739071Sdfr#endif
81859603Sdfr    if (ef->symbase)
81959603Sdfr	free(ef->symbase, M_LINKER);
82059603Sdfr    if (ef->strbase)
82159603Sdfr	free(ef->strbase, M_LINKER);
82238514Sdfr}
82338514Sdfr
82440156Speterstatic void
82559751Speterlink_elf_unload_preload(linker_file_t file)
82640156Speter{
82740156Speter    if (file->filename)
82840156Speter	preload_delete_name(file->filename);
82940156Speter}
83040156Speter
83139071Sdfrstatic const char *
83240435Spetersymbol_name(elf_file_t ef, Elf_Word r_info)
83338514Sdfr{
83439071Sdfr    const Elf_Sym *ref;
83538514Sdfr
83640435Speter    if (ELF_R_SYM(r_info)) {
83740435Speter	ref = ef->symtab + ELF_R_SYM(r_info);
83840397Speter	return ef->strtab + ref->st_name;
83939071Sdfr    } else
84039071Sdfr	return NULL;
84138514Sdfr}
84238514Sdfr
84338514Sdfrstatic int
84459603Sdfrrelocate_file(elf_file_t ef)
84538514Sdfr{
84639071Sdfr    const Elf_Rel *rellim;
84739071Sdfr    const Elf_Rel *rel;
84839071Sdfr    const Elf_Rela *relalim;
84939071Sdfr    const Elf_Rela *rela;
85040435Speter    const char *symname;
85138514Sdfr
85239071Sdfr    /* Perform relocations without addend if there are any: */
85340435Speter    rel = ef->rel;
85440435Speter    if (rel) {
85543388Sbde	rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize);
85640435Speter	while (rel < rellim) {
85740435Speter	    symname = symbol_name(ef, rel->r_info);
85859603Sdfr	    if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) {
85959744Speter		printf("link_elf: symbol %s undefined\n", symname);
86040435Speter		return ENOENT;
86142200Speter	    }
86240435Speter	    rel++;
86340435Speter	}
86439071Sdfr    }
86538514Sdfr
86639071Sdfr    /* Perform relocations with addend if there are any: */
86740435Speter    rela = ef->rela;
86840435Speter    if (rela) {
86943388Sbde	relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize);
87040435Speter	while (rela < relalim) {
87140435Speter	    symname = symbol_name(ef, rela->r_info);
87259603Sdfr	    if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) {
87359744Speter		printf("link_elf: symbol %s undefined\n", symname);
87440435Speter		return ENOENT;
87542200Speter	    }
87640435Speter	    rela++;
87740435Speter	}
87839071Sdfr    }
87938514Sdfr
88039071Sdfr    /* Perform PLT relocations without addend if there are any: */
88140435Speter    rel = ef->pltrel;
88240435Speter    if (rel) {
88343388Sbde	rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize);
88440435Speter	while (rel < rellim) {
88540435Speter	    symname = symbol_name(ef, rel->r_info);
88659603Sdfr	    if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) {
88759744Speter		printf("link_elf: symbol %s undefined\n", symname);
88840435Speter		return ENOENT;
88942200Speter	    }
89040435Speter	    rel++;
89140435Speter	}
89239071Sdfr    }
89338514Sdfr
89439071Sdfr    /* Perform relocations with addend if there are any: */
89540435Speter    rela = ef->pltrela;
89640435Speter    if (rela) {
89743388Sbde	relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize);
89840435Speter	while (rela < relalim) {
89940435Speter	    symname = symbol_name(ef, rela->r_info);
90059603Sdfr	    if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) {
90159744Speter		printf("link_elf: symbol %s undefined\n", symname);
90240435Speter		return ENOENT;
90342200Speter	    }
90440435Speter	    rela++;
90540435Speter	}
90638514Sdfr    }
90738514Sdfr
90838514Sdfr    return 0;
90938514Sdfr}
91038514Sdfr
91139071Sdfr/*
91239071Sdfr * Hash function for symbol table lookup.  Don't even think about changing
91339071Sdfr * this.  It is specified by the System V ABI.
91439071Sdfr */
91539071Sdfrstatic unsigned long
91639071Sdfrelf_hash(const char *name)
91738514Sdfr{
91839071Sdfr    const unsigned char *p = (const unsigned char *) name;
91939071Sdfr    unsigned long h = 0;
92039071Sdfr    unsigned long g;
92138514Sdfr
92239071Sdfr    while (*p != '\0') {
92339071Sdfr	h = (h << 4) + *p++;
92439071Sdfr	if ((g = h & 0xf0000000) != 0)
92539071Sdfr	    h ^= g >> 24;
92639071Sdfr	h &= ~g;
92739071Sdfr    }
92839071Sdfr    return h;
92938514Sdfr}
93038514Sdfr
93138514Sdfrint
93243301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
93338514Sdfr{
93459603Sdfr    elf_file_t ef = (elf_file_t) lf;
93539071Sdfr    unsigned long symnum;
93640254Speter    const Elf_Sym* symp;
93740254Speter    const char *strp;
93839071Sdfr    unsigned long hash;
93939071Sdfr    int i;
94038514Sdfr
94140254Speter    /* First, search hashed global symbols */
94239071Sdfr    hash = elf_hash(name);
94339071Sdfr    symnum = ef->buckets[hash % ef->nbuckets];
94439071Sdfr
94539071Sdfr    while (symnum != STN_UNDEF) {
94639071Sdfr	if (symnum >= ef->nchains) {
94739071Sdfr	    printf("link_elf_lookup_symbol: corrupt symbol table\n");
94839071Sdfr	    return ENOENT;
94938514Sdfr	}
95038514Sdfr
95139071Sdfr	symp = ef->symtab + symnum;
95239071Sdfr	if (symp->st_name == 0) {
95339071Sdfr	    printf("link_elf_lookup_symbol: corrupt symbol table\n");
95439071Sdfr	    return ENOENT;
95539071Sdfr	}
95639071Sdfr
95739071Sdfr	strp = ef->strtab + symp->st_name;
95839071Sdfr
95939071Sdfr	if (strcmp(name, strp) == 0) {
96039071Sdfr	    if (symp->st_shndx != SHN_UNDEF ||
96139071Sdfr		(symp->st_value != 0 &&
96239071Sdfr		 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
96343301Sdillon		*sym = (c_linker_sym_t) symp;
96439071Sdfr		return 0;
96539071Sdfr	    } else
96639071Sdfr		return ENOENT;
96739071Sdfr	}
96839071Sdfr
96939071Sdfr	symnum = ef->chains[symnum];
97039071Sdfr    }
97139071Sdfr
97240254Speter    /* If we have not found it, look at the full table (if loaded) */
97340254Speter    if (ef->symtab == ef->ddbsymtab)
97440254Speter	return ENOENT;
97540254Speter
97640254Speter    /* Exhaustive search */
97740254Speter    for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
97840254Speter	strp = ef->ddbstrtab + symp->st_name;
97940254Speter	if (strcmp(name, strp) == 0) {
98040254Speter	    if (symp->st_shndx != SHN_UNDEF ||
98140254Speter		(symp->st_value != 0 &&
98240254Speter		 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
98343301Sdillon		*sym = (c_linker_sym_t) symp;
98440254Speter		return 0;
98540254Speter	    } else
98640254Speter		return ENOENT;
98740254Speter	}
98840254Speter    }
98940254Speter
99039071Sdfr    return ENOENT;
99138514Sdfr}
99238514Sdfr
99340156Speterstatic int
99443309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval)
99538514Sdfr{
99659603Sdfr	elf_file_t ef = (elf_file_t) lf;
99743311Sdillon	const Elf_Sym* es = (const Elf_Sym*) sym;
99838514Sdfr
99940254Speter	if (es >= ef->symtab && ((es - ef->symtab) < ef->nchains)) {
100040254Speter	    symval->name = ef->strtab + es->st_name;
100140254Speter	    symval->value = (caddr_t) ef->address + es->st_value;
100240254Speter	    symval->size = es->st_size;
100340254Speter	    return 0;
100440254Speter	}
100540254Speter	if (ef->symtab == ef->ddbsymtab)
100640254Speter	    return ENOENT;
100740254Speter	if (es >= ef->ddbsymtab && ((es - ef->ddbsymtab) < ef->ddbsymcnt)) {
100840254Speter	    symval->name = ef->ddbstrtab + es->st_name;
100940254Speter	    symval->value = (caddr_t) ef->address + es->st_value;
101040254Speter	    symval->size = es->st_size;
101140254Speter	    return 0;
101240254Speter	}
101340254Speter	return ENOENT;
101438514Sdfr}
101538514Sdfr
101638514Sdfrstatic int
101738514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value,
101843301Sdillon		       c_linker_sym_t* sym, long* diffp)
101938514Sdfr{
102059603Sdfr	elf_file_t ef = (elf_file_t) lf;
102155090Sbde	u_long off = (uintptr_t) (void *) value;
102238514Sdfr	u_long diff = off;
102355090Sbde	u_long st_value;
102439071Sdfr	const Elf_Sym* es;
102539071Sdfr	const Elf_Sym* best = 0;
102638514Sdfr	int i;
102738514Sdfr
102840254Speter	for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
102938514Sdfr		if (es->st_name == 0)
103038514Sdfr			continue;
103155090Sbde		st_value = es->st_value + (uintptr_t) (void *) ef->address;
103253820Speter		if (off >= st_value) {
103353820Speter			if (off - st_value < diff) {
103453820Speter				diff = off - st_value;
103538514Sdfr				best = es;
103638514Sdfr				if (diff == 0)
103738514Sdfr					break;
103853820Speter			} else if (off - st_value == diff) {
103938514Sdfr				best = es;
104038514Sdfr			}
104138514Sdfr		}
104238514Sdfr	}
104338514Sdfr	if (best == 0)
104438514Sdfr		*diffp = off;
104538514Sdfr	else
104638514Sdfr		*diffp = diff;
104743301Sdillon	*sym = (c_linker_sym_t) best;
104838514Sdfr
104938514Sdfr	return 0;
105038514Sdfr}
1051