link_elf.c revision 105468
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 105468 2002-10-19 18:59:33Z marcel $
2738514Sdfr */
2838514Sdfr
2959603Sdfr#include "opt_ddb.h"
3059603Sdfr
3138514Sdfr#include <sys/param.h>
3276166Smarkm#include <sys/systm.h>
3338514Sdfr#include <sys/kernel.h>
3476166Smarkm#include <sys/lock.h>
3538514Sdfr#include <sys/malloc.h>
3677642Sdd#include <sys/mutex.h>
3738514Sdfr#include <sys/proc.h>
3838514Sdfr#include <sys/namei.h>
3938514Sdfr#include <sys/fcntl.h>
4038514Sdfr#include <sys/vnode.h>
4138514Sdfr#include <sys/linker.h>
4276166Smarkm
4338514Sdfr#include <machine/elf.h>
4485735Sgreen#ifdef GPROF
4585735Sgreen#include <machine/profile.h>
4685735Sgreen#endif
4738514Sdfr
4839071Sdfr#include <vm/vm.h>
4939071Sdfr#include <vm/vm_param.h>
5052128Speter#ifdef SPARSE_MAPPING
5139071Sdfr#include <vm/vm_object.h>
5239071Sdfr#include <vm/vm_kern.h>
5339071Sdfr#include <vm/vm_extern.h>
5452128Speter#endif
5539071Sdfr#include <vm/pmap.h>
5639071Sdfr#include <vm/vm_map.h>
5776166Smarkm
58102288Speter#include <sys/link_elf.h>
5939071Sdfr
6059603Sdfr#include "linker_if.h"
6138514Sdfr
6238514Sdfrtypedef struct elf_file {
6359603Sdfr    struct linker_file	lf;		/* Common fields */
6459603Sdfr    int			preloaded;	/* Was file pre-loaded */
6539071Sdfr    caddr_t		address;	/* Relocation address */
6639071Sdfr#ifdef SPARSE_MAPPING
6739071Sdfr    vm_object_t		object;		/* VM object to hold file pages */
6839071Sdfr#endif
6959603Sdfr    Elf_Dyn*		dynamic;	/* Symbol table etc. */
7080700Sjake    Elf_Hashelt		nbuckets;	/* DT_HASH info */
7180700Sjake    Elf_Hashelt		nchains;
7280700Sjake    const Elf_Hashelt*	buckets;
7380700Sjake    const Elf_Hashelt*	chains;
7438514Sdfr    caddr_t		hash;
7538514Sdfr    caddr_t		strtab;		/* DT_STRTAB */
7640254Speter    int			strsz;		/* DT_STRSZ */
7739071Sdfr    const Elf_Sym*	symtab;		/* DT_SYMTAB */
7839071Sdfr    Elf_Addr*		got;		/* DT_PLTGOT */
7939071Sdfr    const Elf_Rel*	pltrel;		/* DT_JMPREL */
8039071Sdfr    int			pltrelsize;	/* DT_PLTRELSZ */
8139071Sdfr    const Elf_Rela*	pltrela;	/* DT_JMPREL */
8239071Sdfr    int			pltrelasize;	/* DT_PLTRELSZ */
8339071Sdfr    const Elf_Rel*	rel;		/* DT_REL */
8439071Sdfr    int			relsize;	/* DT_RELSZ */
8539071Sdfr    const Elf_Rela*	rela;		/* DT_RELA */
8639071Sdfr    int			relasize;	/* DT_RELASZ */
8740254Speter    caddr_t		modptr;
8840254Speter    const Elf_Sym*	ddbsymtab;	/* The symbol table we are using */
8940254Speter    long		ddbsymcnt;	/* Number of symbols */
9040254Speter    caddr_t		ddbstrtab;	/* String table */
9140254Speter    long		ddbstrcnt;	/* number of bytes in string table */
9240292Speter    caddr_t		symbase;	/* malloc'ed symbold base */
9340292Speter    caddr_t		strbase;	/* malloc'ed string base */
9459603Sdfr#ifdef DDB
9559603Sdfr    struct link_map	gdb;		/* hooks for gdb */
9659603Sdfr#endif
9738514Sdfr} *elf_file_t;
9838514Sdfr
99105468Smarcelstatic int	link_elf_link_common_finish(linker_file_t);
10059751Speterstatic int	link_elf_link_preload(linker_class_t cls,
10159751Speter				      const char*, linker_file_t*);
10259751Speterstatic int	link_elf_link_preload_finish(linker_file_t);
10359751Speterstatic int	link_elf_load_file(linker_class_t, const char*, linker_file_t*);
10459603Sdfrstatic int	link_elf_lookup_symbol(linker_file_t, const char*,
10559603Sdfr				       c_linker_sym_t*);
10659603Sdfrstatic int	link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*);
10759603Sdfrstatic int	link_elf_search_symbol(linker_file_t, caddr_t value,
10859603Sdfr				       c_linker_sym_t* sym, long* diffp);
10938514Sdfr
11059603Sdfrstatic void	link_elf_unload_file(linker_file_t);
11159751Speterstatic void	link_elf_unload_preload(linker_file_t);
11278161Speterstatic int	link_elf_lookup_set(linker_file_t, const char *,
11378161Speter				    void ***, void ***, int *);
11485736Sgreenstatic int	link_elf_each_function_name(linker_file_t,
11585736Sgreen				int (*)(const char *, void *),
11685736Sgreen				void *);
11759603Sdfr
11859603Sdfrstatic kobj_method_t link_elf_methods[] = {
11959603Sdfr    KOBJMETHOD(linker_lookup_symbol,	link_elf_lookup_symbol),
12059603Sdfr    KOBJMETHOD(linker_symbol_values,	link_elf_symbol_values),
12159603Sdfr    KOBJMETHOD(linker_search_symbol,	link_elf_search_symbol),
12259603Sdfr    KOBJMETHOD(linker_unload,		link_elf_unload_file),
12359751Speter    KOBJMETHOD(linker_load_file,	link_elf_load_file),
12459751Speter    KOBJMETHOD(linker_link_preload,	link_elf_link_preload),
12559751Speter    KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish),
12678161Speter    KOBJMETHOD(linker_lookup_set,	link_elf_lookup_set),
12785736Sgreen    KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
12859603Sdfr    { 0, 0 }
12959603Sdfr};
13059603Sdfr
13159603Sdfrstatic struct linker_class link_elf_class = {
13259603Sdfr#if ELF_TARG_CLASS == ELFCLASS32
13359603Sdfr    "elf32",
13459603Sdfr#else
13559603Sdfr    "elf64",
13659603Sdfr#endif
13759603Sdfr    link_elf_methods, sizeof(struct elf_file)
13859603Sdfr};
13959603Sdfr
14059603Sdfrstatic int		parse_dynamic(elf_file_t ef);
14159603Sdfrstatic int		relocate_file(elf_file_t ef);
14259751Speterstatic int		link_elf_preload_parse_symbols(elf_file_t ef);
14359603Sdfr
14459603Sdfr#ifdef DDB
14566719Sjhbstatic void		r_debug_state(struct r_debug *dummy_one,
14666719Sjhb				      struct link_map *dummy_two);
14759603Sdfr
14838514Sdfr/*
14959603Sdfr * A list of loaded modules for GDB to use for loading symbols.
15059603Sdfr */
15159603Sdfrstruct r_debug r_debug;
15259603Sdfr
15366719Sjhb#define GDB_STATE(s)	r_debug.r_state = s; r_debug_state(NULL, NULL);
15459603Sdfr
15559603Sdfr/*
15659603Sdfr * Function for the debugger to set a breakpoint on to gain control.
15759603Sdfr */
158104094Sphkstatic void
15966719Sjhbr_debug_state(struct r_debug *dummy_one __unused,
16066719Sjhb	      struct link_map *dummy_two __unused)
16159603Sdfr{
16259603Sdfr}
16359603Sdfr
164105467Smarcelstatic void
165105467Smarcellink_elf_add_gdb(struct link_map *l)
166105467Smarcel{
167105467Smarcel    struct link_map *prev;
16859603Sdfr
169105468Smarcel    l->l_next = NULL;
170105467Smarcel
171105468Smarcel    if (r_debug.r_map == NULL) {
172105468Smarcel	/* Add first. */
173105468Smarcel	l->l_prev = NULL;
174105468Smarcel	r_debug.r_map = l;
175105468Smarcel    } else {
176105468Smarcel	/* Append to list. */
177105468Smarcel	for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next)
178105468Smarcel	    ;
179105468Smarcel	l->l_prev = prev;
180105468Smarcel	prev->l_next = l;
181105468Smarcel    }
182105467Smarcel}
183105467Smarcel
184105467Smarcelstatic void
185105467Smarcellink_elf_delete_gdb(struct link_map *l)
186105467Smarcel{
187105467Smarcel    if (l->l_prev == NULL) {
188105468Smarcel	/* Remove first. */
189105467Smarcel	if ((r_debug.r_map = l->l_next) != NULL)
190105467Smarcel	    l->l_next->l_prev = NULL;
191105468Smarcel    } else {
192105468Smarcel	/* Remove any but first. */
193105468Smarcel	if ((l->l_prev->l_next = l->l_next) != NULL)
194105468Smarcel	    l->l_next->l_prev = l->l_prev;
195105467Smarcel    }
196105467Smarcel}
197105467Smarcel#endif /* DDB */
198105467Smarcel
19995228Smarcel#ifdef __ia64__
20095228SmarcelElf_Addr link_elf_get_gp(linker_file_t);
20195228Smarcel#endif
20295228Smarcel
20359603Sdfr/*
20438514Sdfr * The kernel symbol table starts here.
20538514Sdfr */
20638514Sdfrextern struct _dynamic _DYNAMIC;
20738514Sdfr
20838514Sdfrstatic void
209105467Smarcellink_elf_error(const char *s)
210105467Smarcel{
211105467Smarcel    printf("kldload: %s\n", s);
212105467Smarcel}
213105467Smarcel
214105468Smarcel/*
215105468Smarcel * Actions performed after linking/loading both the preloaded kernel and any
216105468Smarcel * modules; whether preloaded or dynamicly loaded.
217105468Smarcel */
218105468Smarcelstatic int
219105468Smarcellink_elf_link_common_finish(linker_file_t lf)
220105468Smarcel{
221105468Smarcel#ifdef DDB
222105468Smarcel    elf_file_t ef = (elf_file_t)lf;
223105468Smarcel    char *newfilename;
224105468Smarcel#endif
225105468Smarcel
226105468Smarcel#ifdef DDB
227105468Smarcel    GDB_STATE(RT_ADD);
228105468Smarcel    ef->gdb.l_addr = lf->address;
229105468Smarcel    newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK);
230105468Smarcel    strcpy(newfilename, lf->filename);
231105468Smarcel    ef->gdb.l_name = newfilename;
232105468Smarcel    ef->gdb.l_ld = ef->dynamic;
233105468Smarcel    link_elf_add_gdb(&ef->gdb);
234105468Smarcel    GDB_STATE(RT_CONSISTENT);
235105468Smarcel#endif
236105468Smarcel
237105468Smarcel    return (0);
238105468Smarcel}
239105468Smarcel
240105467Smarcelstatic void
24138514Sdfrlink_elf_init(void* arg)
24238514Sdfr{
24340156Speter    Elf_Dyn	*dp;
24440156Speter    caddr_t	modptr, baseptr, sizeptr;
24540156Speter    elf_file_t	ef;
24640156Speter    char	*modname;
24738514Sdfr
24859603Sdfr    linker_add_class(&link_elf_class);
24938514Sdfr
25040156Speter    dp = (Elf_Dyn*) &_DYNAMIC;
25182848Speter    modname = NULL;
25282848Speter    modptr = preload_search_by_type("elf kernel");
25382848Speter    if (modptr)
25482848Speter	modname = (char *)preload_search_info(modptr, MODINFO_NAME);
25582848Speter    if (modname == NULL)
25682848Speter	modname = "kernel";
25782848Speter    linker_kernel_file = linker_make_file(modname, &link_elf_class);
25882848Speter    if (linker_kernel_file == NULL)
25982848Speter	panic("link_elf_init: Can't create linker structures for kernel");
260105468Smarcel
26182848Speter    ef = (elf_file_t) linker_kernel_file;
26282848Speter    ef->preloaded = 1;
26382848Speter    ef->address = 0;
26459603Sdfr#ifdef SPARSE_MAPPING
26582848Speter    ef->object = 0;
26659603Sdfr#endif
26782848Speter    ef->dynamic = dp;
26859603Sdfr
26982848Speter    if (dp)
27082848Speter	parse_dynamic(ef);
27182848Speter    linker_kernel_file->address = (caddr_t) KERNBASE;
27282848Speter    linker_kernel_file->size = -(intptr_t)linker_kernel_file->address;
27340156Speter
27482848Speter    if (modptr) {
27582848Speter	ef->modptr = modptr;
27682848Speter	baseptr = preload_search_info(modptr, MODINFO_ADDR);
27782848Speter	if (baseptr)
27882848Speter	    linker_kernel_file->address = *(caddr_t *)baseptr;
27982848Speter	sizeptr = preload_search_info(modptr, MODINFO_SIZE);
28082848Speter	if (sizeptr)
28182848Speter	    linker_kernel_file->size = *(size_t *)sizeptr;
28282848Speter    }
28382848Speter    (void)link_elf_preload_parse_symbols(ef);
28459603Sdfr
28559603Sdfr#ifdef DDB
286105468Smarcel    r_debug.r_map = NULL;
28782848Speter    r_debug.r_brk = r_debug_state;
28882848Speter    r_debug.r_state = RT_CONSISTENT;
289105468Smarcel#endif
29059603Sdfr
291105468Smarcel    (void)link_elf_link_common_finish(linker_kernel_file);
29238514Sdfr}
29338514Sdfr
29440156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
29538514Sdfr
29638514Sdfrstatic int
29759751Speterlink_elf_preload_parse_symbols(elf_file_t ef)
29840254Speter{
29940254Speter    caddr_t	pointer;
30040254Speter    caddr_t	ssym, esym, base;
30140254Speter    caddr_t	strtab;
30240254Speter    int		strcnt;
30340254Speter    Elf_Sym*	symtab;
30440254Speter    int		symcnt;
30540254Speter
30640292Speter    if (ef->modptr == NULL)
30740292Speter	return 0;
30840254Speter    pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM);
30940254Speter    if (pointer == NULL)
31040254Speter	return 0;
31140254Speter    ssym = *(caddr_t *)pointer;
31240254Speter    pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM);
31340254Speter    if (pointer == NULL)
31440254Speter	return 0;
31540254Speter    esym = *(caddr_t *)pointer;
31640254Speter
31740254Speter    base = ssym;
31840254Speter
31940254Speter    symcnt = *(long *)base;
32040254Speter    base += sizeof(long);
32140254Speter    symtab = (Elf_Sym *)base;
32240254Speter    base += roundup(symcnt, sizeof(long));
32340254Speter
32440254Speter    if (base > esym || base < ssym) {
32540254Speter	printf("Symbols are corrupt!\n");
32640254Speter	return EINVAL;
32740254Speter    }
32840254Speter
32940254Speter    strcnt = *(long *)base;
33040254Speter    base += sizeof(long);
33140254Speter    strtab = base;
33240254Speter    base += roundup(strcnt, sizeof(long));
33340254Speter
33440254Speter    if (base > esym || base < ssym) {
33540254Speter	printf("Symbols are corrupt!\n");
33640254Speter	return EINVAL;
33740254Speter    }
33840254Speter
33940254Speter    ef->ddbsymtab = symtab;
34040254Speter    ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
34140254Speter    ef->ddbstrtab = strtab;
34240254Speter    ef->ddbstrcnt = strcnt;
34340254Speter
34440254Speter    return 0;
34540254Speter}
34640254Speter
34740254Speterstatic int
34859603Sdfrparse_dynamic(elf_file_t ef)
34938514Sdfr{
35059603Sdfr    Elf_Dyn *dp;
35139071Sdfr    int plttype = DT_REL;
35238514Sdfr
35338514Sdfr    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
35438514Sdfr	switch (dp->d_tag) {
35538514Sdfr	case DT_HASH:
35638514Sdfr	{
35738514Sdfr	    /* From src/libexec/rtld-elf/rtld.c */
35880700Sjake	    const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
35938514Sdfr		(ef->address + dp->d_un.d_ptr);
36038514Sdfr	    ef->nbuckets = hashtab[0];
36138514Sdfr	    ef->nchains = hashtab[1];
36238514Sdfr	    ef->buckets = hashtab + 2;
36338514Sdfr	    ef->chains = ef->buckets + ef->nbuckets;
36438514Sdfr	    break;
36538514Sdfr	}
36638514Sdfr	case DT_STRTAB:
36739071Sdfr	    ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr);
36838514Sdfr	    break;
36940254Speter	case DT_STRSZ:
37040254Speter	    ef->strsz = dp->d_un.d_val;
37140254Speter	    break;
37238514Sdfr	case DT_SYMTAB:
37339071Sdfr	    ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr);
37438514Sdfr	    break;
37538514Sdfr	case DT_SYMENT:
37638514Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Sym))
37738514Sdfr		return ENOEXEC;
37839071Sdfr	    break;
37939071Sdfr	case DT_PLTGOT:
38039071Sdfr	    ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr);
38139071Sdfr	    break;
38239071Sdfr	case DT_REL:
38339071Sdfr	    ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr);
38439071Sdfr	    break;
38539071Sdfr	case DT_RELSZ:
38639071Sdfr	    ef->relsize = dp->d_un.d_val;
38739071Sdfr	    break;
38839071Sdfr	case DT_RELENT:
38939071Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Rel))
39039071Sdfr		return ENOEXEC;
39139071Sdfr	    break;
39239071Sdfr	case DT_JMPREL:
39339071Sdfr	    ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr);
39439071Sdfr	    break;
39539071Sdfr	case DT_PLTRELSZ:
39639071Sdfr	    ef->pltrelsize = dp->d_un.d_val;
39739071Sdfr	    break;
39839071Sdfr	case DT_RELA:
39939071Sdfr	    ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr);
40039071Sdfr	    break;
40139071Sdfr	case DT_RELASZ:
40239071Sdfr	    ef->relasize = dp->d_un.d_val;
40339071Sdfr	    break;
40439071Sdfr	case DT_RELAENT:
40539071Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Rela))
40639071Sdfr		return ENOEXEC;
40739071Sdfr	    break;
40839071Sdfr	case DT_PLTREL:
40939071Sdfr	    plttype = dp->d_un.d_val;
41039071Sdfr	    if (plttype != DT_REL && plttype != DT_RELA)
41139071Sdfr		return ENOEXEC;
41239071Sdfr	    break;
41359603Sdfr#ifdef DDB
41459603Sdfr	case DT_DEBUG:
41559603Sdfr	    dp->d_un.d_ptr = (Elf_Addr) &r_debug;
41659603Sdfr	    break;
41759603Sdfr#endif
41838514Sdfr	}
41938514Sdfr    }
42039071Sdfr
42139071Sdfr    if (plttype == DT_RELA) {
42239071Sdfr	ef->pltrela = (const Elf_Rela *) ef->pltrel;
42339071Sdfr	ef->pltrel = NULL;
42439071Sdfr	ef->pltrelasize = ef->pltrelsize;
42539071Sdfr	ef->pltrelsize = 0;
42639071Sdfr    }
42739071Sdfr
42840254Speter    ef->ddbsymtab = ef->symtab;
42940254Speter    ef->ddbsymcnt = ef->nchains;
43040254Speter    ef->ddbstrtab = ef->strtab;
43140254Speter    ef->ddbstrcnt = ef->strsz;
43240254Speter
43338514Sdfr    return 0;
43438514Sdfr}
43538514Sdfr
43638514Sdfrstatic int
43759751Speterlink_elf_link_preload(linker_class_t cls,
43859751Speter		      const char* filename, linker_file_t *result)
43940156Speter{
44040156Speter    caddr_t		modptr, baseptr, sizeptr, dynptr;
44140156Speter    char		*type;
44240156Speter    elf_file_t		ef;
44340156Speter    linker_file_t	lf;
44440156Speter    int			error;
44540156Speter    vm_offset_t		dp;
44640156Speter
44759751Speter    /* Look to see if we have the file preloaded */
44840156Speter    modptr = preload_search_by_name(filename);
44940156Speter    if (modptr == NULL)
45059751Speter	return ENOENT;
45140156Speter
45240156Speter    type = (char *)preload_search_info(modptr, MODINFO_TYPE);
45340156Speter    baseptr = preload_search_info(modptr, MODINFO_ADDR);
45440156Speter    sizeptr = preload_search_info(modptr, MODINFO_SIZE);
45540156Speter    dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC);
45640156Speter    if (type == NULL || strcmp(type, "elf module") != 0)
45740156Speter	return (EFTYPE);
45840156Speter    if (baseptr == NULL || sizeptr == NULL || dynptr == NULL)
45940156Speter	return (EINVAL);
46040156Speter
46159603Sdfr    lf = linker_make_file(filename, &link_elf_class);
46259603Sdfr    if (lf == NULL) {
46359603Sdfr	return ENOMEM;
46459603Sdfr    }
46559603Sdfr
46659603Sdfr    ef = (elf_file_t) lf;
46759603Sdfr    ef->preloaded = 1;
46840292Speter    ef->modptr = modptr;
46940156Speter    ef->address = *(caddr_t *)baseptr;
47040156Speter#ifdef SPARSE_MAPPING
47140156Speter    ef->object = 0;
47240156Speter#endif
47340156Speter    dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr;
47440156Speter    ef->dynamic = (Elf_Dyn *)dp;
47540156Speter    lf->address = ef->address;
47640156Speter    lf->size = *(size_t *)sizeptr;
47740156Speter
47859603Sdfr    error = parse_dynamic(ef);
47940156Speter    if (error) {
48040156Speter	linker_file_unload(lf);
48140156Speter	return error;
48240156Speter    }
48359751Speter    *result = lf;
48459751Speter    return (0);
48559751Speter}
48659751Speter
48759751Speterstatic int
48859751Speterlink_elf_link_preload_finish(linker_file_t lf)
48959751Speter{
49059751Speter    elf_file_t		ef;
49159751Speter    int error;
49259751Speter
49359751Speter    ef = (elf_file_t) lf;
49459751Speter#if 0	/* this will be more trouble than it's worth for now */
49559751Speter    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
49659751Speter	if (dp->d_tag != DT_NEEDED)
49759751Speter	    continue;
49859751Speter	modname = ef->strtab + dp->d_un.d_val;
49959751Speter	error = linker_load_module(modname, lf);
50059751Speter	if (error)
50159751Speter	    goto out;
50240156Speter    }
50359751Speter#endif
50459603Sdfr    error = relocate_file(ef);
50559751Speter    if (error)
50640156Speter	return error;
50759751Speter    (void)link_elf_preload_parse_symbols(ef);
50859603Sdfr
509105468Smarcel    return (link_elf_link_common_finish(lf));
51040156Speter}
51140156Speter
51240156Speterstatic int
513105468Smarcellink_elf_load_file(linker_class_t cls, const char* filename,
514105468Smarcel	linker_file_t* result)
51538514Sdfr{
51638514Sdfr    struct nameidata nd;
51783366Sjulian    struct thread* td = curthread;	/* XXX */
51840962Speter    Elf_Ehdr *hdr;
51940962Speter    caddr_t firstpage;
52039071Sdfr    int nbytes, i;
52139071Sdfr    Elf_Phdr *phdr;
52239071Sdfr    Elf_Phdr *phlimit;
52339071Sdfr    Elf_Phdr *segs[2];
52439071Sdfr    int nsegs;
52539071Sdfr    Elf_Phdr *phdyn;
52639071Sdfr    Elf_Phdr *phphdr;
52739071Sdfr    caddr_t mapbase;
52839071Sdfr    size_t mapsize;
52939071Sdfr    Elf_Off base_offset;
53039071Sdfr    Elf_Addr base_vaddr;
53139071Sdfr    Elf_Addr base_vlimit;
53238514Sdfr    int error = 0;
53362550Smckusick    int resid, flags;
53438514Sdfr    elf_file_t ef;
53538514Sdfr    linker_file_t lf;
53640292Speter    Elf_Shdr *shdr;
53740292Speter    int symtabindex;
53840292Speter    int symstrindex;
53940292Speter    int symcnt;
54040292Speter    int strcnt;
54138514Sdfr
54279224Sdillon    GIANT_REQUIRED;
54379224Sdillon
54440292Speter    shdr = NULL;
54540292Speter    lf = NULL;
54640292Speter
54783366Sjulian    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
54862550Smckusick    flags = FREAD;
54962550Smckusick    error = vn_open(&nd, &flags, 0);
55038514Sdfr    if (error)
55138514Sdfr	return error;
55254655Seivind    NDFREE(&nd, NDF_ONLY_PNBUF);
55338514Sdfr
55438514Sdfr    /*
55539071Sdfr     * Read the elf header from the file.
55638514Sdfr     */
55740962Speter    firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
55840962Speter    if (firstpage == NULL) {
55940962Speter	error = ENOMEM;
56040962Speter	goto out;
56140962Speter    }
56240962Speter    hdr = (Elf_Ehdr *)firstpage;
56340962Speter    error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0,
564101941Srwatson		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
565101941Srwatson		    &resid, td);
56640962Speter    nbytes = PAGE_SIZE - resid;
56738514Sdfr    if (error)
56838514Sdfr	goto out;
56938514Sdfr
57040962Speter    if (!IS_ELF(*hdr)) {
57139071Sdfr	error = ENOEXEC;
57238514Sdfr	goto out;
57339071Sdfr    }
57438514Sdfr
57540962Speter    if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
57640962Speter      || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
57739071Sdfr	link_elf_error("Unsupported file layout");
57839071Sdfr	error = ENOEXEC;
57939071Sdfr	goto out;
58039071Sdfr    }
58140962Speter    if (hdr->e_ident[EI_VERSION] != EV_CURRENT
58240962Speter      || hdr->e_version != EV_CURRENT) {
58339071Sdfr	link_elf_error("Unsupported file version");
58439071Sdfr	error = ENOEXEC;
58539071Sdfr	goto out;
58639071Sdfr    }
58740962Speter    if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
58839071Sdfr	link_elf_error("Unsupported file type");
58939071Sdfr	error = ENOEXEC;
59039071Sdfr	goto out;
59139071Sdfr    }
59240962Speter    if (hdr->e_machine != ELF_TARG_MACH) {
59339071Sdfr	link_elf_error("Unsupported machine");
59439071Sdfr	error = ENOEXEC;
59539071Sdfr	goto out;
59639071Sdfr    }
59739071Sdfr
59838514Sdfr    /*
59939071Sdfr     * We rely on the program header being in the first page.  This is
60039071Sdfr     * not strictly required by the ABI specification, but it seems to
60139071Sdfr     * always true in practice.  And, it simplifies things considerably.
60238514Sdfr     */
60340962Speter    if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) &&
60440962Speter	  (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) &&
60540962Speter	  (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes)))
60639071Sdfr	link_elf_error("Unreadable program headers");
60739071Sdfr
60838514Sdfr    /*
60939071Sdfr     * Scan the program header entries, and save key information.
61039071Sdfr     *
61139071Sdfr     * We rely on there being exactly two load segments, text and data,
61239071Sdfr     * in that order.
61338514Sdfr     */
61440962Speter    phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff);
61540962Speter    phlimit = phdr + hdr->e_phnum;
61639071Sdfr    nsegs = 0;
61739071Sdfr    phdyn = NULL;
61839071Sdfr    phphdr = NULL;
61939071Sdfr    while (phdr < phlimit) {
62039071Sdfr	switch (phdr->p_type) {
62139071Sdfr
62239071Sdfr	case PT_LOAD:
62339071Sdfr	    if (nsegs == 2) {
62439071Sdfr		link_elf_error("Too many sections");
62539071Sdfr		error = ENOEXEC;
62639071Sdfr		goto out;
62739071Sdfr	    }
62839071Sdfr	    segs[nsegs] = phdr;
62939071Sdfr	    ++nsegs;
63039071Sdfr	    break;
63139071Sdfr
63239071Sdfr	case PT_PHDR:
63339071Sdfr	    phphdr = phdr;
63439071Sdfr	    break;
63539071Sdfr
63639071Sdfr	case PT_DYNAMIC:
63739071Sdfr	    phdyn = phdr;
63839071Sdfr	    break;
63965503Sbp
64065503Sbp	case PT_INTERP:
64165503Sbp	    link_elf_error("Unsupported file type");
64265503Sbp	    error = ENOEXEC;
64365503Sbp	    goto out;
64439071Sdfr	}
64539071Sdfr
64639071Sdfr	++phdr;
64739071Sdfr    }
64839071Sdfr    if (phdyn == NULL) {
64939071Sdfr	link_elf_error("Object is not dynamically-linked");
65039071Sdfr	error = ENOEXEC;
65138514Sdfr	goto out;
65239071Sdfr    }
65338514Sdfr
65438514Sdfr    /*
65539071Sdfr     * Allocate the entire address space of the object, to stake out our
65639071Sdfr     * contiguous region, and to establish the base address for relocation.
65738514Sdfr     */
65839071Sdfr    base_offset = trunc_page(segs[0]->p_offset);
65939071Sdfr    base_vaddr = trunc_page(segs[0]->p_vaddr);
66039071Sdfr    base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz);
66139071Sdfr    mapsize = base_vlimit - base_vaddr;
66239071Sdfr
66359603Sdfr    lf = linker_make_file(filename, &link_elf_class);
66459603Sdfr    if (!lf) {
66559603Sdfr	error = ENOMEM;
66659603Sdfr	goto out;
66759603Sdfr    }
66859603Sdfr
66959603Sdfr    ef = (elf_file_t) lf;
67039071Sdfr#ifdef SPARSE_MAPPING
67139071Sdfr    ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT);
67239071Sdfr    if (ef->object == NULL) {
67339071Sdfr	error = ENOMEM;
67438514Sdfr	goto out;
67538514Sdfr    }
67639071Sdfr    vm_object_reference(ef->object);
67739071Sdfr    ef->address = (caddr_t) vm_map_min(kernel_map);
67839071Sdfr    error = vm_map_find(kernel_map, ef->object, 0,
67939071Sdfr			(vm_offset_t *) &ef->address,
68039071Sdfr			mapsize, 1,
68139071Sdfr			VM_PROT_ALL, VM_PROT_ALL, 0);
68239071Sdfr    if (error) {
68339071Sdfr	vm_object_deallocate(ef->object);
68459603Sdfr	ef->object = 0;
68539071Sdfr	goto out;
68639071Sdfr    }
68739071Sdfr#else
68839071Sdfr    ef->address = malloc(mapsize, M_LINKER, M_WAITOK);
68959603Sdfr    if (!ef->address) {
69059603Sdfr	error = ENOMEM;
69159603Sdfr	goto out;
69259603Sdfr    }
69339071Sdfr#endif
69439071Sdfr    mapbase = ef->address;
69538514Sdfr
69639071Sdfr    /*
69739071Sdfr     * Read the text and data sections and zero the bss.
69839071Sdfr     */
69939071Sdfr    for (i = 0; i < 2; i++) {
70039071Sdfr	caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
70139071Sdfr	error = vn_rdwr(UIO_READ, nd.ni_vp,
70239071Sdfr			segbase, segs[i]->p_filesz, segs[i]->p_offset,
703101941Srwatson			UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
704101941Srwatson			&resid, td);
70539071Sdfr	if (error) {
70639071Sdfr	    goto out;
70739071Sdfr	}
70839071Sdfr	bzero(segbase + segs[i]->p_filesz,
70939071Sdfr	      segs[i]->p_memsz - segs[i]->p_filesz);
71039071Sdfr
71139071Sdfr#ifdef SPARSE_MAPPING
71239071Sdfr	/*
71339071Sdfr	 * Wire down the pages
71439071Sdfr	 */
715102547Sjake	vm_map_wire(kernel_map,
716102547Sjake		    (vm_offset_t) segbase,
717102547Sjake		    (vm_offset_t) segbase + segs[i]->p_memsz,
718102547Sjake		    FALSE);
71939071Sdfr#endif
72039071Sdfr    }
72139071Sdfr
72285734Sgreen#ifdef GPROF
72385734Sgreen    /* Update profiling information with the new text segment. */
72485734Sgreen    kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr +
72585734Sgreen	segs[0]->p_memsz));
72685734Sgreen#endif
72785734Sgreen
72859603Sdfr    ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
72939071Sdfr
73038514Sdfr    lf->address = ef->address;
73139071Sdfr    lf->size = mapsize;
73238514Sdfr
73359603Sdfr    error = parse_dynamic(ef);
73440292Speter    if (error)
73538514Sdfr	goto out;
73686469Siedowse    error = linker_load_dependencies(lf);
73740292Speter    if (error)
73840156Speter	goto out;
73959751Speter#if 0	/* this will be more trouble than it's worth for now */
74059751Speter    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
74159751Speter	if (dp->d_tag != DT_NEEDED)
74259751Speter	    continue;
74359751Speter	modname = ef->strtab + dp->d_un.d_val;
74459751Speter	error = linker_load_module(modname, lf);
74559751Speter	if (error)
74659751Speter	    goto out;
74759751Speter    }
74859751Speter#endif
74959603Sdfr    error = relocate_file(ef);
75040292Speter    if (error)
75140156Speter	goto out;
75240292Speter
75340292Speter    /* Try and load the symbol table if it's present.  (you can strip it!) */
75440962Speter    nbytes = hdr->e_shnum * hdr->e_shentsize;
75540962Speter    if (nbytes == 0 || hdr->e_shoff == 0)
75640292Speter	goto nosyms;
75769781Sdwmalone    shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO);
75840292Speter    if (shdr == NULL) {
75940292Speter	error = ENOMEM;
76040292Speter	goto out;
76140156Speter    }
76240292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
76340962Speter		    (caddr_t)shdr, nbytes, hdr->e_shoff,
764101941Srwatson		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
765101941Srwatson		    &resid, td);
76640292Speter    if (error)
76740292Speter	goto out;
76840292Speter    symtabindex = -1;
76940292Speter    symstrindex = -1;
77040962Speter    for (i = 0; i < hdr->e_shnum; i++) {
77140292Speter	if (shdr[i].sh_type == SHT_SYMTAB) {
77240292Speter	    symtabindex = i;
77340292Speter	    symstrindex = shdr[i].sh_link;
77440292Speter	}
77540292Speter    }
77640292Speter    if (symtabindex < 0 || symstrindex < 0)
77740292Speter	goto nosyms;
77840156Speter
77940292Speter    symcnt = shdr[symtabindex].sh_size;
78040292Speter    ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK);
78140292Speter    strcnt = shdr[symstrindex].sh_size;
78240292Speter    ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK);
78340292Speter
78440292Speter    if (ef->symbase == NULL || ef->strbase == NULL) {
78540292Speter	error = ENOMEM;
78640292Speter	goto out;
78740292Speter    }
78840292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
78940292Speter		    ef->symbase, symcnt, shdr[symtabindex].sh_offset,
790101941Srwatson		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
791101941Srwatson		    &resid, td);
79240292Speter    if (error)
79340292Speter	goto out;
79440292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
79540292Speter		    ef->strbase, strcnt, shdr[symstrindex].sh_offset,
796101941Srwatson		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
797101941Srwatson		    &resid, td);
79840292Speter    if (error)
79940292Speter	goto out;
80040292Speter
80140292Speter    ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
80240292Speter    ef->ddbsymtab = (const Elf_Sym *)ef->symbase;
80340292Speter    ef->ddbstrcnt = strcnt;
80440292Speter    ef->ddbstrtab = ef->strbase;
80540292Speter
806105468Smarcel    error = link_elf_link_common_finish(lf);
807105468Smarcel    if (error)
808105468Smarcel	goto out;
80959603Sdfr
81040292Speternosyms:
81140292Speter
81238514Sdfr    *result = lf;
81338514Sdfr
81438514Sdfrout:
81540292Speter    if (error && lf)
81640292Speter	linker_file_unload(lf);
81740292Speter    if (shdr)
81840292Speter	free(shdr, M_LINKER);
81940962Speter    if (firstpage)
82040962Speter	free(firstpage, M_LINKER);
82183366Sjulian    VOP_UNLOCK(nd.ni_vp, 0, td);
82291406Sjhb    vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
82338514Sdfr
82438514Sdfr    return error;
82538514Sdfr}
82638514Sdfr
82738514Sdfrstatic void
82840156Speterlink_elf_unload_file(linker_file_t file)
82938514Sdfr{
83059603Sdfr    elf_file_t ef = (elf_file_t) file;
83138514Sdfr
83259603Sdfr#ifdef DDB
83359603Sdfr    if (ef->gdb.l_ld) {
83459603Sdfr	GDB_STATE(RT_DELETE);
83583282Speter	free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER);
83659603Sdfr	link_elf_delete_gdb(&ef->gdb);
83759603Sdfr	GDB_STATE(RT_CONSISTENT);
83859603Sdfr    }
83959603Sdfr#endif
84059603Sdfr
84159603Sdfr    if (ef->preloaded) {
84259751Speter	link_elf_unload_preload(file);
84359603Sdfr	return;
84459603Sdfr    }
845105468Smarcel
84639071Sdfr#ifdef SPARSE_MAPPING
84759603Sdfr    if (ef->object) {
84859603Sdfr	vm_map_remove(kernel_map, (vm_offset_t) ef->address,
84959603Sdfr		      (vm_offset_t) ef->address
85059603Sdfr		      + (ef->object->size << PAGE_SHIFT));
85159603Sdfr	vm_object_deallocate(ef->object);
85259603Sdfr    }
85339071Sdfr#else
85459603Sdfr    if (ef->address)
85559603Sdfr	free(ef->address, M_LINKER);
85639071Sdfr#endif
85759603Sdfr    if (ef->symbase)
85859603Sdfr	free(ef->symbase, M_LINKER);
85959603Sdfr    if (ef->strbase)
86059603Sdfr	free(ef->strbase, M_LINKER);
86138514Sdfr}
86238514Sdfr
86340156Speterstatic void
86459751Speterlink_elf_unload_preload(linker_file_t file)
86540156Speter{
86640156Speter    if (file->filename)
86740156Speter	preload_delete_name(file->filename);
86840156Speter}
86940156Speter
87039071Sdfrstatic const char *
87140435Spetersymbol_name(elf_file_t ef, Elf_Word r_info)
87238514Sdfr{
87339071Sdfr    const Elf_Sym *ref;
87438514Sdfr
87540435Speter    if (ELF_R_SYM(r_info)) {
87640435Speter	ref = ef->symtab + ELF_R_SYM(r_info);
87740397Speter	return ef->strtab + ref->st_name;
87839071Sdfr    } else
87939071Sdfr	return NULL;
88038514Sdfr}
88138514Sdfr
88238514Sdfrstatic int
88359603Sdfrrelocate_file(elf_file_t ef)
88438514Sdfr{
88539071Sdfr    const Elf_Rel *rellim;
88639071Sdfr    const Elf_Rel *rel;
88739071Sdfr    const Elf_Rela *relalim;
88839071Sdfr    const Elf_Rela *rela;
88940435Speter    const char *symname;
89038514Sdfr
89139071Sdfr    /* Perform relocations without addend if there are any: */
89240435Speter    rel = ef->rel;
89340435Speter    if (rel) {
89443388Sbde	rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize);
89540435Speter	while (rel < rellim) {
89695410Smarcel	    if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) {
89795410Smarcel		symname = symbol_name(ef, rel->r_info);
89859744Speter		printf("link_elf: symbol %s undefined\n", symname);
89940435Speter		return ENOENT;
90042200Speter	    }
90140435Speter	    rel++;
90240435Speter	}
90339071Sdfr    }
90438514Sdfr
90539071Sdfr    /* Perform relocations with addend if there are any: */
90640435Speter    rela = ef->rela;
90740435Speter    if (rela) {
90843388Sbde	relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize);
90940435Speter	while (rela < relalim) {
91095410Smarcel	    if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) {
91195410Smarcel		symname = symbol_name(ef, rela->r_info);
91259744Speter		printf("link_elf: symbol %s undefined\n", symname);
91340435Speter		return ENOENT;
91442200Speter	    }
91540435Speter	    rela++;
91640435Speter	}
91739071Sdfr    }
91838514Sdfr
91939071Sdfr    /* Perform PLT relocations without addend if there are any: */
92040435Speter    rel = ef->pltrel;
92140435Speter    if (rel) {
92243388Sbde	rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize);
92340435Speter	while (rel < rellim) {
92495410Smarcel	    if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) {
92595410Smarcel		symname = symbol_name(ef, rel->r_info);
92659744Speter		printf("link_elf: symbol %s undefined\n", symname);
92740435Speter		return ENOENT;
92842200Speter	    }
92940435Speter	    rel++;
93040435Speter	}
93139071Sdfr    }
93238514Sdfr
93339071Sdfr    /* Perform relocations with addend if there are any: */
93440435Speter    rela = ef->pltrela;
93540435Speter    if (rela) {
93643388Sbde	relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize);
93740435Speter	while (rela < relalim) {
93895410Smarcel	    if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) {
93995410Smarcel		symname = symbol_name(ef, rela->r_info);
94059744Speter		printf("link_elf: symbol %s undefined\n", symname);
94140435Speter		return ENOENT;
94242200Speter	    }
94340435Speter	    rela++;
94440435Speter	}
94538514Sdfr    }
94638514Sdfr
94738514Sdfr    return 0;
94838514Sdfr}
94938514Sdfr
95039071Sdfr/*
95139071Sdfr * Hash function for symbol table lookup.  Don't even think about changing
95239071Sdfr * this.  It is specified by the System V ABI.
95339071Sdfr */
95439071Sdfrstatic unsigned long
95539071Sdfrelf_hash(const char *name)
95638514Sdfr{
95739071Sdfr    const unsigned char *p = (const unsigned char *) name;
95839071Sdfr    unsigned long h = 0;
95939071Sdfr    unsigned long g;
96038514Sdfr
96139071Sdfr    while (*p != '\0') {
96239071Sdfr	h = (h << 4) + *p++;
96339071Sdfr	if ((g = h & 0xf0000000) != 0)
96439071Sdfr	    h ^= g >> 24;
96539071Sdfr	h &= ~g;
96639071Sdfr    }
96739071Sdfr    return h;
96838514Sdfr}
96938514Sdfr
970104094Sphkstatic int
97143301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
97238514Sdfr{
97359603Sdfr    elf_file_t ef = (elf_file_t) lf;
97439071Sdfr    unsigned long symnum;
97540254Speter    const Elf_Sym* symp;
97640254Speter    const char *strp;
97739071Sdfr    unsigned long hash;
97839071Sdfr    int i;
97938514Sdfr
98040254Speter    /* First, search hashed global symbols */
98139071Sdfr    hash = elf_hash(name);
98239071Sdfr    symnum = ef->buckets[hash % ef->nbuckets];
98339071Sdfr
98439071Sdfr    while (symnum != STN_UNDEF) {
98539071Sdfr	if (symnum >= ef->nchains) {
98639071Sdfr	    printf("link_elf_lookup_symbol: corrupt symbol table\n");
98739071Sdfr	    return ENOENT;
98838514Sdfr	}
98938514Sdfr
99039071Sdfr	symp = ef->symtab + symnum;
99139071Sdfr	if (symp->st_name == 0) {
99239071Sdfr	    printf("link_elf_lookup_symbol: corrupt symbol table\n");
99339071Sdfr	    return ENOENT;
99439071Sdfr	}
99539071Sdfr
99639071Sdfr	strp = ef->strtab + symp->st_name;
99739071Sdfr
99839071Sdfr	if (strcmp(name, strp) == 0) {
99939071Sdfr	    if (symp->st_shndx != SHN_UNDEF ||
100039071Sdfr		(symp->st_value != 0 &&
100139071Sdfr		 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
100243301Sdillon		*sym = (c_linker_sym_t) symp;
100339071Sdfr		return 0;
100439071Sdfr	    } else
100539071Sdfr		return ENOENT;
100639071Sdfr	}
100739071Sdfr
100839071Sdfr	symnum = ef->chains[symnum];
100939071Sdfr    }
101039071Sdfr
101140254Speter    /* If we have not found it, look at the full table (if loaded) */
101240254Speter    if (ef->symtab == ef->ddbsymtab)
101340254Speter	return ENOENT;
101440254Speter
101540254Speter    /* Exhaustive search */
101640254Speter    for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
101740254Speter	strp = ef->ddbstrtab + symp->st_name;
101840254Speter	if (strcmp(name, strp) == 0) {
101940254Speter	    if (symp->st_shndx != SHN_UNDEF ||
102040254Speter		(symp->st_value != 0 &&
102140254Speter		 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
102243301Sdillon		*sym = (c_linker_sym_t) symp;
102340254Speter		return 0;
102440254Speter	    } else
102540254Speter		return ENOENT;
102640254Speter	}
102740254Speter    }
102840254Speter
102939071Sdfr    return ENOENT;
103038514Sdfr}
103138514Sdfr
103240156Speterstatic int
103343309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval)
103438514Sdfr{
103559603Sdfr	elf_file_t ef = (elf_file_t) lf;
103643311Sdillon	const Elf_Sym* es = (const Elf_Sym*) sym;
103738514Sdfr
1038102348Smarcel	if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) {
103940254Speter	    symval->name = ef->strtab + es->st_name;
104040254Speter	    symval->value = (caddr_t) ef->address + es->st_value;
104140254Speter	    symval->size = es->st_size;
104240254Speter	    return 0;
104340254Speter	}
104440254Speter	if (ef->symtab == ef->ddbsymtab)
104540254Speter	    return ENOENT;
1046102348Smarcel	if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
104740254Speter	    symval->name = ef->ddbstrtab + es->st_name;
104840254Speter	    symval->value = (caddr_t) ef->address + es->st_value;
104940254Speter	    symval->size = es->st_size;
105040254Speter	    return 0;
105140254Speter	}
105240254Speter	return ENOENT;
105338514Sdfr}
105438514Sdfr
105538514Sdfrstatic int
105638514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value,
105743301Sdillon		       c_linker_sym_t* sym, long* diffp)
105838514Sdfr{
105959603Sdfr	elf_file_t ef = (elf_file_t) lf;
106055090Sbde	u_long off = (uintptr_t) (void *) value;
106138514Sdfr	u_long diff = off;
106255090Sbde	u_long st_value;
106339071Sdfr	const Elf_Sym* es;
106439071Sdfr	const Elf_Sym* best = 0;
106538514Sdfr	int i;
106638514Sdfr
106740254Speter	for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
106838514Sdfr		if (es->st_name == 0)
106938514Sdfr			continue;
107055090Sbde		st_value = es->st_value + (uintptr_t) (void *) ef->address;
107153820Speter		if (off >= st_value) {
107253820Speter			if (off - st_value < diff) {
107353820Speter				diff = off - st_value;
107438514Sdfr				best = es;
107538514Sdfr				if (diff == 0)
107638514Sdfr					break;
107753820Speter			} else if (off - st_value == diff) {
107838514Sdfr				best = es;
107938514Sdfr			}
108038514Sdfr		}
108138514Sdfr	}
108238514Sdfr	if (best == 0)
108338514Sdfr		*diffp = off;
108438514Sdfr	else
108538514Sdfr		*diffp = diff;
108643301Sdillon	*sym = (c_linker_sym_t) best;
108738514Sdfr
108838514Sdfr	return 0;
108938514Sdfr}
109078161Speter
109178161Speter/*
109278161Speter * Look up a linker set on an ELF system.
109378161Speter */
109478161Speterstatic int
109578161Speterlink_elf_lookup_set(linker_file_t lf, const char *name,
109678161Speter		    void ***startp, void ***stopp, int *countp)
109778161Speter{
109878161Speter	c_linker_sym_t sym;
109978161Speter	linker_symval_t symval;
110078161Speter	char *setsym;
110178161Speter	void **start, **stop;
110278161Speter	int len, error = 0, count;
110378161Speter
110478161Speter	len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
110578161Speter	setsym = malloc(len, M_LINKER, M_WAITOK);
110678161Speter	if (setsym == NULL)
110778161Speter		return ENOMEM;
110878161Speter
110978161Speter	/* get address of first entry */
111078161Speter	snprintf(setsym, len, "%s%s", "__start_set_", name);
111178161Speter	error = link_elf_lookup_symbol(lf, setsym, &sym);
111278161Speter	if (error)
111378161Speter		goto out;
111478161Speter	link_elf_symbol_values(lf, sym, &symval);
111578161Speter	if (symval.value == 0) {
111678161Speter		error = ESRCH;
111778161Speter		goto out;
111878161Speter	}
111978161Speter	start = (void **)symval.value;
112078161Speter
112178161Speter	/* get address of last entry */
112278161Speter	snprintf(setsym, len, "%s%s", "__stop_set_", name);
112378161Speter	error = link_elf_lookup_symbol(lf, setsym, &sym);
112478161Speter	if (error)
112578161Speter		goto out;
112678161Speter	link_elf_symbol_values(lf, sym, &symval);
112778161Speter	if (symval.value == 0) {
112878161Speter		error = ESRCH;
112978161Speter		goto out;
113078161Speter	}
113178161Speter	stop = (void **)symval.value;
113278161Speter
113378161Speter	/* and the number of entries */
113478161Speter	count = stop - start;
113578161Speter
113678161Speter	/* and copy out */
113778161Speter	if (startp)
113878161Speter		*startp = start;
113978161Speter	if (stopp)
114078161Speter		*stopp = stop;
114178161Speter	if (countp)
114278161Speter		*countp = count;
114378161Speter
114478161Speterout:
114578161Speter	free(setsym, M_LINKER);
114678161Speter	return error;
114778161Speter}
114885736Sgreen
114985736Sgreenstatic int
115085736Sgreenlink_elf_each_function_name(linker_file_t file,
115185736Sgreen  int (*callback)(const char *, void *), void *opaque) {
115285736Sgreen    elf_file_t ef = (elf_file_t)file;
115385736Sgreen    const Elf_Sym* symp;
115485736Sgreen    int i, error;
115585736Sgreen
115685736Sgreen    /* Exhaustive search */
115785736Sgreen    for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
115885736Sgreen	if (symp->st_value != 0 &&
115985736Sgreen	    ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
116085736Sgreen		error = callback(ef->ddbstrtab + symp->st_name, opaque);
116185736Sgreen		if (error)
116285736Sgreen		    return (error);
116385736Sgreen	}
116485736Sgreen    }
116585736Sgreen    return (0);
116685736Sgreen}
116795228Smarcel
116895228Smarcel#ifdef __ia64__
116995228Smarcel/*
117095228Smarcel * Each KLD has its own GP. The GP value for each load module is given by
117195228Smarcel * DT_PLTGOT on ia64. We need GP to construct function descriptors, but
117295228Smarcel * don't have direct access to the ELF file structure. The link_elf_get_gp()
117395228Smarcel * function returns the GP given a pointer to a generic linker file struct.
117495228Smarcel */
117595228SmarcelElf_Addr
117695228Smarcellink_elf_get_gp(linker_file_t lf)
117795228Smarcel{
117895228Smarcel	elf_file_t ef = (elf_file_t)lf;
117995228Smarcel	return (Elf_Addr)ef->got;
118095228Smarcel}
118195228Smarcel#endif
118295410Smarcel
1183104072Sjakeconst Elf_Sym *
1184104072Sjakeelf_get_sym(linker_file_t lf, Elf_Word symidx)
1185104072Sjake{
1186104072Sjake	elf_file_t ef = (elf_file_t)lf;
1187104072Sjake
1188104072Sjake	if (symidx >= ef->nchains)
1189104072Sjake		return (NULL);
1190104072Sjake	return (ef->symtab + symidx);
1191104072Sjake}
1192104072Sjake
1193105147Smarcelconst char *
1194105147Smarcelelf_get_symname(linker_file_t lf, Elf_Word symidx)
1195105147Smarcel{
1196105147Smarcel	elf_file_t ef = (elf_file_t)lf;
1197105147Smarcel	const Elf_Sym *sym;
1198105147Smarcel
1199105147Smarcel	if (symidx >= ef->nchains)
1200105147Smarcel		return (NULL);
1201105147Smarcel	sym = ef->symtab + symidx;
1202105147Smarcel	return (ef->strtab + sym->st_name);
1203105147Smarcel}
1204105147Smarcel
120595410Smarcel/*
120695410Smarcel * Symbol lookup function that can be used when the symbol index is known (ie
120795410Smarcel * in relocations). It uses the symbol index instead of doing a fully fledged
120895410Smarcel * hash table based lookup when such is valid. For example for local symbols.
120995410Smarcel * This is not only more efficient, it's also more correct. It's not always
121095410Smarcel * the case that the symbol can be found through the hash table.
121195410Smarcel */
121295410SmarcelElf_Addr
121395410Smarcelelf_lookup(linker_file_t lf, Elf_Word symidx, int deps)
121495410Smarcel{
121595410Smarcel	elf_file_t ef = (elf_file_t)lf;
121695410Smarcel	const Elf_Sym *sym;
121795410Smarcel	const char *symbol;
121895410Smarcel
121995410Smarcel	/* Don't even try to lookup the symbol if the index is bogus. */
122095410Smarcel	if (symidx >= ef->nchains)
122195410Smarcel		return (0);
122295410Smarcel
122395410Smarcel	sym = ef->symtab + symidx;
122495410Smarcel
122595410Smarcel	/*
122695410Smarcel	 * Don't do a full lookup when the symbol is local. It may even
122795410Smarcel	 * fail because it may not be found through the hash table.
122895410Smarcel	 */
122995410Smarcel	if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
123095410Smarcel		/* Force lookup failure when we have an insanity. */
123195410Smarcel		if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0)
123295410Smarcel			return (0);
123395410Smarcel		return ((Elf_Addr)ef->address + sym->st_value);
123495410Smarcel	}
123595410Smarcel
123695410Smarcel	/*
123795410Smarcel	 * XXX we can avoid doing a hash table based lookup for global
123895410Smarcel	 * symbols as well. This however is not always valid, so we'll
123995410Smarcel	 * just do it the hard way for now. Performance tweaks can
124095410Smarcel	 * always be added.
124195410Smarcel	 */
124295410Smarcel
124395410Smarcel	symbol = ef->strtab + sym->st_name;
124495410Smarcel
124595410Smarcel	/* Force a lookup failure if the symbol name is bogus. */
124695410Smarcel	if (*symbol == 0)
124795410Smarcel		return (0);
124895410Smarcel
124995410Smarcel	return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
125095410Smarcel}
1251