link_elf.c revision 86469
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 86469 2001-11-16 21:08:40Z iedowse $
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
5873016Speter#ifdef __AOUT__
5973016Speter#include <nlist.h>
6073016Speter#endif
6159603Sdfr#include <link.h>
6239071Sdfr
6359603Sdfr#include "linker_if.h"
6438514Sdfr
6538514Sdfrtypedef struct elf_file {
6659603Sdfr    struct linker_file	lf;		/* Common fields */
6759603Sdfr    int			preloaded;	/* Was file pre-loaded */
6839071Sdfr    caddr_t		address;	/* Relocation address */
6939071Sdfr#ifdef SPARSE_MAPPING
7039071Sdfr    vm_object_t		object;		/* VM object to hold file pages */
7139071Sdfr#endif
7259603Sdfr    Elf_Dyn*		dynamic;	/* Symbol table etc. */
7380700Sjake    Elf_Hashelt		nbuckets;	/* DT_HASH info */
7480700Sjake    Elf_Hashelt		nchains;
7580700Sjake    const Elf_Hashelt*	buckets;
7680700Sjake    const Elf_Hashelt*	chains;
7738514Sdfr    caddr_t		hash;
7838514Sdfr    caddr_t		strtab;		/* DT_STRTAB */
7940254Speter    int			strsz;		/* DT_STRSZ */
8039071Sdfr    const Elf_Sym*	symtab;		/* DT_SYMTAB */
8139071Sdfr    Elf_Addr*		got;		/* DT_PLTGOT */
8239071Sdfr    const Elf_Rel*	pltrel;		/* DT_JMPREL */
8339071Sdfr    int			pltrelsize;	/* DT_PLTRELSZ */
8439071Sdfr    const Elf_Rela*	pltrela;	/* DT_JMPREL */
8539071Sdfr    int			pltrelasize;	/* DT_PLTRELSZ */
8639071Sdfr    const Elf_Rel*	rel;		/* DT_REL */
8739071Sdfr    int			relsize;	/* DT_RELSZ */
8839071Sdfr    const Elf_Rela*	rela;		/* DT_RELA */
8939071Sdfr    int			relasize;	/* DT_RELASZ */
9040254Speter    caddr_t		modptr;
9140254Speter    const Elf_Sym*	ddbsymtab;	/* The symbol table we are using */
9240254Speter    long		ddbsymcnt;	/* Number of symbols */
9340254Speter    caddr_t		ddbstrtab;	/* String table */
9440254Speter    long		ddbstrcnt;	/* number of bytes in string table */
9540292Speter    caddr_t		symbase;	/* malloc'ed symbold base */
9640292Speter    caddr_t		strbase;	/* malloc'ed string base */
9759603Sdfr#ifdef DDB
9859603Sdfr    struct link_map	gdb;		/* hooks for gdb */
9959603Sdfr#endif
10038514Sdfr} *elf_file_t;
10138514Sdfr
10259751Speterstatic int	link_elf_link_preload(linker_class_t cls,
10359751Speter				      const char*, linker_file_t*);
10459751Speterstatic int	link_elf_link_preload_finish(linker_file_t);
10559751Speterstatic int	link_elf_load_file(linker_class_t, const char*, linker_file_t*);
10659603Sdfrstatic int	link_elf_lookup_symbol(linker_file_t, const char*,
10759603Sdfr				       c_linker_sym_t*);
10859603Sdfrstatic int	link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*);
10959603Sdfrstatic int	link_elf_search_symbol(linker_file_t, caddr_t value,
11059603Sdfr				       c_linker_sym_t* sym, long* diffp);
11138514Sdfr
11259603Sdfrstatic void	link_elf_unload_file(linker_file_t);
11359751Speterstatic void	link_elf_unload_preload(linker_file_t);
11478161Speterstatic int	link_elf_lookup_set(linker_file_t, const char *,
11578161Speter				    void ***, void ***, int *);
11685736Sgreenstatic int	link_elf_each_function_name(linker_file_t,
11785736Sgreen				int (*)(const char *, void *),
11885736Sgreen				void *);
11959603Sdfr
12059603Sdfrstatic kobj_method_t link_elf_methods[] = {
12159603Sdfr    KOBJMETHOD(linker_lookup_symbol,	link_elf_lookup_symbol),
12259603Sdfr    KOBJMETHOD(linker_symbol_values,	link_elf_symbol_values),
12359603Sdfr    KOBJMETHOD(linker_search_symbol,	link_elf_search_symbol),
12459603Sdfr    KOBJMETHOD(linker_unload,		link_elf_unload_file),
12559751Speter    KOBJMETHOD(linker_load_file,	link_elf_load_file),
12659751Speter    KOBJMETHOD(linker_link_preload,	link_elf_link_preload),
12759751Speter    KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish),
12878161Speter    KOBJMETHOD(linker_lookup_set,	link_elf_lookup_set),
12985736Sgreen    KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
13059603Sdfr    { 0, 0 }
13159603Sdfr};
13259603Sdfr
13359603Sdfrstatic struct linker_class link_elf_class = {
13459603Sdfr#if ELF_TARG_CLASS == ELFCLASS32
13559603Sdfr    "elf32",
13659603Sdfr#else
13759603Sdfr    "elf64",
13859603Sdfr#endif
13959603Sdfr    link_elf_methods, sizeof(struct elf_file)
14059603Sdfr};
14159603Sdfr
14259603Sdfrstatic int		parse_dynamic(elf_file_t ef);
14359603Sdfrstatic int		relocate_file(elf_file_t ef);
14459751Speterstatic int		link_elf_preload_parse_symbols(elf_file_t ef);
14559603Sdfr
14659603Sdfr#ifdef DDB
14766719Sjhbstatic void		r_debug_state(struct r_debug *dummy_one,
14866719Sjhb				      struct link_map *dummy_two);
14959603Sdfr
15038514Sdfr/*
15159603Sdfr * A list of loaded modules for GDB to use for loading symbols.
15259603Sdfr */
15359603Sdfrstruct r_debug r_debug;
15459603Sdfr
15566719Sjhb#define GDB_STATE(s)	r_debug.r_state = s; r_debug_state(NULL, NULL);
15659603Sdfr
15759603Sdfr/*
15859603Sdfr * Function for the debugger to set a breakpoint on to gain control.
15959603Sdfr */
16059603Sdfrvoid
16166719Sjhbr_debug_state(struct r_debug *dummy_one __unused,
16266719Sjhb	      struct link_map *dummy_two __unused)
16359603Sdfr{
16459603Sdfr}
16559603Sdfr
16659603Sdfr#endif
16759603Sdfr
16859603Sdfr/*
16938514Sdfr * The kernel symbol table starts here.
17038514Sdfr */
17138514Sdfrextern struct _dynamic _DYNAMIC;
17238514Sdfr
17338514Sdfrstatic void
17438514Sdfrlink_elf_init(void* arg)
17538514Sdfr{
17640156Speter#ifdef __ELF__
17740156Speter    Elf_Dyn	*dp;
17840156Speter    caddr_t	modptr, baseptr, sizeptr;
17940156Speter    elf_file_t	ef;
18040156Speter    char	*modname;
18181500Swpaul#ifdef DDB
18281500Swpaul    char *newfilename;
18340156Speter#endif
18481500Swpaul#endif
18538514Sdfr
18659603Sdfr    linker_add_class(&link_elf_class);
18738514Sdfr
18840156Speter#ifdef __ELF__
18940156Speter    dp = (Elf_Dyn*) &_DYNAMIC;
19082848Speter    modname = NULL;
19182848Speter    modptr = preload_search_by_type("elf kernel");
19282848Speter    if (modptr)
19382848Speter	modname = (char *)preload_search_info(modptr, MODINFO_NAME);
19482848Speter    if (modname == NULL)
19582848Speter	modname = "kernel";
19682848Speter    linker_kernel_file = linker_make_file(modname, &link_elf_class);
19782848Speter    if (linker_kernel_file == NULL)
19882848Speter	panic("link_elf_init: Can't create linker structures for kernel");
19982848Speter
20082848Speter    ef = (elf_file_t) linker_kernel_file;
20182848Speter    ef->preloaded = 1;
20282848Speter    ef->address = 0;
20359603Sdfr#ifdef SPARSE_MAPPING
20482848Speter    ef->object = 0;
20559603Sdfr#endif
20682848Speter    ef->dynamic = dp;
20759603Sdfr
20882848Speter    if (dp)
20982848Speter	parse_dynamic(ef);
21082848Speter    linker_kernel_file->address = (caddr_t) KERNBASE;
21182848Speter    linker_kernel_file->size = -(intptr_t)linker_kernel_file->address;
21240156Speter
21382848Speter    if (modptr) {
21482848Speter	ef->modptr = modptr;
21582848Speter	baseptr = preload_search_info(modptr, MODINFO_ADDR);
21682848Speter	if (baseptr)
21782848Speter	    linker_kernel_file->address = *(caddr_t *)baseptr;
21882848Speter	sizeptr = preload_search_info(modptr, MODINFO_SIZE);
21982848Speter	if (sizeptr)
22082848Speter	    linker_kernel_file->size = *(size_t *)sizeptr;
22182848Speter    }
22282848Speter    (void)link_elf_preload_parse_symbols(ef);
22359603Sdfr
22459603Sdfr#ifdef DDB
22582848Speter    ef->gdb.l_addr = linker_kernel_file->address;
22682848Speter    newfilename = malloc(strlen(modname) + 1, M_LINKER, M_WAITOK);
22782848Speter    strcpy(newfilename, modname);
22882848Speter    ef->gdb.l_name = newfilename;
22982848Speter    ef->gdb.l_ld = dp;
23082848Speter    ef->gdb.l_prev = 0;
23182848Speter    ef->gdb.l_next = 0;
23259603Sdfr
23382848Speter    r_debug.r_map = &ef->gdb;
23482848Speter    r_debug.r_brk = r_debug_state;
23582848Speter    r_debug.r_state = RT_CONSISTENT;
23659603Sdfr
23782848Speter    r_debug_state(NULL, NULL);	/* say hello to gdb! */
23859603Sdfr#endif
23959603Sdfr
24040156Speter#endif
24138514Sdfr}
24238514Sdfr
24340156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
24438514Sdfr
24538514Sdfrstatic int
24659751Speterlink_elf_preload_parse_symbols(elf_file_t ef)
24740254Speter{
24840254Speter    caddr_t	pointer;
24940254Speter    caddr_t	ssym, esym, base;
25040254Speter    caddr_t	strtab;
25140254Speter    int		strcnt;
25240254Speter    Elf_Sym*	symtab;
25340254Speter    int		symcnt;
25440254Speter
25540292Speter    if (ef->modptr == NULL)
25640292Speter	return 0;
25740254Speter    pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM);
25840254Speter    if (pointer == NULL)
25940254Speter	return 0;
26040254Speter    ssym = *(caddr_t *)pointer;
26140254Speter    pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM);
26240254Speter    if (pointer == NULL)
26340254Speter	return 0;
26440254Speter    esym = *(caddr_t *)pointer;
26540254Speter
26640254Speter    base = ssym;
26740254Speter
26840254Speter    symcnt = *(long *)base;
26940254Speter    base += sizeof(long);
27040254Speter    symtab = (Elf_Sym *)base;
27140254Speter    base += roundup(symcnt, sizeof(long));
27240254Speter
27340254Speter    if (base > esym || base < ssym) {
27440254Speter	printf("Symbols are corrupt!\n");
27540254Speter	return EINVAL;
27640254Speter    }
27740254Speter
27840254Speter    strcnt = *(long *)base;
27940254Speter    base += sizeof(long);
28040254Speter    strtab = base;
28140254Speter    base += roundup(strcnt, sizeof(long));
28240254Speter
28340254Speter    if (base > esym || base < ssym) {
28440254Speter	printf("Symbols are corrupt!\n");
28540254Speter	return EINVAL;
28640254Speter    }
28740254Speter
28840254Speter    ef->ddbsymtab = symtab;
28940254Speter    ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
29040254Speter    ef->ddbstrtab = strtab;
29140254Speter    ef->ddbstrcnt = strcnt;
29240254Speter
29340254Speter    return 0;
29440254Speter}
29540254Speter
29640254Speterstatic int
29759603Sdfrparse_dynamic(elf_file_t ef)
29838514Sdfr{
29959603Sdfr    Elf_Dyn *dp;
30039071Sdfr    int plttype = DT_REL;
30138514Sdfr
30238514Sdfr    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
30338514Sdfr	switch (dp->d_tag) {
30438514Sdfr	case DT_HASH:
30538514Sdfr	{
30638514Sdfr	    /* From src/libexec/rtld-elf/rtld.c */
30780700Sjake	    const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
30838514Sdfr		(ef->address + dp->d_un.d_ptr);
30938514Sdfr	    ef->nbuckets = hashtab[0];
31038514Sdfr	    ef->nchains = hashtab[1];
31138514Sdfr	    ef->buckets = hashtab + 2;
31238514Sdfr	    ef->chains = ef->buckets + ef->nbuckets;
31338514Sdfr	    break;
31438514Sdfr	}
31538514Sdfr	case DT_STRTAB:
31639071Sdfr	    ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr);
31738514Sdfr	    break;
31840254Speter	case DT_STRSZ:
31940254Speter	    ef->strsz = dp->d_un.d_val;
32040254Speter	    break;
32138514Sdfr	case DT_SYMTAB:
32239071Sdfr	    ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr);
32338514Sdfr	    break;
32438514Sdfr	case DT_SYMENT:
32538514Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Sym))
32638514Sdfr		return ENOEXEC;
32739071Sdfr	    break;
32839071Sdfr	case DT_PLTGOT:
32939071Sdfr	    ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr);
33039071Sdfr	    break;
33139071Sdfr	case DT_REL:
33239071Sdfr	    ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr);
33339071Sdfr	    break;
33439071Sdfr	case DT_RELSZ:
33539071Sdfr	    ef->relsize = dp->d_un.d_val;
33639071Sdfr	    break;
33739071Sdfr	case DT_RELENT:
33839071Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Rel))
33939071Sdfr		return ENOEXEC;
34039071Sdfr	    break;
34139071Sdfr	case DT_JMPREL:
34239071Sdfr	    ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr);
34339071Sdfr	    break;
34439071Sdfr	case DT_PLTRELSZ:
34539071Sdfr	    ef->pltrelsize = dp->d_un.d_val;
34639071Sdfr	    break;
34739071Sdfr	case DT_RELA:
34839071Sdfr	    ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr);
34939071Sdfr	    break;
35039071Sdfr	case DT_RELASZ:
35139071Sdfr	    ef->relasize = dp->d_un.d_val;
35239071Sdfr	    break;
35339071Sdfr	case DT_RELAENT:
35439071Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Rela))
35539071Sdfr		return ENOEXEC;
35639071Sdfr	    break;
35739071Sdfr	case DT_PLTREL:
35839071Sdfr	    plttype = dp->d_un.d_val;
35939071Sdfr	    if (plttype != DT_REL && plttype != DT_RELA)
36039071Sdfr		return ENOEXEC;
36139071Sdfr	    break;
36259603Sdfr#ifdef DDB
36359603Sdfr	case DT_DEBUG:
36459603Sdfr	    dp->d_un.d_ptr = (Elf_Addr) &r_debug;
36559603Sdfr	    break;
36659603Sdfr#endif
36738514Sdfr	}
36838514Sdfr    }
36939071Sdfr
37039071Sdfr    if (plttype == DT_RELA) {
37139071Sdfr	ef->pltrela = (const Elf_Rela *) ef->pltrel;
37239071Sdfr	ef->pltrel = NULL;
37339071Sdfr	ef->pltrelasize = ef->pltrelsize;
37439071Sdfr	ef->pltrelsize = 0;
37539071Sdfr    }
37639071Sdfr
37740254Speter    ef->ddbsymtab = ef->symtab;
37840254Speter    ef->ddbsymcnt = ef->nchains;
37940254Speter    ef->ddbstrtab = ef->strtab;
38040254Speter    ef->ddbstrcnt = ef->strsz;
38140254Speter
38238514Sdfr    return 0;
38338514Sdfr}
38438514Sdfr
38539071Sdfrstatic void
38639071Sdfrlink_elf_error(const char *s)
38739071Sdfr{
38839071Sdfr    printf("kldload: %s\n", s);
38939071Sdfr}
39039071Sdfr
39159603Sdfr#ifdef DDB
39259603Sdfr
39359603Sdfrstatic void
39459603Sdfrlink_elf_add_gdb(struct link_map *l)
39559603Sdfr{
39659603Sdfr    struct link_map *prev;
39759603Sdfr
39859603Sdfr    /*
39959603Sdfr     * Scan to the end of the list.
40059603Sdfr     */
40159603Sdfr    for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next)
40259603Sdfr	;
40359603Sdfr
40459603Sdfr    /* Link in the new entry. */
40559603Sdfr    l->l_prev = prev;
40659603Sdfr    l->l_next = prev->l_next;
40759603Sdfr    prev->l_next = l;
40859603Sdfr}
40959603Sdfr
41059603Sdfrstatic void
41159603Sdfrlink_elf_delete_gdb(struct link_map *l)
41259603Sdfr{
41359603Sdfr    if (l->l_prev == NULL) {
41459603Sdfr	if ((r_debug.r_map = l->l_next) != NULL)
41559603Sdfr	    l->l_next->l_prev = NULL;
41659603Sdfr	return;
41759603Sdfr    }
41859603Sdfr
41959603Sdfr    if ((l->l_prev->l_next = l->l_next) != NULL)
42059603Sdfr	l->l_next->l_prev = l->l_prev;
42159603Sdfr}
42259603Sdfr
42359603Sdfr#endif /* DDB */
42459603Sdfr
42538514Sdfrstatic int
42659751Speterlink_elf_link_preload(linker_class_t cls,
42759751Speter		      const char* filename, linker_file_t *result)
42840156Speter{
42940156Speter    caddr_t		modptr, baseptr, sizeptr, dynptr;
43040156Speter    char		*type;
43140156Speter    elf_file_t		ef;
43240156Speter    linker_file_t	lf;
43340156Speter    int			error;
43440156Speter    vm_offset_t		dp;
43540156Speter
43659751Speter    /* Look to see if we have the file preloaded */
43740156Speter    modptr = preload_search_by_name(filename);
43840156Speter    if (modptr == NULL)
43959751Speter	return ENOENT;
44040156Speter
44140156Speter    type = (char *)preload_search_info(modptr, MODINFO_TYPE);
44240156Speter    baseptr = preload_search_info(modptr, MODINFO_ADDR);
44340156Speter    sizeptr = preload_search_info(modptr, MODINFO_SIZE);
44440156Speter    dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC);
44540156Speter    if (type == NULL || strcmp(type, "elf module") != 0)
44640156Speter	return (EFTYPE);
44740156Speter    if (baseptr == NULL || sizeptr == NULL || dynptr == NULL)
44840156Speter	return (EINVAL);
44940156Speter
45059603Sdfr    lf = linker_make_file(filename, &link_elf_class);
45159603Sdfr    if (lf == NULL) {
45259603Sdfr	return ENOMEM;
45359603Sdfr    }
45459603Sdfr
45559603Sdfr    ef = (elf_file_t) lf;
45659603Sdfr    ef->preloaded = 1;
45740292Speter    ef->modptr = modptr;
45840156Speter    ef->address = *(caddr_t *)baseptr;
45940156Speter#ifdef SPARSE_MAPPING
46040156Speter    ef->object = 0;
46140156Speter#endif
46240156Speter    dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr;
46340156Speter    ef->dynamic = (Elf_Dyn *)dp;
46440156Speter    lf->address = ef->address;
46540156Speter    lf->size = *(size_t *)sizeptr;
46640156Speter
46759603Sdfr    error = parse_dynamic(ef);
46840156Speter    if (error) {
46940156Speter	linker_file_unload(lf);
47040156Speter	return error;
47140156Speter    }
47259751Speter    *result = lf;
47359751Speter    return (0);
47459751Speter}
47559751Speter
47659751Speterstatic int
47759751Speterlink_elf_link_preload_finish(linker_file_t lf)
47859751Speter{
47959751Speter    elf_file_t		ef;
48059751Speter    int error;
48181500Swpaul#ifdef DDB
48281500Swpaul    char *newfilename;
48381500Swpaul#endif
48459751Speter
48559751Speter    ef = (elf_file_t) lf;
48659751Speter#if 0	/* this will be more trouble than it's worth for now */
48759751Speter    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
48859751Speter	if (dp->d_tag != DT_NEEDED)
48959751Speter	    continue;
49059751Speter	modname = ef->strtab + dp->d_un.d_val;
49159751Speter	error = linker_load_module(modname, lf);
49259751Speter	if (error)
49359751Speter	    goto out;
49440156Speter    }
49559751Speter#endif
49659603Sdfr    error = relocate_file(ef);
49759751Speter    if (error)
49840156Speter	return error;
49959751Speter    (void)link_elf_preload_parse_symbols(ef);
50059603Sdfr
50159603Sdfr#ifdef DDB
50259603Sdfr    GDB_STATE(RT_ADD);
50359603Sdfr    ef->gdb.l_addr = lf->address;
50481500Swpaul    newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK);
50581500Swpaul    strcpy(newfilename, lf->filename);
50681500Swpaul    ef->gdb.l_name = newfilename;
50759603Sdfr    ef->gdb.l_ld = ef->dynamic;
50859603Sdfr    link_elf_add_gdb(&ef->gdb);
50959603Sdfr    GDB_STATE(RT_CONSISTENT);
51059603Sdfr#endif
51159603Sdfr
51240156Speter    return (0);
51340156Speter}
51440156Speter
51540156Speterstatic int
51659751Speterlink_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result)
51738514Sdfr{
51838514Sdfr    struct nameidata nd;
51983366Sjulian    struct thread* td = curthread;	/* XXX */
52083366Sjulian    struct proc* p = td->td_proc;	/* XXX */
52140962Speter    Elf_Ehdr *hdr;
52240962Speter    caddr_t firstpage;
52339071Sdfr    int nbytes, i;
52439071Sdfr    Elf_Phdr *phdr;
52539071Sdfr    Elf_Phdr *phlimit;
52639071Sdfr    Elf_Phdr *segs[2];
52739071Sdfr    int nsegs;
52839071Sdfr    Elf_Phdr *phdyn;
52939071Sdfr    Elf_Phdr *phphdr;
53039071Sdfr    caddr_t mapbase;
53139071Sdfr    size_t mapsize;
53239071Sdfr    Elf_Off base_offset;
53339071Sdfr    Elf_Addr base_vaddr;
53439071Sdfr    Elf_Addr base_vlimit;
53538514Sdfr    int error = 0;
53662550Smckusick    int resid, flags;
53738514Sdfr    elf_file_t ef;
53838514Sdfr    linker_file_t lf;
53940292Speter    Elf_Shdr *shdr;
54040292Speter    int symtabindex;
54140292Speter    int symstrindex;
54240292Speter    int symcnt;
54340292Speter    int strcnt;
54481500Swpaul#ifdef DDB
54581201Sgreen    char *newfilename;
54681500Swpaul#endif
54738514Sdfr
54879224Sdillon    GIANT_REQUIRED;
54979224Sdillon
55040292Speter    shdr = NULL;
55140292Speter    lf = NULL;
55240292Speter
55383366Sjulian    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
55462550Smckusick    flags = FREAD;
55562550Smckusick    error = vn_open(&nd, &flags, 0);
55638514Sdfr    if (error)
55738514Sdfr	return error;
55854655Seivind    NDFREE(&nd, NDF_ONLY_PNBUF);
55938514Sdfr
56038514Sdfr    /*
56139071Sdfr     * Read the elf header from the file.
56238514Sdfr     */
56340962Speter    firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
56440962Speter    if (firstpage == NULL) {
56540962Speter	error = ENOMEM;
56640962Speter	goto out;
56740962Speter    }
56840962Speter    hdr = (Elf_Ehdr *)firstpage;
56940962Speter    error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0,
57083366Sjulian		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
57140962Speter    nbytes = PAGE_SIZE - resid;
57238514Sdfr    if (error)
57338514Sdfr	goto out;
57438514Sdfr
57540962Speter    if (!IS_ELF(*hdr)) {
57639071Sdfr	error = ENOEXEC;
57738514Sdfr	goto out;
57839071Sdfr    }
57938514Sdfr
58040962Speter    if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
58140962Speter      || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
58239071Sdfr	link_elf_error("Unsupported file layout");
58339071Sdfr	error = ENOEXEC;
58439071Sdfr	goto out;
58539071Sdfr    }
58640962Speter    if (hdr->e_ident[EI_VERSION] != EV_CURRENT
58740962Speter      || hdr->e_version != EV_CURRENT) {
58839071Sdfr	link_elf_error("Unsupported file version");
58939071Sdfr	error = ENOEXEC;
59039071Sdfr	goto out;
59139071Sdfr    }
59240962Speter    if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
59339071Sdfr	link_elf_error("Unsupported file type");
59439071Sdfr	error = ENOEXEC;
59539071Sdfr	goto out;
59639071Sdfr    }
59740962Speter    if (hdr->e_machine != ELF_TARG_MACH) {
59839071Sdfr	link_elf_error("Unsupported machine");
59939071Sdfr	error = ENOEXEC;
60039071Sdfr	goto out;
60139071Sdfr    }
60239071Sdfr
60338514Sdfr    /*
60439071Sdfr     * We rely on the program header being in the first page.  This is
60539071Sdfr     * not strictly required by the ABI specification, but it seems to
60639071Sdfr     * always true in practice.  And, it simplifies things considerably.
60738514Sdfr     */
60840962Speter    if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) &&
60940962Speter	  (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) &&
61040962Speter	  (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes)))
61139071Sdfr	link_elf_error("Unreadable program headers");
61239071Sdfr
61338514Sdfr    /*
61439071Sdfr     * Scan the program header entries, and save key information.
61539071Sdfr     *
61639071Sdfr     * We rely on there being exactly two load segments, text and data,
61739071Sdfr     * in that order.
61838514Sdfr     */
61940962Speter    phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff);
62040962Speter    phlimit = phdr + hdr->e_phnum;
62139071Sdfr    nsegs = 0;
62239071Sdfr    phdyn = NULL;
62339071Sdfr    phphdr = NULL;
62439071Sdfr    while (phdr < phlimit) {
62539071Sdfr	switch (phdr->p_type) {
62639071Sdfr
62739071Sdfr	case PT_LOAD:
62839071Sdfr	    if (nsegs == 2) {
62939071Sdfr		link_elf_error("Too many sections");
63039071Sdfr		error = ENOEXEC;
63139071Sdfr		goto out;
63239071Sdfr	    }
63339071Sdfr	    segs[nsegs] = phdr;
63439071Sdfr	    ++nsegs;
63539071Sdfr	    break;
63639071Sdfr
63739071Sdfr	case PT_PHDR:
63839071Sdfr	    phphdr = phdr;
63939071Sdfr	    break;
64039071Sdfr
64139071Sdfr	case PT_DYNAMIC:
64239071Sdfr	    phdyn = phdr;
64339071Sdfr	    break;
64465503Sbp
64565503Sbp	case PT_INTERP:
64665503Sbp	    link_elf_error("Unsupported file type");
64765503Sbp	    error = ENOEXEC;
64865503Sbp	    goto out;
64939071Sdfr	}
65039071Sdfr
65139071Sdfr	++phdr;
65239071Sdfr    }
65339071Sdfr    if (phdyn == NULL) {
65439071Sdfr	link_elf_error("Object is not dynamically-linked");
65539071Sdfr	error = ENOEXEC;
65638514Sdfr	goto out;
65739071Sdfr    }
65838514Sdfr
65938514Sdfr    /*
66039071Sdfr     * Allocate the entire address space of the object, to stake out our
66139071Sdfr     * contiguous region, and to establish the base address for relocation.
66238514Sdfr     */
66339071Sdfr    base_offset = trunc_page(segs[0]->p_offset);
66439071Sdfr    base_vaddr = trunc_page(segs[0]->p_vaddr);
66539071Sdfr    base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz);
66639071Sdfr    mapsize = base_vlimit - base_vaddr;
66739071Sdfr
66859603Sdfr    lf = linker_make_file(filename, &link_elf_class);
66959603Sdfr    if (!lf) {
67059603Sdfr	error = ENOMEM;
67159603Sdfr	goto out;
67259603Sdfr    }
67359603Sdfr
67459603Sdfr    ef = (elf_file_t) lf;
67539071Sdfr#ifdef SPARSE_MAPPING
67639071Sdfr    ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT);
67739071Sdfr    if (ef->object == NULL) {
67838514Sdfr	free(ef, M_LINKER);
67939071Sdfr	error = ENOMEM;
68038514Sdfr	goto out;
68138514Sdfr    }
68239071Sdfr    vm_object_reference(ef->object);
68339071Sdfr    ef->address = (caddr_t) vm_map_min(kernel_map);
68439071Sdfr    error = vm_map_find(kernel_map, ef->object, 0,
68539071Sdfr			(vm_offset_t *) &ef->address,
68639071Sdfr			mapsize, 1,
68739071Sdfr			VM_PROT_ALL, VM_PROT_ALL, 0);
68839071Sdfr    if (error) {
68939071Sdfr	vm_object_deallocate(ef->object);
69059603Sdfr	ef->object = 0;
69139071Sdfr	goto out;
69239071Sdfr    }
69339071Sdfr#else
69439071Sdfr    ef->address = malloc(mapsize, M_LINKER, M_WAITOK);
69559603Sdfr    if (!ef->address) {
69659603Sdfr	error = ENOMEM;
69759603Sdfr	goto out;
69859603Sdfr    }
69939071Sdfr#endif
70039071Sdfr    mapbase = ef->address;
70138514Sdfr
70239071Sdfr    /*
70339071Sdfr     * Read the text and data sections and zero the bss.
70439071Sdfr     */
70539071Sdfr    for (i = 0; i < 2; i++) {
70639071Sdfr	caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
70739071Sdfr	error = vn_rdwr(UIO_READ, nd.ni_vp,
70839071Sdfr			segbase, segs[i]->p_filesz, segs[i]->p_offset,
70983366Sjulian			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
71039071Sdfr	if (error) {
71139071Sdfr	    goto out;
71239071Sdfr	}
71339071Sdfr	bzero(segbase + segs[i]->p_filesz,
71439071Sdfr	      segs[i]->p_memsz - segs[i]->p_filesz);
71539071Sdfr
71639071Sdfr#ifdef SPARSE_MAPPING
71739071Sdfr	/*
71839071Sdfr	 * Wire down the pages
71939071Sdfr	 */
72039071Sdfr	vm_map_pageable(kernel_map,
72139071Sdfr			(vm_offset_t) segbase,
72239071Sdfr			(vm_offset_t) segbase + segs[i]->p_memsz,
72339071Sdfr			FALSE);
72439071Sdfr#endif
72539071Sdfr    }
72639071Sdfr
72785734Sgreen#ifdef GPROF
72885734Sgreen    /* Update profiling information with the new text segment. */
72985734Sgreen    kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr +
73085734Sgreen	segs[0]->p_memsz));
73185734Sgreen#endif
73285734Sgreen
73359603Sdfr    ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
73439071Sdfr
73538514Sdfr    lf->address = ef->address;
73639071Sdfr    lf->size = mapsize;
73738514Sdfr
73859603Sdfr    error = parse_dynamic(ef);
73940292Speter    if (error)
74038514Sdfr	goto out;
74186469Siedowse    error = linker_load_dependencies(lf);
74240292Speter    if (error)
74340156Speter	goto out;
74459751Speter#if 0	/* this will be more trouble than it's worth for now */
74559751Speter    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
74659751Speter	if (dp->d_tag != DT_NEEDED)
74759751Speter	    continue;
74859751Speter	modname = ef->strtab + dp->d_un.d_val;
74959751Speter	error = linker_load_module(modname, lf);
75059751Speter	if (error)
75159751Speter	    goto out;
75259751Speter    }
75359751Speter#endif
75459603Sdfr    error = relocate_file(ef);
75540292Speter    if (error)
75640156Speter	goto out;
75740292Speter
75840292Speter    /* Try and load the symbol table if it's present.  (you can strip it!) */
75940962Speter    nbytes = hdr->e_shnum * hdr->e_shentsize;
76040962Speter    if (nbytes == 0 || hdr->e_shoff == 0)
76140292Speter	goto nosyms;
76269781Sdwmalone    shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO);
76340292Speter    if (shdr == NULL) {
76440292Speter	error = ENOMEM;
76540292Speter	goto out;
76640156Speter    }
76740292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
76840962Speter		    (caddr_t)shdr, nbytes, hdr->e_shoff,
76983366Sjulian		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
77040292Speter    if (error)
77140292Speter	goto out;
77240292Speter    symtabindex = -1;
77340292Speter    symstrindex = -1;
77440962Speter    for (i = 0; i < hdr->e_shnum; i++) {
77540292Speter	if (shdr[i].sh_type == SHT_SYMTAB) {
77640292Speter	    symtabindex = i;
77740292Speter	    symstrindex = shdr[i].sh_link;
77840292Speter	}
77940292Speter    }
78040292Speter    if (symtabindex < 0 || symstrindex < 0)
78140292Speter	goto nosyms;
78240156Speter
78340292Speter    symcnt = shdr[symtabindex].sh_size;
78440292Speter    ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK);
78540292Speter    strcnt = shdr[symstrindex].sh_size;
78640292Speter    ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK);
78740292Speter
78840292Speter    if (ef->symbase == NULL || ef->strbase == NULL) {
78940292Speter	error = ENOMEM;
79040292Speter	goto out;
79140292Speter    }
79240292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
79340292Speter		    ef->symbase, symcnt, shdr[symtabindex].sh_offset,
79483366Sjulian		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
79540292Speter    if (error)
79640292Speter	goto out;
79740292Speter    error = vn_rdwr(UIO_READ, nd.ni_vp,
79840292Speter		    ef->strbase, strcnt, shdr[symstrindex].sh_offset,
79983366Sjulian		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
80040292Speter    if (error)
80140292Speter	goto out;
80240292Speter
80340292Speter    ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
80440292Speter    ef->ddbsymtab = (const Elf_Sym *)ef->symbase;
80540292Speter    ef->ddbstrcnt = strcnt;
80640292Speter    ef->ddbstrtab = ef->strbase;
80740292Speter
80859603Sdfr#ifdef DDB
80959603Sdfr    GDB_STATE(RT_ADD);
81059603Sdfr    ef->gdb.l_addr = lf->address;
81181201Sgreen    newfilename = malloc(strlen(filename) + 1, M_LINKER, M_WAITOK);
81281201Sgreen    strcpy(newfilename, filename);
81381201Sgreen    ef->gdb.l_name = (const char *)newfilename;
81459603Sdfr    ef->gdb.l_ld = ef->dynamic;
81559603Sdfr    link_elf_add_gdb(&ef->gdb);
81659603Sdfr    GDB_STATE(RT_CONSISTENT);
81759603Sdfr#endif
81859603Sdfr
81940292Speternosyms:
82040292Speter
82138514Sdfr    *result = lf;
82238514Sdfr
82338514Sdfrout:
82440292Speter    if (error && lf)
82540292Speter	linker_file_unload(lf);
82640292Speter    if (shdr)
82740292Speter	free(shdr, M_LINKER);
82840962Speter    if (firstpage)
82940962Speter	free(firstpage, M_LINKER);
83083366Sjulian    VOP_UNLOCK(nd.ni_vp, 0, td);
83183366Sjulian    vn_close(nd.ni_vp, FREAD, p->p_ucred, td);
83238514Sdfr
83338514Sdfr    return error;
83438514Sdfr}
83538514Sdfr
83638514Sdfrstatic void
83740156Speterlink_elf_unload_file(linker_file_t file)
83838514Sdfr{
83959603Sdfr    elf_file_t ef = (elf_file_t) file;
84038514Sdfr
84159603Sdfr#ifdef DDB
84259603Sdfr    if (ef->gdb.l_ld) {
84359603Sdfr	GDB_STATE(RT_DELETE);
84483282Speter	free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER);
84559603Sdfr	link_elf_delete_gdb(&ef->gdb);
84659603Sdfr	GDB_STATE(RT_CONSISTENT);
84759603Sdfr    }
84859603Sdfr#endif
84959603Sdfr
85059603Sdfr    if (ef->preloaded) {
85159751Speter	link_elf_unload_preload(file);
85259603Sdfr	return;
85359603Sdfr    }
85439071Sdfr#ifdef SPARSE_MAPPING
85559603Sdfr    if (ef->object) {
85659603Sdfr	vm_map_remove(kernel_map, (vm_offset_t) ef->address,
85759603Sdfr		      (vm_offset_t) ef->address
85859603Sdfr		      + (ef->object->size << PAGE_SHIFT));
85959603Sdfr	vm_object_deallocate(ef->object);
86059603Sdfr    }
86139071Sdfr#else
86259603Sdfr    if (ef->address)
86359603Sdfr	free(ef->address, M_LINKER);
86439071Sdfr#endif
86559603Sdfr    if (ef->symbase)
86659603Sdfr	free(ef->symbase, M_LINKER);
86759603Sdfr    if (ef->strbase)
86859603Sdfr	free(ef->strbase, M_LINKER);
86938514Sdfr}
87038514Sdfr
87140156Speterstatic void
87259751Speterlink_elf_unload_preload(linker_file_t file)
87340156Speter{
87440156Speter    if (file->filename)
87540156Speter	preload_delete_name(file->filename);
87640156Speter}
87740156Speter
87839071Sdfrstatic const char *
87940435Spetersymbol_name(elf_file_t ef, Elf_Word r_info)
88038514Sdfr{
88139071Sdfr    const Elf_Sym *ref;
88238514Sdfr
88340435Speter    if (ELF_R_SYM(r_info)) {
88440435Speter	ref = ef->symtab + ELF_R_SYM(r_info);
88540397Speter	return ef->strtab + ref->st_name;
88639071Sdfr    } else
88739071Sdfr	return NULL;
88838514Sdfr}
88938514Sdfr
89038514Sdfrstatic int
89159603Sdfrrelocate_file(elf_file_t ef)
89238514Sdfr{
89339071Sdfr    const Elf_Rel *rellim;
89439071Sdfr    const Elf_Rel *rel;
89539071Sdfr    const Elf_Rela *relalim;
89639071Sdfr    const Elf_Rela *rela;
89740435Speter    const char *symname;
89838514Sdfr
89939071Sdfr    /* Perform relocations without addend if there are any: */
90040435Speter    rel = ef->rel;
90140435Speter    if (rel) {
90243388Sbde	rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize);
90340435Speter	while (rel < rellim) {
90440435Speter	    symname = symbol_name(ef, rel->r_info);
90559603Sdfr	    if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) {
90659744Speter		printf("link_elf: symbol %s undefined\n", symname);
90740435Speter		return ENOENT;
90842200Speter	    }
90940435Speter	    rel++;
91040435Speter	}
91139071Sdfr    }
91238514Sdfr
91339071Sdfr    /* Perform relocations with addend if there are any: */
91440435Speter    rela = ef->rela;
91540435Speter    if (rela) {
91643388Sbde	relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize);
91740435Speter	while (rela < relalim) {
91840435Speter	    symname = symbol_name(ef, rela->r_info);
91959603Sdfr	    if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) {
92059744Speter		printf("link_elf: symbol %s undefined\n", symname);
92140435Speter		return ENOENT;
92242200Speter	    }
92340435Speter	    rela++;
92440435Speter	}
92539071Sdfr    }
92638514Sdfr
92739071Sdfr    /* Perform PLT relocations without addend if there are any: */
92840435Speter    rel = ef->pltrel;
92940435Speter    if (rel) {
93043388Sbde	rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize);
93140435Speter	while (rel < rellim) {
93240435Speter	    symname = symbol_name(ef, rel->r_info);
93359603Sdfr	    if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) {
93459744Speter		printf("link_elf: symbol %s undefined\n", symname);
93540435Speter		return ENOENT;
93642200Speter	    }
93740435Speter	    rel++;
93840435Speter	}
93939071Sdfr    }
94038514Sdfr
94139071Sdfr    /* Perform relocations with addend if there are any: */
94240435Speter    rela = ef->pltrela;
94340435Speter    if (rela) {
94443388Sbde	relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize);
94540435Speter	while (rela < relalim) {
94640435Speter	    symname = symbol_name(ef, rela->r_info);
94759603Sdfr	    if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) {
94859744Speter		printf("link_elf: symbol %s undefined\n", symname);
94940435Speter		return ENOENT;
95042200Speter	    }
95140435Speter	    rela++;
95240435Speter	}
95338514Sdfr    }
95438514Sdfr
95538514Sdfr    return 0;
95638514Sdfr}
95738514Sdfr
95839071Sdfr/*
95939071Sdfr * Hash function for symbol table lookup.  Don't even think about changing
96039071Sdfr * this.  It is specified by the System V ABI.
96139071Sdfr */
96239071Sdfrstatic unsigned long
96339071Sdfrelf_hash(const char *name)
96438514Sdfr{
96539071Sdfr    const unsigned char *p = (const unsigned char *) name;
96639071Sdfr    unsigned long h = 0;
96739071Sdfr    unsigned long g;
96838514Sdfr
96939071Sdfr    while (*p != '\0') {
97039071Sdfr	h = (h << 4) + *p++;
97139071Sdfr	if ((g = h & 0xf0000000) != 0)
97239071Sdfr	    h ^= g >> 24;
97339071Sdfr	h &= ~g;
97439071Sdfr    }
97539071Sdfr    return h;
97638514Sdfr}
97738514Sdfr
97838514Sdfrint
97943301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
98038514Sdfr{
98159603Sdfr    elf_file_t ef = (elf_file_t) lf;
98239071Sdfr    unsigned long symnum;
98340254Speter    const Elf_Sym* symp;
98440254Speter    const char *strp;
98539071Sdfr    unsigned long hash;
98639071Sdfr    int i;
98738514Sdfr
98840254Speter    /* First, search hashed global symbols */
98939071Sdfr    hash = elf_hash(name);
99039071Sdfr    symnum = ef->buckets[hash % ef->nbuckets];
99139071Sdfr
99239071Sdfr    while (symnum != STN_UNDEF) {
99339071Sdfr	if (symnum >= ef->nchains) {
99439071Sdfr	    printf("link_elf_lookup_symbol: corrupt symbol table\n");
99539071Sdfr	    return ENOENT;
99638514Sdfr	}
99738514Sdfr
99839071Sdfr	symp = ef->symtab + symnum;
99939071Sdfr	if (symp->st_name == 0) {
100039071Sdfr	    printf("link_elf_lookup_symbol: corrupt symbol table\n");
100139071Sdfr	    return ENOENT;
100239071Sdfr	}
100339071Sdfr
100439071Sdfr	strp = ef->strtab + symp->st_name;
100539071Sdfr
100639071Sdfr	if (strcmp(name, strp) == 0) {
100739071Sdfr	    if (symp->st_shndx != SHN_UNDEF ||
100839071Sdfr		(symp->st_value != 0 &&
100939071Sdfr		 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
101043301Sdillon		*sym = (c_linker_sym_t) symp;
101139071Sdfr		return 0;
101239071Sdfr	    } else
101339071Sdfr		return ENOENT;
101439071Sdfr	}
101539071Sdfr
101639071Sdfr	symnum = ef->chains[symnum];
101739071Sdfr    }
101839071Sdfr
101940254Speter    /* If we have not found it, look at the full table (if loaded) */
102040254Speter    if (ef->symtab == ef->ddbsymtab)
102140254Speter	return ENOENT;
102240254Speter
102340254Speter    /* Exhaustive search */
102440254Speter    for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
102540254Speter	strp = ef->ddbstrtab + symp->st_name;
102640254Speter	if (strcmp(name, strp) == 0) {
102740254Speter	    if (symp->st_shndx != SHN_UNDEF ||
102840254Speter		(symp->st_value != 0 &&
102940254Speter		 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
103043301Sdillon		*sym = (c_linker_sym_t) symp;
103140254Speter		return 0;
103240254Speter	    } else
103340254Speter		return ENOENT;
103440254Speter	}
103540254Speter    }
103640254Speter
103739071Sdfr    return ENOENT;
103838514Sdfr}
103938514Sdfr
104040156Speterstatic int
104143309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval)
104238514Sdfr{
104359603Sdfr	elf_file_t ef = (elf_file_t) lf;
104443311Sdillon	const Elf_Sym* es = (const Elf_Sym*) sym;
104538514Sdfr
104640254Speter	if (es >= ef->symtab && ((es - ef->symtab) < ef->nchains)) {
104740254Speter	    symval->name = ef->strtab + es->st_name;
104840254Speter	    symval->value = (caddr_t) ef->address + es->st_value;
104940254Speter	    symval->size = es->st_size;
105040254Speter	    return 0;
105140254Speter	}
105240254Speter	if (ef->symtab == ef->ddbsymtab)
105340254Speter	    return ENOENT;
105440254Speter	if (es >= ef->ddbsymtab && ((es - ef->ddbsymtab) < ef->ddbsymcnt)) {
105540254Speter	    symval->name = ef->ddbstrtab + es->st_name;
105640254Speter	    symval->value = (caddr_t) ef->address + es->st_value;
105740254Speter	    symval->size = es->st_size;
105840254Speter	    return 0;
105940254Speter	}
106040254Speter	return ENOENT;
106138514Sdfr}
106238514Sdfr
106338514Sdfrstatic int
106438514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value,
106543301Sdillon		       c_linker_sym_t* sym, long* diffp)
106638514Sdfr{
106759603Sdfr	elf_file_t ef = (elf_file_t) lf;
106855090Sbde	u_long off = (uintptr_t) (void *) value;
106938514Sdfr	u_long diff = off;
107055090Sbde	u_long st_value;
107139071Sdfr	const Elf_Sym* es;
107239071Sdfr	const Elf_Sym* best = 0;
107338514Sdfr	int i;
107438514Sdfr
107540254Speter	for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
107638514Sdfr		if (es->st_name == 0)
107738514Sdfr			continue;
107855090Sbde		st_value = es->st_value + (uintptr_t) (void *) ef->address;
107953820Speter		if (off >= st_value) {
108053820Speter			if (off - st_value < diff) {
108153820Speter				diff = off - st_value;
108238514Sdfr				best = es;
108338514Sdfr				if (diff == 0)
108438514Sdfr					break;
108553820Speter			} else if (off - st_value == diff) {
108638514Sdfr				best = es;
108738514Sdfr			}
108838514Sdfr		}
108938514Sdfr	}
109038514Sdfr	if (best == 0)
109138514Sdfr		*diffp = off;
109238514Sdfr	else
109338514Sdfr		*diffp = diff;
109443301Sdillon	*sym = (c_linker_sym_t) best;
109538514Sdfr
109638514Sdfr	return 0;
109738514Sdfr}
109878161Speter
109978161Speter/*
110078161Speter * Look up a linker set on an ELF system.
110178161Speter */
110278161Speterstatic int
110378161Speterlink_elf_lookup_set(linker_file_t lf, const char *name,
110478161Speter		    void ***startp, void ***stopp, int *countp)
110578161Speter{
110678161Speter	c_linker_sym_t sym;
110778161Speter	linker_symval_t symval;
110878161Speter	char *setsym;
110978161Speter	void **start, **stop;
111078161Speter	int len, error = 0, count;
111178161Speter
111278161Speter	len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
111378161Speter	setsym = malloc(len, M_LINKER, M_WAITOK);
111478161Speter	if (setsym == NULL)
111578161Speter		return ENOMEM;
111678161Speter
111778161Speter	/* get address of first entry */
111878161Speter	snprintf(setsym, len, "%s%s", "__start_set_", name);
111978161Speter	error = link_elf_lookup_symbol(lf, setsym, &sym);
112078161Speter	if (error)
112178161Speter		goto out;
112278161Speter	link_elf_symbol_values(lf, sym, &symval);
112378161Speter	if (symval.value == 0) {
112478161Speter		error = ESRCH;
112578161Speter		goto out;
112678161Speter	}
112778161Speter	start = (void **)symval.value;
112878161Speter
112978161Speter	/* get address of last entry */
113078161Speter	snprintf(setsym, len, "%s%s", "__stop_set_", name);
113178161Speter	error = link_elf_lookup_symbol(lf, setsym, &sym);
113278161Speter	if (error)
113378161Speter		goto out;
113478161Speter	link_elf_symbol_values(lf, sym, &symval);
113578161Speter	if (symval.value == 0) {
113678161Speter		error = ESRCH;
113778161Speter		goto out;
113878161Speter	}
113978161Speter	stop = (void **)symval.value;
114078161Speter
114178161Speter	/* and the number of entries */
114278161Speter	count = stop - start;
114378161Speter
114478161Speter	/* and copy out */
114578161Speter	if (startp)
114678161Speter		*startp = start;
114778161Speter	if (stopp)
114878161Speter		*stopp = stop;
114978161Speter	if (countp)
115078161Speter		*countp = count;
115178161Speter
115278161Speterout:
115378161Speter	free(setsym, M_LINKER);
115478161Speter	return error;
115578161Speter}
115685736Sgreen
115785736Sgreenstatic int
115885736Sgreenlink_elf_each_function_name(linker_file_t file,
115985736Sgreen  int (*callback)(const char *, void *), void *opaque) {
116085736Sgreen    elf_file_t ef = (elf_file_t)file;
116185736Sgreen    const Elf_Sym* symp;
116285736Sgreen    int i, error;
116385736Sgreen
116485736Sgreen    /* Exhaustive search */
116585736Sgreen    for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
116685736Sgreen	if (symp->st_value != 0 &&
116785736Sgreen	    ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
116885736Sgreen		error = callback(ef->ddbstrtab + symp->st_name, opaque);
116985736Sgreen		if (error)
117085736Sgreen		    return (error);
117185736Sgreen	}
117285736Sgreen    }
117385736Sgreen    return (0);
117485736Sgreen}
1175