xref: /dragonfly/sys/kern/link_elf_obj.c (revision b2b3ffcd)
128730bd3SSimon Schubert /*-
228730bd3SSimon Schubert  * Copyright (c) 1998 Doug Rabson
328730bd3SSimon Schubert  * Copyright (c) 2004 Peter Wemm
428730bd3SSimon Schubert  * All rights reserved.
528730bd3SSimon Schubert  *
628730bd3SSimon Schubert  * Redistribution and use in source and binary forms, with or without
728730bd3SSimon Schubert  * modification, are permitted provided that the following conditions
828730bd3SSimon Schubert  * are met:
928730bd3SSimon Schubert  * 1. Redistributions of source code must retain the above copyright
1028730bd3SSimon Schubert  *    notice, this list of conditions and the following disclaimer.
1128730bd3SSimon Schubert  * 2. Redistributions in binary form must reproduce the above copyright
1228730bd3SSimon Schubert  *    notice, this list of conditions and the following disclaimer in the
1328730bd3SSimon Schubert  *    documentation and/or other materials provided with the distribution.
1428730bd3SSimon Schubert  *
1528730bd3SSimon Schubert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1628730bd3SSimon Schubert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1728730bd3SSimon Schubert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1828730bd3SSimon Schubert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1928730bd3SSimon Schubert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2028730bd3SSimon Schubert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2128730bd3SSimon Schubert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2228730bd3SSimon Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2328730bd3SSimon Schubert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2428730bd3SSimon Schubert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2528730bd3SSimon Schubert  * SUCH DAMAGE.
2628730bd3SSimon Schubert  *
2728730bd3SSimon Schubert  * $FreeBSD: src/sys/kern/link_elf.c,v 1.24 1999/12/24 15:33:36 bde Exp $
2828730bd3SSimon Schubert  * $DragonFly: src/sys/kern/link_elf.c,v 1.29 2008/08/01 23:11:16 dillon Exp $
2928730bd3SSimon Schubert  */
3028730bd3SSimon Schubert 
3128730bd3SSimon Schubert #include <sys/param.h>
3228730bd3SSimon Schubert #include <sys/kernel.h>
3328730bd3SSimon Schubert #include <sys/systm.h>
3428730bd3SSimon Schubert #include <sys/malloc.h>
3528730bd3SSimon Schubert #include <sys/proc.h>
3628730bd3SSimon Schubert #include <sys/nlookup.h>
3728730bd3SSimon Schubert #include <sys/fcntl.h>
3828730bd3SSimon Schubert #include <sys/vnode.h>
3928730bd3SSimon Schubert #include <sys/linker.h>
4028730bd3SSimon Schubert #include <machine/elf.h>
4128730bd3SSimon Schubert 
4228730bd3SSimon Schubert #include <vm/vm.h>
4328730bd3SSimon Schubert #include <vm/vm_param.h>
4428730bd3SSimon Schubert #include <vm/vm_zone.h>
4528730bd3SSimon Schubert #include <vm/vm_object.h>
4628730bd3SSimon Schubert #include <vm/vm_kern.h>
4728730bd3SSimon Schubert #include <vm/vm_extern.h>
4828730bd3SSimon Schubert #include <sys/lock.h>
4928730bd3SSimon Schubert #include <vm/pmap.h>
5028730bd3SSimon Schubert #include <vm/vm_map.h>
5128730bd3SSimon Schubert 
5228730bd3SSimon Schubert static int	link_elf_obj_preload_file(const char *, linker_file_t *);
5328730bd3SSimon Schubert static int	link_elf_obj_preload_finish(linker_file_t);
5428730bd3SSimon Schubert static int	link_elf_obj_load_file(const char *, linker_file_t *);
5528730bd3SSimon Schubert static int
5628730bd3SSimon Schubert link_elf_obj_lookup_symbol(linker_file_t, const char *,
5728730bd3SSimon Schubert 		       c_linker_sym_t *);
5828730bd3SSimon Schubert static int	link_elf_obj_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t *);
5928730bd3SSimon Schubert static int
6028730bd3SSimon Schubert link_elf_obj_search_symbol(linker_file_t, caddr_t value,
6128730bd3SSimon Schubert 		       c_linker_sym_t * sym, long *diffp);
6228730bd3SSimon Schubert 
6328730bd3SSimon Schubert static void	link_elf_obj_unload_file(linker_file_t);
6428730bd3SSimon Schubert static int
6528730bd3SSimon Schubert link_elf_obj_lookup_set(linker_file_t, const char *,
6628730bd3SSimon Schubert 		    void ***, void ***, int *);
6728730bd3SSimon Schubert static void	link_elf_obj_reloc_local(linker_file_t lf);
6828730bd3SSimon Schubert static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *);
6928730bd3SSimon Schubert 
7028730bd3SSimon Schubert static struct linker_class_ops link_elf_obj_class_ops = {
7128730bd3SSimon Schubert 	link_elf_obj_load_file,
7228730bd3SSimon Schubert 	link_elf_obj_preload_file,
7328730bd3SSimon Schubert };
7428730bd3SSimon Schubert 
7528730bd3SSimon Schubert static struct linker_file_ops link_elf_obj_file_ops = {
7628730bd3SSimon Schubert 	.lookup_symbol = link_elf_obj_lookup_symbol,
7728730bd3SSimon Schubert 	.symbol_values = link_elf_obj_symbol_values,
7828730bd3SSimon Schubert 	.search_symbol = link_elf_obj_search_symbol,
7928730bd3SSimon Schubert 	.preload_finish = link_elf_obj_preload_finish,
8028730bd3SSimon Schubert 	.unload = link_elf_obj_unload_file,
8128730bd3SSimon Schubert 	.lookup_set = link_elf_obj_lookup_set,
8228730bd3SSimon Schubert };
8328730bd3SSimon Schubert 
8428730bd3SSimon Schubert typedef struct {
8528730bd3SSimon Schubert 	void           *addr;
8628730bd3SSimon Schubert 	Elf_Off		size;
8728730bd3SSimon Schubert 	int		flags;
8828730bd3SSimon Schubert 	int		sec;		/* Original section */
8928730bd3SSimon Schubert 	char           *name;
9028730bd3SSimon Schubert }		Elf_progent;
9128730bd3SSimon Schubert 
9228730bd3SSimon Schubert typedef struct {
9328730bd3SSimon Schubert 	Elf_Rel        *rel;
9428730bd3SSimon Schubert 	int		nrel;
9528730bd3SSimon Schubert 	int		sec;
9628730bd3SSimon Schubert }		Elf_relent;
9728730bd3SSimon Schubert 
9828730bd3SSimon Schubert typedef struct {
9928730bd3SSimon Schubert 	Elf_Rela       *rela;
10028730bd3SSimon Schubert 	int		nrela;
10128730bd3SSimon Schubert 	int		sec;
10228730bd3SSimon Schubert }		Elf_relaent;
10328730bd3SSimon Schubert 
10428730bd3SSimon Schubert 
10528730bd3SSimon Schubert typedef struct elf_file {
10628730bd3SSimon Schubert 	int		preloaded;
10728730bd3SSimon Schubert 
10828730bd3SSimon Schubert 	caddr_t		address;	/* Relocation address */
10928730bd3SSimon Schubert 	vm_object_t	object;		/* VM object to hold file pages */
11028730bd3SSimon Schubert 	Elf_Shdr       *e_shdr;
11128730bd3SSimon Schubert 
11228730bd3SSimon Schubert 	Elf_progent    *progtab;
11328730bd3SSimon Schubert 	int		nprogtab;
11428730bd3SSimon Schubert 
11528730bd3SSimon Schubert 	Elf_relaent    *relatab;
11628730bd3SSimon Schubert 	int		nrelatab;
11728730bd3SSimon Schubert 
11828730bd3SSimon Schubert 	Elf_relent     *reltab;
11928730bd3SSimon Schubert 	int		nreltab;
12028730bd3SSimon Schubert 
12128730bd3SSimon Schubert 	Elf_Sym        *ddbsymtab;	/* The symbol table we are using */
12228730bd3SSimon Schubert 	long		ddbsymcnt;	/* Number of symbols */
12328730bd3SSimon Schubert 	caddr_t		ddbstrtab;	/* String table */
12428730bd3SSimon Schubert 	long		ddbstrcnt;	/* number of bytes in string table */
12528730bd3SSimon Schubert 
12628730bd3SSimon Schubert 	caddr_t		shstrtab;	/* Section name string table */
12728730bd3SSimon Schubert 	long		shstrcnt;	/* number of bytes in string table */
12828730bd3SSimon Schubert 
12928730bd3SSimon Schubert 	caddr_t		ctftab;		/* CTF table */
13028730bd3SSimon Schubert 	long		ctfcnt;		/* number of bytes in CTF table */
13128730bd3SSimon Schubert 	caddr_t		ctfoff;		/* CTF offset table */
13228730bd3SSimon Schubert 	caddr_t		typoff;		/* Type offset table */
13328730bd3SSimon Schubert 	long		typlen;		/* Number of type entries. */
13428730bd3SSimon Schubert 
13528730bd3SSimon Schubert }              *elf_file_t;
13628730bd3SSimon Schubert 
13728730bd3SSimon Schubert static int	relocate_file(linker_file_t lf);
13828730bd3SSimon Schubert 
13928730bd3SSimon Schubert /*
14028730bd3SSimon Schubert  * The kernel symbol table starts here.
14128730bd3SSimon Schubert  */
14228730bd3SSimon Schubert extern struct _dynamic _DYNAMIC;
14328730bd3SSimon Schubert 
14428730bd3SSimon Schubert static void
14528730bd3SSimon Schubert link_elf_obj_init(void *arg)
14628730bd3SSimon Schubert {
14728730bd3SSimon Schubert #if ELF_TARG_CLASS == ELFCLASS32
14828730bd3SSimon Schubert 	linker_add_class("elf32", NULL, &link_elf_obj_class_ops);
14928730bd3SSimon Schubert #else
15028730bd3SSimon Schubert 	linker_add_class("elf64", NULL, &link_elf_obj_class_ops);
15128730bd3SSimon Schubert #endif
15228730bd3SSimon Schubert }
15328730bd3SSimon Schubert 
15428730bd3SSimon Schubert SYSINIT(link_elf, SI_BOOT2_KLD, SI_ORDER_SECOND, link_elf_obj_init, 0);
15528730bd3SSimon Schubert 
15628730bd3SSimon Schubert static void
15728730bd3SSimon Schubert link_elf_obj_error(const char *file, const char *s)
15828730bd3SSimon Schubert {
15928730bd3SSimon Schubert 	kprintf("kldload: %s: %s\n", file, s);
16028730bd3SSimon Schubert }
16128730bd3SSimon Schubert 
16228730bd3SSimon Schubert static int
16328730bd3SSimon Schubert link_elf_obj_preload_file(const char *filename, linker_file_t *result)
16428730bd3SSimon Schubert {
16528730bd3SSimon Schubert 	Elf_Ehdr       *hdr;
16628730bd3SSimon Schubert 	Elf_Shdr       *shdr;
16728730bd3SSimon Schubert 	Elf_Sym	*es;
16828730bd3SSimon Schubert 	caddr_t		modptr, baseptr, sizeptr;
16928730bd3SSimon Schubert 	char           *type;
17028730bd3SSimon Schubert 	elf_file_t	ef;
17128730bd3SSimon Schubert 	linker_file_t	lf;
17228730bd3SSimon Schubert 	Elf_Addr	off;
17328730bd3SSimon Schubert 	int		error, i, j, pb, ra, rl, shstrindex, symstrindex, symtabindex;
17428730bd3SSimon Schubert 
17528730bd3SSimon Schubert 	/*
17628730bd3SSimon Schubert 	 * Look to see if we have the module preloaded.
17728730bd3SSimon Schubert 	 */
17828730bd3SSimon Schubert 	modptr = preload_search_by_name(filename);
17928730bd3SSimon Schubert 	if (modptr == NULL)
18028730bd3SSimon Schubert 		return ENOENT;
18128730bd3SSimon Schubert 
18228730bd3SSimon Schubert 	/* It's preloaded, check we can handle it and collect information */
18328730bd3SSimon Schubert 	type = (char *)preload_search_info(modptr, MODINFO_TYPE);
18428730bd3SSimon Schubert 	baseptr = preload_search_info(modptr, MODINFO_ADDR);
18528730bd3SSimon Schubert 	sizeptr = preload_search_info(modptr, MODINFO_SIZE);
18628730bd3SSimon Schubert 	hdr = (Elf_Ehdr *) preload_search_info(modptr, MODINFO_METADATA |
18728730bd3SSimon Schubert 					       MODINFOMD_ELFHDR);
18828730bd3SSimon Schubert 	shdr = (Elf_Shdr *) preload_search_info(modptr, MODINFO_METADATA |
18928730bd3SSimon Schubert 						MODINFOMD_SHDR);
19028730bd3SSimon Schubert 	if (type == NULL ||
19128730bd3SSimon Schubert 	    (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " obj module") != 0 &&
19228730bd3SSimon Schubert 	     strcmp(type, "elf obj module") != 0)) {
19328730bd3SSimon Schubert 		return (EFTYPE);
19428730bd3SSimon Schubert 	}
19528730bd3SSimon Schubert 	if (baseptr == NULL || sizeptr == NULL || hdr == NULL || shdr == NULL)
19628730bd3SSimon Schubert 		return (EINVAL);
19728730bd3SSimon Schubert 
19828730bd3SSimon Schubert 	ef = kmalloc(sizeof(struct elf_file), M_LINKER, M_WAITOK | M_ZERO);
19928730bd3SSimon Schubert 	ef->preloaded = 1;
20028730bd3SSimon Schubert 	ef->address = *(caddr_t *) baseptr;
20128730bd3SSimon Schubert 	lf = linker_make_file(filename, ef, &link_elf_obj_file_ops);
20228730bd3SSimon Schubert 	if (lf == NULL) {
20328730bd3SSimon Schubert 		kfree(ef, M_LINKER);
20428730bd3SSimon Schubert 		return ENOMEM;
20528730bd3SSimon Schubert 	}
20628730bd3SSimon Schubert 	lf->address = ef->address;
20728730bd3SSimon Schubert 	lf->size = *(size_t *) sizeptr;
20828730bd3SSimon Schubert 
20928730bd3SSimon Schubert 	if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
21028730bd3SSimon Schubert 	    hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
21128730bd3SSimon Schubert 	    hdr->e_ident[EI_VERSION] != EV_CURRENT ||
21228730bd3SSimon Schubert 	    hdr->e_version != EV_CURRENT ||
21328730bd3SSimon Schubert 	    hdr->e_type != ET_REL ||
21428730bd3SSimon Schubert 	    hdr->e_machine != ELF_TARG_MACH) {
21528730bd3SSimon Schubert 		error = EFTYPE;
21628730bd3SSimon Schubert 		goto out;
21728730bd3SSimon Schubert 	}
21828730bd3SSimon Schubert 	ef->e_shdr = shdr;
21928730bd3SSimon Schubert 
22028730bd3SSimon Schubert 	/* Scan the section header for information and table sizing. */
22128730bd3SSimon Schubert 	symtabindex = -1;
22228730bd3SSimon Schubert 	symstrindex = -1;
22328730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
22428730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
22528730bd3SSimon Schubert 		case SHT_PROGBITS:
22628730bd3SSimon Schubert 		case SHT_NOBITS:
22728730bd3SSimon Schubert 			ef->nprogtab++;
22828730bd3SSimon Schubert 			break;
22928730bd3SSimon Schubert 		case SHT_SYMTAB:
23028730bd3SSimon Schubert 			symtabindex = i;
23128730bd3SSimon Schubert 			symstrindex = shdr[i].sh_link;
23228730bd3SSimon Schubert 			break;
23328730bd3SSimon Schubert 		case SHT_REL:
23428730bd3SSimon Schubert 			ef->nreltab++;
23528730bd3SSimon Schubert 			break;
23628730bd3SSimon Schubert 		case SHT_RELA:
23728730bd3SSimon Schubert 			ef->nrelatab++;
23828730bd3SSimon Schubert 			break;
23928730bd3SSimon Schubert 		}
24028730bd3SSimon Schubert 	}
24128730bd3SSimon Schubert 
24228730bd3SSimon Schubert 	shstrindex = hdr->e_shstrndx;
24328730bd3SSimon Schubert 	if (ef->nprogtab == 0 || symstrindex < 0 ||
24428730bd3SSimon Schubert 	    symstrindex >= hdr->e_shnum ||
24528730bd3SSimon Schubert 	    shdr[symstrindex].sh_type != SHT_STRTAB || shstrindex == 0 ||
24628730bd3SSimon Schubert 	    shstrindex >= hdr->e_shnum ||
24728730bd3SSimon Schubert 	    shdr[shstrindex].sh_type != SHT_STRTAB) {
24828730bd3SSimon Schubert 		error = ENOEXEC;
24928730bd3SSimon Schubert 		goto out;
25028730bd3SSimon Schubert 	}
25128730bd3SSimon Schubert 	/* Allocate space for tracking the load chunks */
25228730bd3SSimon Schubert 	if (ef->nprogtab != 0)
25328730bd3SSimon Schubert 		ef->progtab = kmalloc(ef->nprogtab * sizeof(*ef->progtab),
25428730bd3SSimon Schubert 				     M_LINKER, M_WAITOK | M_ZERO);
25528730bd3SSimon Schubert 	if (ef->nreltab != 0)
25628730bd3SSimon Schubert 		ef->reltab = kmalloc(ef->nreltab * sizeof(*ef->reltab),
25728730bd3SSimon Schubert 				    M_LINKER, M_WAITOK | M_ZERO);
25828730bd3SSimon Schubert 	if (ef->nrelatab != 0)
25928730bd3SSimon Schubert 		ef->relatab = kmalloc(ef->nrelatab * sizeof(*ef->relatab),
26028730bd3SSimon Schubert 				     M_LINKER, M_WAITOK | M_ZERO);
26128730bd3SSimon Schubert 	if ((ef->nprogtab != 0 && ef->progtab == NULL) ||
26228730bd3SSimon Schubert 	    (ef->nreltab != 0 && ef->reltab == NULL) ||
26328730bd3SSimon Schubert 	    (ef->nrelatab != 0 && ef->relatab == NULL)) {
26428730bd3SSimon Schubert 		error = ENOMEM;
26528730bd3SSimon Schubert 		goto out;
26628730bd3SSimon Schubert 	}
26728730bd3SSimon Schubert 	/* XXX, relocate the sh_addr fields saved by the loader. */
26828730bd3SSimon Schubert 	off = 0;
26928730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
27028730bd3SSimon Schubert 		if (shdr[i].sh_addr != 0 && (off == 0 || shdr[i].sh_addr < off))
27128730bd3SSimon Schubert 			off = shdr[i].sh_addr;
27228730bd3SSimon Schubert 	}
27328730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
27428730bd3SSimon Schubert 		if (shdr[i].sh_addr != 0)
27528730bd3SSimon Schubert 			shdr[i].sh_addr = shdr[i].sh_addr - off +
27628730bd3SSimon Schubert 				(Elf_Addr) ef->address;
27728730bd3SSimon Schubert 	}
27828730bd3SSimon Schubert 
27928730bd3SSimon Schubert 	ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
28028730bd3SSimon Schubert 	ef->ddbsymtab = (Elf_Sym *) shdr[symtabindex].sh_addr;
28128730bd3SSimon Schubert 	ef->ddbstrcnt = shdr[symstrindex].sh_size;
28228730bd3SSimon Schubert 	ef->ddbstrtab = (char *)shdr[symstrindex].sh_addr;
28328730bd3SSimon Schubert 	ef->shstrcnt = shdr[shstrindex].sh_size;
28428730bd3SSimon Schubert 	ef->shstrtab = (char *)shdr[shstrindex].sh_addr;
28528730bd3SSimon Schubert 
28628730bd3SSimon Schubert 	/* Now fill out progtab and the relocation tables. */
28728730bd3SSimon Schubert 	pb = 0;
28828730bd3SSimon Schubert 	rl = 0;
28928730bd3SSimon Schubert 	ra = 0;
29028730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
29128730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
29228730bd3SSimon Schubert 		case SHT_PROGBITS:
29328730bd3SSimon Schubert 		case SHT_NOBITS:
29428730bd3SSimon Schubert 			ef->progtab[pb].addr = (void *)shdr[i].sh_addr;
29528730bd3SSimon Schubert 			if (shdr[i].sh_type == SHT_PROGBITS)
29628730bd3SSimon Schubert 				ef->progtab[pb].name = "<<PROGBITS>>";
29728730bd3SSimon Schubert 			else
29828730bd3SSimon Schubert 				ef->progtab[pb].name = "<<NOBITS>>";
29928730bd3SSimon Schubert 			ef->progtab[pb].size = shdr[i].sh_size;
30028730bd3SSimon Schubert 			ef->progtab[pb].sec = i;
30128730bd3SSimon Schubert 			if (ef->shstrtab && shdr[i].sh_name != 0)
30228730bd3SSimon Schubert 				ef->progtab[pb].name =
30328730bd3SSimon Schubert 					ef->shstrtab + shdr[i].sh_name;
30428730bd3SSimon Schubert #if 0
30528730bd3SSimon Schubert 			if (ef->progtab[pb].name != NULL &&
30628730bd3SSimon Schubert 			    !strcmp(ef->progtab[pb].name, "set_pcpu")) {
30728730bd3SSimon Schubert 				void           *dpcpu;
30828730bd3SSimon Schubert 
30928730bd3SSimon Schubert 				dpcpu = dpcpu_alloc(shdr[i].sh_size);
31028730bd3SSimon Schubert 				if (dpcpu == NULL) {
31128730bd3SSimon Schubert 					error = ENOSPC;
31228730bd3SSimon Schubert 					goto out;
31328730bd3SSimon Schubert 				}
31428730bd3SSimon Schubert 				memcpy(dpcpu, ef->progtab[pb].addr,
31528730bd3SSimon Schubert 				       ef->progtab[pb].size);
31628730bd3SSimon Schubert 				dpcpu_copy(dpcpu, shdr[i].sh_size);
31728730bd3SSimon Schubert 				ef->progtab[pb].addr = dpcpu;
31828730bd3SSimon Schubert #ifdef VIMAGE
31928730bd3SSimon Schubert 			} else if (ef->progtab[pb].name != NULL &&
32028730bd3SSimon Schubert 				   !strcmp(ef->progtab[pb].name, VNET_SETNAME)) {
32128730bd3SSimon Schubert 				void           *vnet_data;
32228730bd3SSimon Schubert 
32328730bd3SSimon Schubert 				vnet_data = vnet_data_alloc(shdr[i].sh_size);
32428730bd3SSimon Schubert 				if (vnet_data == NULL) {
32528730bd3SSimon Schubert 					error = ENOSPC;
32628730bd3SSimon Schubert 					goto out;
32728730bd3SSimon Schubert 				}
32828730bd3SSimon Schubert 				memcpy(vnet_data, ef->progtab[pb].addr,
32928730bd3SSimon Schubert 				       ef->progtab[pb].size);
33028730bd3SSimon Schubert 				vnet_data_copy(vnet_data, shdr[i].sh_size);
33128730bd3SSimon Schubert 				ef->progtab[pb].addr = vnet_data;
33228730bd3SSimon Schubert #endif
33328730bd3SSimon Schubert 			}
33428730bd3SSimon Schubert #endif
33528730bd3SSimon Schubert 			/* Update all symbol values with the offset. */
33628730bd3SSimon Schubert 			for (j = 0; j < ef->ddbsymcnt; j++) {
33728730bd3SSimon Schubert 				es = &ef->ddbsymtab[j];
33828730bd3SSimon Schubert 				if (es->st_shndx != i)
33928730bd3SSimon Schubert 					continue;
34028730bd3SSimon Schubert 				es->st_value += (Elf_Addr) ef->progtab[pb].addr;
34128730bd3SSimon Schubert 			}
34228730bd3SSimon Schubert 			pb++;
34328730bd3SSimon Schubert 			break;
34428730bd3SSimon Schubert 		case SHT_REL:
34528730bd3SSimon Schubert 			ef->reltab[rl].rel = (Elf_Rel *) shdr[i].sh_addr;
34628730bd3SSimon Schubert 			ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
34728730bd3SSimon Schubert 			ef->reltab[rl].sec = shdr[i].sh_info;
34828730bd3SSimon Schubert 			rl++;
34928730bd3SSimon Schubert 			break;
35028730bd3SSimon Schubert 		case SHT_RELA:
35128730bd3SSimon Schubert 			ef->relatab[ra].rela = (Elf_Rela *) shdr[i].sh_addr;
35228730bd3SSimon Schubert 			ef->relatab[ra].nrela =
35328730bd3SSimon Schubert 				shdr[i].sh_size / sizeof(Elf_Rela);
35428730bd3SSimon Schubert 			ef->relatab[ra].sec = shdr[i].sh_info;
35528730bd3SSimon Schubert 			ra++;
35628730bd3SSimon Schubert 			break;
35728730bd3SSimon Schubert 		}
35828730bd3SSimon Schubert 	}
35928730bd3SSimon Schubert 	if (pb != ef->nprogtab)
36028730bd3SSimon Schubert 		panic("lost progbits");
36128730bd3SSimon Schubert 	if (rl != ef->nreltab)
36228730bd3SSimon Schubert 		panic("lost reltab");
36328730bd3SSimon Schubert 	if (ra != ef->nrelatab)
36428730bd3SSimon Schubert 		panic("lost relatab");
36528730bd3SSimon Schubert 
36628730bd3SSimon Schubert 	/* Local intra-module relocations */
36728730bd3SSimon Schubert 	link_elf_obj_reloc_local(lf);
36828730bd3SSimon Schubert 
36928730bd3SSimon Schubert 	*result = lf;
37028730bd3SSimon Schubert 	return (0);
37128730bd3SSimon Schubert 
37228730bd3SSimon Schubert out:
37328730bd3SSimon Schubert 	/* preload not done this way */
37428730bd3SSimon Schubert 	linker_file_unload(lf /* , LINKER_UNLOAD_FORCE */ );
37528730bd3SSimon Schubert 	return (error);
37628730bd3SSimon Schubert }
37728730bd3SSimon Schubert 
37828730bd3SSimon Schubert static int
37928730bd3SSimon Schubert link_elf_obj_preload_finish(linker_file_t lf)
38028730bd3SSimon Schubert {
38128730bd3SSimon Schubert 	int error;
38228730bd3SSimon Schubert 
38328730bd3SSimon Schubert 	error = relocate_file(lf);
38428730bd3SSimon Schubert 
38528730bd3SSimon Schubert 	return (error);
38628730bd3SSimon Schubert }
38728730bd3SSimon Schubert 
38828730bd3SSimon Schubert static int
38928730bd3SSimon Schubert link_elf_obj_load_file(const char *filename, linker_file_t * result)
39028730bd3SSimon Schubert {
39128730bd3SSimon Schubert 	struct nlookupdata nd;
39228730bd3SSimon Schubert 	struct thread  *td = curthread;	/* XXX */
39328730bd3SSimon Schubert 	struct proc    *p = td->td_proc;
39428730bd3SSimon Schubert 	char           *pathname;
39528730bd3SSimon Schubert 	struct vnode   *vp;
39628730bd3SSimon Schubert 	Elf_Ehdr       *hdr;
39728730bd3SSimon Schubert 	Elf_Shdr       *shdr;
39828730bd3SSimon Schubert 	Elf_Sym        *es;
39928730bd3SSimon Schubert 	int		nbytes, i, j;
40028730bd3SSimon Schubert 	vm_offset_t	mapbase;
40128730bd3SSimon Schubert 	size_t		mapsize;
40228730bd3SSimon Schubert 	int		error = 0;
40328730bd3SSimon Schubert 	int		resid;
40428730bd3SSimon Schubert 	elf_file_t	ef;
40528730bd3SSimon Schubert 	linker_file_t	lf;
40628730bd3SSimon Schubert 	int		symtabindex;
40728730bd3SSimon Schubert 	int		symstrindex;
40828730bd3SSimon Schubert 	int		shstrindex;
40928730bd3SSimon Schubert 	int		nsym;
41028730bd3SSimon Schubert 	int		pb, rl, ra;
41128730bd3SSimon Schubert 	int		alignmask;
41228730bd3SSimon Schubert 
41328730bd3SSimon Schubert 	KKASSERT(p != NULL);
41428730bd3SSimon Schubert 	if (p->p_ucred == NULL) {
41528730bd3SSimon Schubert 		kprintf("link_elf_obj_load_file: cannot load '%s' from filesystem"
41628730bd3SSimon Schubert 			" this early\n", filename);
41728730bd3SSimon Schubert 		return ENOENT;
41828730bd3SSimon Schubert 	}
41928730bd3SSimon Schubert 	shdr = NULL;
42028730bd3SSimon Schubert 	lf = NULL;
42128730bd3SSimon Schubert 	mapsize = 0;
42228730bd3SSimon Schubert 	hdr = NULL;
42328730bd3SSimon Schubert 	pathname = linker_search_path(filename);
42428730bd3SSimon Schubert 	if (pathname == NULL)
42528730bd3SSimon Schubert 		return ENOENT;
42628730bd3SSimon Schubert 
42728730bd3SSimon Schubert 	error = nlookup_init(&nd, pathname, UIO_SYSSPACE, NLC_FOLLOW | NLC_LOCKVP);
42828730bd3SSimon Schubert 	if (error == 0)
42928730bd3SSimon Schubert 		error = vn_open(&nd, NULL, FREAD, 0);
43028730bd3SSimon Schubert 	kfree(pathname, M_LINKER);
43128730bd3SSimon Schubert 	if (error) {
43228730bd3SSimon Schubert 		nlookup_done(&nd);
43328730bd3SSimon Schubert 		return error;
43428730bd3SSimon Schubert 	}
43528730bd3SSimon Schubert 	vp = nd.nl_open_vp;
43628730bd3SSimon Schubert 	nd.nl_open_vp = NULL;
43728730bd3SSimon Schubert 	nlookup_done(&nd);
43828730bd3SSimon Schubert 
43928730bd3SSimon Schubert 	/*
44028730bd3SSimon Schubert 	 * Read the elf header from the file.
44128730bd3SSimon Schubert 	 */
44228730bd3SSimon Schubert 	hdr = kmalloc(sizeof(*hdr), M_LINKER, M_WAITOK);
44328730bd3SSimon Schubert 	if (hdr == NULL) {
44428730bd3SSimon Schubert 		error = ENOMEM;
44528730bd3SSimon Schubert 		goto out;
44628730bd3SSimon Schubert 	}
44728730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, (void *)hdr, sizeof(*hdr), 0,
44828730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
44928730bd3SSimon Schubert 	if (error)
45028730bd3SSimon Schubert 		goto out;
45128730bd3SSimon Schubert 	if (resid != 0) {
45228730bd3SSimon Schubert 		error = ENOEXEC;
45328730bd3SSimon Schubert 		goto out;
45428730bd3SSimon Schubert 	}
45528730bd3SSimon Schubert 	if (!IS_ELF(*hdr)) {
45628730bd3SSimon Schubert 		error = ENOEXEC;
45728730bd3SSimon Schubert 		goto out;
45828730bd3SSimon Schubert 	}
45928730bd3SSimon Schubert 
46028730bd3SSimon Schubert 	if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
46128730bd3SSimon Schubert 	    || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
46228730bd3SSimon Schubert 		link_elf_obj_error(filename, "Unsupported file layout");
46328730bd3SSimon Schubert 		error = ENOEXEC;
46428730bd3SSimon Schubert 		goto out;
46528730bd3SSimon Schubert 	}
46628730bd3SSimon Schubert 	if (hdr->e_ident[EI_VERSION] != EV_CURRENT
46728730bd3SSimon Schubert 	    || hdr->e_version != EV_CURRENT) {
46828730bd3SSimon Schubert 		link_elf_obj_error(filename, "Unsupported file version");
46928730bd3SSimon Schubert 		error = ENOEXEC;
47028730bd3SSimon Schubert 		goto out;
47128730bd3SSimon Schubert 	}
47228730bd3SSimon Schubert 	if (hdr->e_type != ET_REL) {
47328730bd3SSimon Schubert 		error = ENOSYS;
47428730bd3SSimon Schubert 		goto out;
47528730bd3SSimon Schubert 	}
47628730bd3SSimon Schubert 	if (hdr->e_machine != ELF_TARG_MACH) {
47728730bd3SSimon Schubert 		link_elf_obj_error(filename, "Unsupported machine");
47828730bd3SSimon Schubert 		error = ENOEXEC;
47928730bd3SSimon Schubert 		goto out;
48028730bd3SSimon Schubert 	}
48128730bd3SSimon Schubert 
48228730bd3SSimon Schubert 	ef = kmalloc(sizeof(struct elf_file), M_LINKER, M_WAITOK | M_ZERO);
48328730bd3SSimon Schubert 	lf = linker_make_file(filename, ef, &link_elf_obj_file_ops);
48428730bd3SSimon Schubert 	if (lf == NULL) {
48528730bd3SSimon Schubert 		kfree(ef, M_LINKER);
48628730bd3SSimon Schubert 		error = ENOMEM;
48728730bd3SSimon Schubert 		goto out;
48828730bd3SSimon Schubert 	}
48928730bd3SSimon Schubert 	ef->nprogtab = 0;
49028730bd3SSimon Schubert 	ef->e_shdr = 0;
49128730bd3SSimon Schubert 	ef->nreltab = 0;
49228730bd3SSimon Schubert 	ef->nrelatab = 0;
49328730bd3SSimon Schubert 
49428730bd3SSimon Schubert 	/* Allocate and read in the section header */
49528730bd3SSimon Schubert 	nbytes = hdr->e_shnum * hdr->e_shentsize;
49628730bd3SSimon Schubert 	if (nbytes == 0 || hdr->e_shoff == 0 ||
49728730bd3SSimon Schubert 	    hdr->e_shentsize != sizeof(Elf_Shdr)) {
49828730bd3SSimon Schubert 		error = ENOEXEC;
49928730bd3SSimon Schubert 		goto out;
50028730bd3SSimon Schubert 	}
50128730bd3SSimon Schubert 	shdr = kmalloc(nbytes, M_LINKER, M_WAITOK);
50228730bd3SSimon Schubert 	if (shdr == NULL) {
50328730bd3SSimon Schubert 		error = ENOMEM;
50428730bd3SSimon Schubert 		goto out;
50528730bd3SSimon Schubert 	}
50628730bd3SSimon Schubert 	ef->e_shdr = shdr;
50728730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, (caddr_t) shdr, nbytes, hdr->e_shoff,
50828730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
50928730bd3SSimon Schubert 	if (error)
51028730bd3SSimon Schubert 		goto out;
51128730bd3SSimon Schubert 	if (resid) {
51228730bd3SSimon Schubert 		error = ENOEXEC;
51328730bd3SSimon Schubert 		goto out;
51428730bd3SSimon Schubert 	}
51528730bd3SSimon Schubert 	/* Scan the section header for information and table sizing. */
51628730bd3SSimon Schubert 	nsym = 0;
51728730bd3SSimon Schubert 	symtabindex = -1;
51828730bd3SSimon Schubert 	symstrindex = -1;
51928730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
52028730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
52128730bd3SSimon Schubert 		case SHT_PROGBITS:
52228730bd3SSimon Schubert 		case SHT_NOBITS:
52328730bd3SSimon Schubert 			ef->nprogtab++;
52428730bd3SSimon Schubert 			break;
52528730bd3SSimon Schubert 		case SHT_SYMTAB:
52628730bd3SSimon Schubert 			nsym++;
52728730bd3SSimon Schubert 			symtabindex = i;
52828730bd3SSimon Schubert 			symstrindex = shdr[i].sh_link;
52928730bd3SSimon Schubert 			break;
53028730bd3SSimon Schubert 		case SHT_REL:
53128730bd3SSimon Schubert 			ef->nreltab++;
53228730bd3SSimon Schubert 			break;
53328730bd3SSimon Schubert 		case SHT_RELA:
53428730bd3SSimon Schubert 			ef->nrelatab++;
53528730bd3SSimon Schubert 			break;
53628730bd3SSimon Schubert 		case SHT_STRTAB:
53728730bd3SSimon Schubert 			break;
53828730bd3SSimon Schubert 		}
53928730bd3SSimon Schubert 	}
54028730bd3SSimon Schubert 	if (ef->nprogtab == 0) {
54128730bd3SSimon Schubert 		link_elf_obj_error(filename, "file has no contents");
54228730bd3SSimon Schubert 		error = ENOEXEC;
54328730bd3SSimon Schubert 		goto out;
54428730bd3SSimon Schubert 	}
54528730bd3SSimon Schubert 	if (nsym != 1) {
54628730bd3SSimon Schubert 		/* Only allow one symbol table for now */
54728730bd3SSimon Schubert 		link_elf_obj_error(filename, "file has no valid symbol table");
54828730bd3SSimon Schubert 		error = ENOEXEC;
54928730bd3SSimon Schubert 		goto out;
55028730bd3SSimon Schubert 	}
55128730bd3SSimon Schubert 	if (symstrindex < 0 || symstrindex > hdr->e_shnum ||
55228730bd3SSimon Schubert 	    shdr[symstrindex].sh_type != SHT_STRTAB) {
55328730bd3SSimon Schubert 		link_elf_obj_error(filename, "file has invalid symbol strings");
55428730bd3SSimon Schubert 		error = ENOEXEC;
55528730bd3SSimon Schubert 		goto out;
55628730bd3SSimon Schubert 	}
55728730bd3SSimon Schubert 	/* Allocate space for tracking the load chunks */
55828730bd3SSimon Schubert 	if (ef->nprogtab != 0)
55928730bd3SSimon Schubert 		ef->progtab = kmalloc(ef->nprogtab * sizeof(*ef->progtab),
56028730bd3SSimon Schubert 				      M_LINKER, M_WAITOK | M_ZERO);
56128730bd3SSimon Schubert 	if (ef->nreltab != 0)
56228730bd3SSimon Schubert 		ef->reltab = kmalloc(ef->nreltab * sizeof(*ef->reltab),
56328730bd3SSimon Schubert 				     M_LINKER, M_WAITOK | M_ZERO);
56428730bd3SSimon Schubert 	if (ef->nrelatab != 0)
56528730bd3SSimon Schubert 		ef->relatab = kmalloc(ef->nrelatab * sizeof(*ef->relatab),
56628730bd3SSimon Schubert 				      M_LINKER, M_WAITOK | M_ZERO);
56728730bd3SSimon Schubert 	if ((ef->nprogtab != 0 && ef->progtab == NULL) ||
56828730bd3SSimon Schubert 	    (ef->nreltab != 0 && ef->reltab == NULL) ||
56928730bd3SSimon Schubert 	    (ef->nrelatab != 0 && ef->relatab == NULL)) {
57028730bd3SSimon Schubert 		error = ENOMEM;
57128730bd3SSimon Schubert 		goto out;
57228730bd3SSimon Schubert 	}
57328730bd3SSimon Schubert 	if (symtabindex == -1)
57428730bd3SSimon Schubert 		panic("lost symbol table index");
57528730bd3SSimon Schubert 	/* Allocate space for and load the symbol table */
57628730bd3SSimon Schubert 	ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
57728730bd3SSimon Schubert 	ef->ddbsymtab = kmalloc(shdr[symtabindex].sh_size, M_LINKER, M_WAITOK);
57828730bd3SSimon Schubert 	if (ef->ddbsymtab == NULL) {
57928730bd3SSimon Schubert 		error = ENOMEM;
58028730bd3SSimon Schubert 		goto out;
58128730bd3SSimon Schubert 	}
58228730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, (void *)ef->ddbsymtab,
58328730bd3SSimon Schubert 			shdr[symtabindex].sh_size, shdr[symtabindex].sh_offset,
58428730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
58528730bd3SSimon Schubert 	if (error)
58628730bd3SSimon Schubert 		goto out;
58728730bd3SSimon Schubert 	if (resid != 0) {
58828730bd3SSimon Schubert 		error = EINVAL;
58928730bd3SSimon Schubert 		goto out;
59028730bd3SSimon Schubert 	}
59128730bd3SSimon Schubert 	if (symstrindex == -1)
59228730bd3SSimon Schubert 		panic("lost symbol string index");
59328730bd3SSimon Schubert 	/* Allocate space for and load the symbol strings */
59428730bd3SSimon Schubert 	ef->ddbstrcnt = shdr[symstrindex].sh_size;
59528730bd3SSimon Schubert 	ef->ddbstrtab = kmalloc(shdr[symstrindex].sh_size, M_LINKER, M_WAITOK);
59628730bd3SSimon Schubert 	if (ef->ddbstrtab == NULL) {
59728730bd3SSimon Schubert 		error = ENOMEM;
59828730bd3SSimon Schubert 		goto out;
59928730bd3SSimon Schubert 	}
60028730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, ef->ddbstrtab,
60128730bd3SSimon Schubert 			shdr[symstrindex].sh_size, shdr[symstrindex].sh_offset,
60228730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
60328730bd3SSimon Schubert 	if (error)
60428730bd3SSimon Schubert 		goto out;
60528730bd3SSimon Schubert 	if (resid != 0) {
60628730bd3SSimon Schubert 		error = EINVAL;
60728730bd3SSimon Schubert 		goto out;
60828730bd3SSimon Schubert 	}
60928730bd3SSimon Schubert 	/* Do we have a string table for the section names?  */
61028730bd3SSimon Schubert 	shstrindex = -1;
61128730bd3SSimon Schubert 	if (hdr->e_shstrndx != 0 &&
61228730bd3SSimon Schubert 	    shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
61328730bd3SSimon Schubert 		shstrindex = hdr->e_shstrndx;
61428730bd3SSimon Schubert 		ef->shstrcnt = shdr[shstrindex].sh_size;
61528730bd3SSimon Schubert 		ef->shstrtab = kmalloc(shdr[shstrindex].sh_size, M_LINKER,
61628730bd3SSimon Schubert 				       M_WAITOK);
61728730bd3SSimon Schubert 		if (ef->shstrtab == NULL) {
61828730bd3SSimon Schubert 			error = ENOMEM;
61928730bd3SSimon Schubert 			goto out;
62028730bd3SSimon Schubert 		}
62128730bd3SSimon Schubert 		error = vn_rdwr(UIO_READ, vp, ef->shstrtab,
62228730bd3SSimon Schubert 				shdr[shstrindex].sh_size, shdr[shstrindex].sh_offset,
62328730bd3SSimon Schubert 				UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
62428730bd3SSimon Schubert 		if (error)
62528730bd3SSimon Schubert 			goto out;
62628730bd3SSimon Schubert 		if (resid != 0) {
62728730bd3SSimon Schubert 			error = EINVAL;
62828730bd3SSimon Schubert 			goto out;
62928730bd3SSimon Schubert 		}
63028730bd3SSimon Schubert 	}
63128730bd3SSimon Schubert 	/* Size up code/data(progbits) and bss(nobits). */
63228730bd3SSimon Schubert 	alignmask = 0;
63328730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
63428730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
63528730bd3SSimon Schubert 		case SHT_PROGBITS:
63628730bd3SSimon Schubert 		case SHT_NOBITS:
63728730bd3SSimon Schubert 			alignmask = shdr[i].sh_addralign - 1;
63828730bd3SSimon Schubert 			mapsize += alignmask;
63928730bd3SSimon Schubert 			mapsize &= ~alignmask;
64028730bd3SSimon Schubert 			mapsize += shdr[i].sh_size;
64128730bd3SSimon Schubert 			break;
64228730bd3SSimon Schubert 		}
64328730bd3SSimon Schubert 	}
64428730bd3SSimon Schubert 
64528730bd3SSimon Schubert 	/*
64628730bd3SSimon Schubert 	 * We know how much space we need for the text/data/bss/etc. This
64728730bd3SSimon Schubert 	 * stuff needs to be in a single chunk so that profiling etc can get
64828730bd3SSimon Schubert 	 * the bounds and gdb can associate offsets with modules
64928730bd3SSimon Schubert 	 */
65028730bd3SSimon Schubert 	ef->object = vm_object_allocate(OBJT_DEFAULT,
65128730bd3SSimon Schubert 					round_page(mapsize) >> PAGE_SHIFT);
65228730bd3SSimon Schubert 	if (ef->object == NULL) {
65328730bd3SSimon Schubert 		error = ENOMEM;
65428730bd3SSimon Schubert 		goto out;
65528730bd3SSimon Schubert 	}
65628730bd3SSimon Schubert 	ef->address = (caddr_t) vm_map_min(&kernel_map);
65728730bd3SSimon Schubert 
65828730bd3SSimon Schubert 	/*
659*b2b3ffcdSSimon Schubert 	 * In order to satisfy x86_64's architectural requirements on the
66028730bd3SSimon Schubert 	 * location of code and data in the kernel's address space, request a
66128730bd3SSimon Schubert 	 * mapping that is above the kernel.
66228730bd3SSimon Schubert 	 */
66328730bd3SSimon Schubert 	mapbase = KERNBASE;
66428730bd3SSimon Schubert 	error = vm_map_find(&kernel_map, ef->object, 0, &mapbase,
66528730bd3SSimon Schubert 			    round_page(mapsize), TRUE, VM_MAPTYPE_NORMAL,
66628730bd3SSimon Schubert 			    VM_PROT_ALL, VM_PROT_ALL, FALSE);
66728730bd3SSimon Schubert 	if (error) {
66828730bd3SSimon Schubert 		vm_object_deallocate(ef->object);
66928730bd3SSimon Schubert 		ef->object = 0;
67028730bd3SSimon Schubert 		goto out;
67128730bd3SSimon Schubert 	}
67228730bd3SSimon Schubert 	/* Wire the pages */
67328730bd3SSimon Schubert 	error = vm_map_wire(&kernel_map, mapbase,
67428730bd3SSimon Schubert 			    mapbase + round_page(mapsize), 0);
67528730bd3SSimon Schubert 	if (error != KERN_SUCCESS) {
67628730bd3SSimon Schubert 		error = ENOMEM;
67728730bd3SSimon Schubert 		goto out;
67828730bd3SSimon Schubert 	}
67928730bd3SSimon Schubert 	/* Inform the kld system about the situation */
68028730bd3SSimon Schubert 	lf->address = ef->address = (caddr_t) mapbase;
68128730bd3SSimon Schubert 	lf->size = mapsize;
68228730bd3SSimon Schubert 
68328730bd3SSimon Schubert 	/*
68428730bd3SSimon Schubert 	 * Now load code/data(progbits), zero bss(nobits), allocate space for
68528730bd3SSimon Schubert 	 * and load relocs
68628730bd3SSimon Schubert 	 */
68728730bd3SSimon Schubert 	pb = 0;
68828730bd3SSimon Schubert 	rl = 0;
68928730bd3SSimon Schubert 	ra = 0;
69028730bd3SSimon Schubert 	alignmask = 0;
69128730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
69228730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
69328730bd3SSimon Schubert 		case SHT_PROGBITS:
69428730bd3SSimon Schubert 		case SHT_NOBITS:
69528730bd3SSimon Schubert 			alignmask = shdr[i].sh_addralign - 1;
69628730bd3SSimon Schubert 			mapbase += alignmask;
69728730bd3SSimon Schubert 			mapbase &= ~alignmask;
69828730bd3SSimon Schubert 			if (ef->shstrtab && shdr[i].sh_name != 0)
69928730bd3SSimon Schubert 				ef->progtab[pb].name =
70028730bd3SSimon Schubert 					ef->shstrtab + shdr[i].sh_name;
70128730bd3SSimon Schubert 			else if (shdr[i].sh_type == SHT_PROGBITS)
70228730bd3SSimon Schubert 				ef->progtab[pb].name = "<<PROGBITS>>";
70328730bd3SSimon Schubert 			else
70428730bd3SSimon Schubert 				ef->progtab[pb].name = "<<NOBITS>>";
70528730bd3SSimon Schubert #if 0
70628730bd3SSimon Schubert 			if (ef->progtab[pb].name != NULL &&
70728730bd3SSimon Schubert 			    !strcmp(ef->progtab[pb].name, "set_pcpu"))
70828730bd3SSimon Schubert 				ef->progtab[pb].addr =
70928730bd3SSimon Schubert 					dpcpu_alloc(shdr[i].sh_size);
71028730bd3SSimon Schubert #ifdef VIMAGE
71128730bd3SSimon Schubert 			else if (ef->progtab[pb].name != NULL &&
71228730bd3SSimon Schubert 				 !strcmp(ef->progtab[pb].name, VNET_SETNAME))
71328730bd3SSimon Schubert 				ef->progtab[pb].addr =
71428730bd3SSimon Schubert 					vnet_data_alloc(shdr[i].sh_size);
71528730bd3SSimon Schubert #endif
71628730bd3SSimon Schubert 			else
71728730bd3SSimon Schubert #endif
71828730bd3SSimon Schubert 				ef->progtab[pb].addr =
71928730bd3SSimon Schubert 					(void *)(uintptr_t) mapbase;
72028730bd3SSimon Schubert 			if (ef->progtab[pb].addr == NULL) {
72128730bd3SSimon Schubert 				error = ENOSPC;
72228730bd3SSimon Schubert 				goto out;
72328730bd3SSimon Schubert 			}
72428730bd3SSimon Schubert 			ef->progtab[pb].size = shdr[i].sh_size;
72528730bd3SSimon Schubert 			ef->progtab[pb].sec = i;
72628730bd3SSimon Schubert 			if (shdr[i].sh_type == SHT_PROGBITS) {
72728730bd3SSimon Schubert 				error = vn_rdwr(UIO_READ, vp,
72828730bd3SSimon Schubert 						ef->progtab[pb].addr,
72928730bd3SSimon Schubert 						shdr[i].sh_size, shdr[i].sh_offset,
73028730bd3SSimon Schubert 						UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred,
73128730bd3SSimon Schubert 						&resid);
73228730bd3SSimon Schubert 				if (error)
73328730bd3SSimon Schubert 					goto out;
73428730bd3SSimon Schubert 				if (resid != 0) {
73528730bd3SSimon Schubert 					error = EINVAL;
73628730bd3SSimon Schubert 					goto out;
73728730bd3SSimon Schubert 				}
73828730bd3SSimon Schubert #if 0
73928730bd3SSimon Schubert 				/* Initialize the per-cpu or vnet area. */
74028730bd3SSimon Schubert 				if (ef->progtab[pb].addr != (void *)mapbase &&
74128730bd3SSimon Schubert 				    !strcmp(ef->progtab[pb].name, "set_pcpu"))
74228730bd3SSimon Schubert 					dpcpu_copy(ef->progtab[pb].addr,
74328730bd3SSimon Schubert 						   shdr[i].sh_size);
74428730bd3SSimon Schubert #ifdef VIMAGE
74528730bd3SSimon Schubert 				else if (ef->progtab[pb].addr !=
74628730bd3SSimon Schubert 					 (void *)mapbase &&
74728730bd3SSimon Schubert 					 !strcmp(ef->progtab[pb].name, VNET_SETNAME))
74828730bd3SSimon Schubert 					vnet_data_copy(ef->progtab[pb].addr,
74928730bd3SSimon Schubert 						       shdr[i].sh_size);
75028730bd3SSimon Schubert #endif
75128730bd3SSimon Schubert #endif
75228730bd3SSimon Schubert 			} else
75328730bd3SSimon Schubert 				bzero(ef->progtab[pb].addr, shdr[i].sh_size);
75428730bd3SSimon Schubert 
75528730bd3SSimon Schubert 			/* Update all symbol values with the offset. */
75628730bd3SSimon Schubert 			for (j = 0; j < ef->ddbsymcnt; j++) {
75728730bd3SSimon Schubert 				es = &ef->ddbsymtab[j];
75828730bd3SSimon Schubert 				if (es->st_shndx != i)
75928730bd3SSimon Schubert 					continue;
76028730bd3SSimon Schubert 				es->st_value += (Elf_Addr) ef->progtab[pb].addr;
76128730bd3SSimon Schubert 			}
76228730bd3SSimon Schubert 			mapbase += shdr[i].sh_size;
76328730bd3SSimon Schubert 			pb++;
76428730bd3SSimon Schubert 			break;
76528730bd3SSimon Schubert 		case SHT_REL:
76628730bd3SSimon Schubert 			ef->reltab[rl].rel = kmalloc(shdr[i].sh_size, M_LINKER, M_WAITOK);
76728730bd3SSimon Schubert 			ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
76828730bd3SSimon Schubert 			ef->reltab[rl].sec = shdr[i].sh_info;
76928730bd3SSimon Schubert 			error = vn_rdwr(UIO_READ, vp,
77028730bd3SSimon Schubert 					(void *)ef->reltab[rl].rel,
77128730bd3SSimon Schubert 					shdr[i].sh_size, shdr[i].sh_offset,
77228730bd3SSimon Schubert 					UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
77328730bd3SSimon Schubert 			if (error)
77428730bd3SSimon Schubert 				goto out;
77528730bd3SSimon Schubert 			if (resid != 0) {
77628730bd3SSimon Schubert 				error = EINVAL;
77728730bd3SSimon Schubert 				goto out;
77828730bd3SSimon Schubert 			}
77928730bd3SSimon Schubert 			rl++;
78028730bd3SSimon Schubert 			break;
78128730bd3SSimon Schubert 		case SHT_RELA:
78228730bd3SSimon Schubert 			ef->relatab[ra].rela = kmalloc(shdr[i].sh_size, M_LINKER, M_WAITOK);
78328730bd3SSimon Schubert 			ef->relatab[ra].nrela = shdr[i].sh_size / sizeof(Elf_Rela);
78428730bd3SSimon Schubert 			ef->relatab[ra].sec = shdr[i].sh_info;
78528730bd3SSimon Schubert 			error = vn_rdwr(UIO_READ, vp,
78628730bd3SSimon Schubert 					(void *)ef->relatab[ra].rela,
78728730bd3SSimon Schubert 					shdr[i].sh_size, shdr[i].sh_offset,
78828730bd3SSimon Schubert 					UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
78928730bd3SSimon Schubert 			if (error)
79028730bd3SSimon Schubert 				goto out;
79128730bd3SSimon Schubert 			if (resid != 0) {
79228730bd3SSimon Schubert 				error = EINVAL;
79328730bd3SSimon Schubert 				goto out;
79428730bd3SSimon Schubert 			}
79528730bd3SSimon Schubert 			ra++;
79628730bd3SSimon Schubert 			break;
79728730bd3SSimon Schubert 		}
79828730bd3SSimon Schubert 	}
79928730bd3SSimon Schubert 	if (pb != ef->nprogtab)
80028730bd3SSimon Schubert 		panic("lost progbits");
80128730bd3SSimon Schubert 	if (rl != ef->nreltab)
80228730bd3SSimon Schubert 		panic("lost reltab");
80328730bd3SSimon Schubert 	if (ra != ef->nrelatab)
80428730bd3SSimon Schubert 		panic("lost relatab");
80528730bd3SSimon Schubert 	if (mapbase != (vm_offset_t) ef->address + mapsize)
80628730bd3SSimon Schubert 		panic("mapbase 0x%lx != address %p + mapsize 0x%lx (0x%lx)\n",
80728730bd3SSimon Schubert 		      mapbase, ef->address, mapsize,
80828730bd3SSimon Schubert 		      (vm_offset_t) ef->address + mapsize);
80928730bd3SSimon Schubert 
81028730bd3SSimon Schubert 	/* Local intra-module relocations */
81128730bd3SSimon Schubert 	link_elf_obj_reloc_local(lf);
81228730bd3SSimon Schubert 
81328730bd3SSimon Schubert 	/* Pull in dependencies */
81428730bd3SSimon Schubert 	error = linker_load_dependencies(lf);
81528730bd3SSimon Schubert 	if (error)
81628730bd3SSimon Schubert 		goto out;
81728730bd3SSimon Schubert 
81828730bd3SSimon Schubert 	/* External relocations */
81928730bd3SSimon Schubert 	error = relocate_file(lf);
82028730bd3SSimon Schubert 	if (error)
82128730bd3SSimon Schubert 		goto out;
82228730bd3SSimon Schubert 
82328730bd3SSimon Schubert 	*result = lf;
82428730bd3SSimon Schubert 
82528730bd3SSimon Schubert out:
82628730bd3SSimon Schubert 	if (error && lf)
82728730bd3SSimon Schubert 		linker_file_unload(lf /*, LINKER_UNLOAD_FORCE */);
82828730bd3SSimon Schubert 	if (hdr)
82928730bd3SSimon Schubert 		kfree(hdr, M_LINKER);
83028730bd3SSimon Schubert 	vn_unlock(vp);
83128730bd3SSimon Schubert 	vn_close(vp, FREAD);
83228730bd3SSimon Schubert 
83328730bd3SSimon Schubert 	return error;
83428730bd3SSimon Schubert }
83528730bd3SSimon Schubert 
83628730bd3SSimon Schubert static void
83728730bd3SSimon Schubert link_elf_obj_unload_file(linker_file_t file)
83828730bd3SSimon Schubert {
83928730bd3SSimon Schubert 	elf_file_t	ef = file->priv;
84028730bd3SSimon Schubert 	int i;
84128730bd3SSimon Schubert 
84228730bd3SSimon Schubert 	if (ef->progtab) {
84328730bd3SSimon Schubert 		for (i = 0; i < ef->nprogtab; i++) {
84428730bd3SSimon Schubert 			if (ef->progtab[i].size == 0)
84528730bd3SSimon Schubert 				continue;
84628730bd3SSimon Schubert 			if (ef->progtab[i].name == NULL)
84728730bd3SSimon Schubert 				continue;
84828730bd3SSimon Schubert #if 0
84928730bd3SSimon Schubert 			if (!strcmp(ef->progtab[i].name, "set_pcpu"))
85028730bd3SSimon Schubert 				dpcpu_free(ef->progtab[i].addr,
85128730bd3SSimon Schubert 				    ef->progtab[i].size);
85228730bd3SSimon Schubert #ifdef VIMAGE
85328730bd3SSimon Schubert 			else if (!strcmp(ef->progtab[i].name, VNET_SETNAME))
85428730bd3SSimon Schubert 				vnet_data_free(ef->progtab[i].addr,
85528730bd3SSimon Schubert 				    ef->progtab[i].size);
85628730bd3SSimon Schubert #endif
85728730bd3SSimon Schubert #endif
85828730bd3SSimon Schubert 		}
85928730bd3SSimon Schubert 	}
86028730bd3SSimon Schubert 	if (ef->preloaded) {
86128730bd3SSimon Schubert 		if (ef->reltab)
86228730bd3SSimon Schubert 			kfree(ef->reltab, M_LINKER);
86328730bd3SSimon Schubert 		if (ef->relatab)
86428730bd3SSimon Schubert 			kfree(ef->relatab, M_LINKER);
86528730bd3SSimon Schubert 		if (ef->progtab)
86628730bd3SSimon Schubert 			kfree(ef->progtab, M_LINKER);
86728730bd3SSimon Schubert 		if (ef->ctftab)
86828730bd3SSimon Schubert 			kfree(ef->ctftab, M_LINKER);
86928730bd3SSimon Schubert 		if (ef->ctfoff)
87028730bd3SSimon Schubert 			kfree(ef->ctfoff, M_LINKER);
87128730bd3SSimon Schubert 		if (ef->typoff)
87228730bd3SSimon Schubert 			kfree(ef->typoff, M_LINKER);
87328730bd3SSimon Schubert 		if (file->filename != NULL)
87428730bd3SSimon Schubert 			preload_delete_name(file->filename);
87528730bd3SSimon Schubert 		kfree(ef, M_LINKER);
87628730bd3SSimon Schubert 		/* XXX reclaim module memory? */
87728730bd3SSimon Schubert 		return;
87828730bd3SSimon Schubert 	}
87928730bd3SSimon Schubert 
88028730bd3SSimon Schubert 	for (i = 0; i < ef->nreltab; i++)
88128730bd3SSimon Schubert 		if (ef->reltab[i].rel)
88228730bd3SSimon Schubert 			kfree(ef->reltab[i].rel, M_LINKER);
88328730bd3SSimon Schubert 	for (i = 0; i < ef->nrelatab; i++)
88428730bd3SSimon Schubert 		if (ef->relatab[i].rela)
88528730bd3SSimon Schubert 			kfree(ef->relatab[i].rela, M_LINKER);
88628730bd3SSimon Schubert 	if (ef->reltab)
88728730bd3SSimon Schubert 		kfree(ef->reltab, M_LINKER);
88828730bd3SSimon Schubert 	if (ef->relatab)
88928730bd3SSimon Schubert 		kfree(ef->relatab, M_LINKER);
89028730bd3SSimon Schubert 	if (ef->progtab)
89128730bd3SSimon Schubert 		kfree(ef->progtab, M_LINKER);
89228730bd3SSimon Schubert 
89328730bd3SSimon Schubert 	if (ef->object) {
89428730bd3SSimon Schubert 		vm_map_remove(&kernel_map, (vm_offset_t) ef->address,
89528730bd3SSimon Schubert 		    (vm_offset_t) ef->address +
89628730bd3SSimon Schubert 		    (ef->object->size << PAGE_SHIFT));
89728730bd3SSimon Schubert 	}
89828730bd3SSimon Schubert 	if (ef->e_shdr)
89928730bd3SSimon Schubert 		kfree(ef->e_shdr, M_LINKER);
90028730bd3SSimon Schubert 	if (ef->ddbsymtab)
90128730bd3SSimon Schubert 		kfree(ef->ddbsymtab, M_LINKER);
90228730bd3SSimon Schubert 	if (ef->ddbstrtab)
90328730bd3SSimon Schubert 		kfree(ef->ddbstrtab, M_LINKER);
90428730bd3SSimon Schubert 	if (ef->shstrtab)
90528730bd3SSimon Schubert 		kfree(ef->shstrtab, M_LINKER);
90628730bd3SSimon Schubert 	if (ef->ctftab)
90728730bd3SSimon Schubert 		kfree(ef->ctftab, M_LINKER);
90828730bd3SSimon Schubert 	if (ef->ctfoff)
90928730bd3SSimon Schubert 		kfree(ef->ctfoff, M_LINKER);
91028730bd3SSimon Schubert 	if (ef->typoff)
91128730bd3SSimon Schubert 		kfree(ef->typoff, M_LINKER);
91228730bd3SSimon Schubert 	kfree(ef, M_LINKER);
91328730bd3SSimon Schubert }
91428730bd3SSimon Schubert 
91528730bd3SSimon Schubert static const char *
91628730bd3SSimon Schubert symbol_name(elf_file_t ef, Elf_Size r_info)
91728730bd3SSimon Schubert {
91828730bd3SSimon Schubert 	const Elf_Sym  *ref;
91928730bd3SSimon Schubert 
92028730bd3SSimon Schubert 	if (ELF_R_SYM(r_info)) {
92128730bd3SSimon Schubert 		ref = ef->ddbsymtab + ELF_R_SYM(r_info);
92228730bd3SSimon Schubert 		return ef->ddbstrtab + ref->st_name;
92328730bd3SSimon Schubert 	} else
92428730bd3SSimon Schubert 		return NULL;
92528730bd3SSimon Schubert }
92628730bd3SSimon Schubert 
92728730bd3SSimon Schubert static Elf_Addr
92828730bd3SSimon Schubert findbase(elf_file_t ef, int sec)
92928730bd3SSimon Schubert {
93028730bd3SSimon Schubert 	int i;
93128730bd3SSimon Schubert 	Elf_Addr base = 0;
93228730bd3SSimon Schubert 
93328730bd3SSimon Schubert 	for (i = 0; i < ef->nprogtab; i++) {
93428730bd3SSimon Schubert 		if (sec == ef->progtab[i].sec) {
93528730bd3SSimon Schubert 			base = (Elf_Addr)ef->progtab[i].addr;
93628730bd3SSimon Schubert 			break;
93728730bd3SSimon Schubert 		}
93828730bd3SSimon Schubert 	}
93928730bd3SSimon Schubert 	return base;
94028730bd3SSimon Schubert }
94128730bd3SSimon Schubert 
94228730bd3SSimon Schubert static int
94328730bd3SSimon Schubert relocate_file(linker_file_t lf)
94428730bd3SSimon Schubert {
94528730bd3SSimon Schubert 	elf_file_t	ef = lf->priv;
94628730bd3SSimon Schubert 	const Elf_Rel *rellim;
94728730bd3SSimon Schubert 	const Elf_Rel *rel;
94828730bd3SSimon Schubert 	const Elf_Rela *relalim;
94928730bd3SSimon Schubert 	const Elf_Rela *rela;
95028730bd3SSimon Schubert 	const char *symname;
95128730bd3SSimon Schubert 	const Elf_Sym *sym;
95228730bd3SSimon Schubert 	int i;
95328730bd3SSimon Schubert 	Elf_Size symidx;
95428730bd3SSimon Schubert 	Elf_Addr base;
95528730bd3SSimon Schubert 
95628730bd3SSimon Schubert 	/* Perform relocations without addend if there are any: */
95728730bd3SSimon Schubert 	for (i = 0; i < ef->nreltab; i++) {
95828730bd3SSimon Schubert 		rel = ef->reltab[i].rel;
95928730bd3SSimon Schubert 		if (rel == NULL)
96028730bd3SSimon Schubert 			panic("lost a reltab!");
96128730bd3SSimon Schubert 		rellim = rel + ef->reltab[i].nrel;
96228730bd3SSimon Schubert 		base = findbase(ef, ef->reltab[i].sec);
96328730bd3SSimon Schubert 		if (base == 0)
96428730bd3SSimon Schubert 			panic("lost base for reltab");
96528730bd3SSimon Schubert 		for ( ; rel < rellim; rel++) {
96628730bd3SSimon Schubert 			symidx = ELF_R_SYM(rel->r_info);
96728730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
96828730bd3SSimon Schubert 				continue;
96928730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
97028730bd3SSimon Schubert 			/* Local relocs are already done */
97128730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL)
97228730bd3SSimon Schubert 				continue;
97328730bd3SSimon Schubert 			if (elf_reloc(lf, base, rel, ELF_RELOC_REL,
97428730bd3SSimon Schubert 			    elf_obj_lookup)) {
97528730bd3SSimon Schubert 				symname = symbol_name(ef, rel->r_info);
97628730bd3SSimon Schubert 				kprintf("link_elf_obj_obj: symbol %s undefined\n",
97728730bd3SSimon Schubert 				    symname);
97828730bd3SSimon Schubert 				return ENOENT;
97928730bd3SSimon Schubert 			}
98028730bd3SSimon Schubert 		}
98128730bd3SSimon Schubert 	}
98228730bd3SSimon Schubert 
98328730bd3SSimon Schubert 	/* Perform relocations with addend if there are any: */
98428730bd3SSimon Schubert 	for (i = 0; i < ef->nrelatab; i++) {
98528730bd3SSimon Schubert 		rela = ef->relatab[i].rela;
98628730bd3SSimon Schubert 		if (rela == NULL)
98728730bd3SSimon Schubert 			panic("lost a relatab!");
98828730bd3SSimon Schubert 		relalim = rela + ef->relatab[i].nrela;
98928730bd3SSimon Schubert 		base = findbase(ef, ef->relatab[i].sec);
99028730bd3SSimon Schubert 		if (base == 0)
99128730bd3SSimon Schubert 			panic("lost base for relatab");
99228730bd3SSimon Schubert 		for ( ; rela < relalim; rela++) {
99328730bd3SSimon Schubert 			symidx = ELF_R_SYM(rela->r_info);
99428730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
99528730bd3SSimon Schubert 				continue;
99628730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
99728730bd3SSimon Schubert 			/* Local relocs are already done */
99828730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL)
99928730bd3SSimon Schubert 				continue;
100028730bd3SSimon Schubert 			if (elf_reloc(lf, base, rela, ELF_RELOC_RELA,
100128730bd3SSimon Schubert 			    elf_obj_lookup)) {
100228730bd3SSimon Schubert 				symname = symbol_name(ef, rela->r_info);
100328730bd3SSimon Schubert 				kprintf("link_elf_obj_obj: symbol %s undefined\n",
100428730bd3SSimon Schubert 				    symname);
100528730bd3SSimon Schubert 				return ENOENT;
100628730bd3SSimon Schubert 			}
100728730bd3SSimon Schubert 		}
100828730bd3SSimon Schubert 	}
100928730bd3SSimon Schubert 
101028730bd3SSimon Schubert 	return 0;
101128730bd3SSimon Schubert }
101228730bd3SSimon Schubert 
101328730bd3SSimon Schubert static int
101428730bd3SSimon Schubert link_elf_obj_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
101528730bd3SSimon Schubert {
101628730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
101728730bd3SSimon Schubert 	const Elf_Sym *symp;
101828730bd3SSimon Schubert 	const char *strp;
101928730bd3SSimon Schubert 	int i;
102028730bd3SSimon Schubert 
102128730bd3SSimon Schubert 	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
102228730bd3SSimon Schubert 		strp = ef->ddbstrtab + symp->st_name;
102328730bd3SSimon Schubert 		if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
102428730bd3SSimon Schubert 			*sym = (c_linker_sym_t) symp;
102528730bd3SSimon Schubert 			return 0;
102628730bd3SSimon Schubert 		}
102728730bd3SSimon Schubert 	}
102828730bd3SSimon Schubert 	return ENOENT;
102928730bd3SSimon Schubert }
103028730bd3SSimon Schubert 
103128730bd3SSimon Schubert static int
103228730bd3SSimon Schubert link_elf_obj_symbol_values(linker_file_t lf, c_linker_sym_t sym,
103328730bd3SSimon Schubert     linker_symval_t *symval)
103428730bd3SSimon Schubert {
103528730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
103628730bd3SSimon Schubert 	const Elf_Sym *es = (const Elf_Sym*) sym;
103728730bd3SSimon Schubert 
103828730bd3SSimon Schubert 	if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
103928730bd3SSimon Schubert 		symval->name = ef->ddbstrtab + es->st_name;
104028730bd3SSimon Schubert 		symval->value = (caddr_t)es->st_value;
104128730bd3SSimon Schubert 		symval->size = es->st_size;
104228730bd3SSimon Schubert 		return 0;
104328730bd3SSimon Schubert 	}
104428730bd3SSimon Schubert 	return ENOENT;
104528730bd3SSimon Schubert }
104628730bd3SSimon Schubert 
104728730bd3SSimon Schubert static int
104828730bd3SSimon Schubert link_elf_obj_search_symbol(linker_file_t lf, caddr_t value,
104928730bd3SSimon Schubert     c_linker_sym_t *sym, long *diffp)
105028730bd3SSimon Schubert {
105128730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
105228730bd3SSimon Schubert 	u_long off = (uintptr_t) (void *) value;
105328730bd3SSimon Schubert 	u_long diff = off;
105428730bd3SSimon Schubert 	u_long st_value;
105528730bd3SSimon Schubert 	const Elf_Sym *es;
105628730bd3SSimon Schubert 	const Elf_Sym *best = 0;
105728730bd3SSimon Schubert 	int i;
105828730bd3SSimon Schubert 
105928730bd3SSimon Schubert 	for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
106028730bd3SSimon Schubert 		if (es->st_name == 0)
106128730bd3SSimon Schubert 			continue;
106228730bd3SSimon Schubert 		st_value = es->st_value;
106328730bd3SSimon Schubert 		if (off >= st_value) {
106428730bd3SSimon Schubert 			if (off - st_value < diff) {
106528730bd3SSimon Schubert 				diff = off - st_value;
106628730bd3SSimon Schubert 				best = es;
106728730bd3SSimon Schubert 				if (diff == 0)
106828730bd3SSimon Schubert 					break;
106928730bd3SSimon Schubert 			} else if (off - st_value == diff) {
107028730bd3SSimon Schubert 				best = es;
107128730bd3SSimon Schubert 			}
107228730bd3SSimon Schubert 		}
107328730bd3SSimon Schubert 	}
107428730bd3SSimon Schubert 	if (best == 0)
107528730bd3SSimon Schubert 		*diffp = off;
107628730bd3SSimon Schubert 	else
107728730bd3SSimon Schubert 		*diffp = diff;
107828730bd3SSimon Schubert 	*sym = (c_linker_sym_t) best;
107928730bd3SSimon Schubert 
108028730bd3SSimon Schubert 	return 0;
108128730bd3SSimon Schubert }
108228730bd3SSimon Schubert 
108328730bd3SSimon Schubert /*
108428730bd3SSimon Schubert  * Look up a linker set on an ELF system.
108528730bd3SSimon Schubert  */
108628730bd3SSimon Schubert static int
108728730bd3SSimon Schubert link_elf_obj_lookup_set(linker_file_t lf, const char *name,
108828730bd3SSimon Schubert     void ***startp, void ***stopp, int *countp)
108928730bd3SSimon Schubert {
109028730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
109128730bd3SSimon Schubert 	void **start, **stop;
109228730bd3SSimon Schubert 	int i, count;
109328730bd3SSimon Schubert 
109428730bd3SSimon Schubert 	/* Relative to section number */
109528730bd3SSimon Schubert 	for (i = 0; i < ef->nprogtab; i++) {
109628730bd3SSimon Schubert 		if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
109728730bd3SSimon Schubert 		    strcmp(ef->progtab[i].name + 4, name) == 0) {
109828730bd3SSimon Schubert 			start  = (void **)ef->progtab[i].addr;
109928730bd3SSimon Schubert 			stop = (void **)((char *)ef->progtab[i].addr +
110028730bd3SSimon Schubert 			    ef->progtab[i].size);
110128730bd3SSimon Schubert 			count = stop - start;
110228730bd3SSimon Schubert 			if (startp)
110328730bd3SSimon Schubert 				*startp = start;
110428730bd3SSimon Schubert 			if (stopp)
110528730bd3SSimon Schubert 				*stopp = stop;
110628730bd3SSimon Schubert 			if (countp)
110728730bd3SSimon Schubert 				*countp = count;
110828730bd3SSimon Schubert 			return (0);
110928730bd3SSimon Schubert 		}
111028730bd3SSimon Schubert 	}
111128730bd3SSimon Schubert 	return (ESRCH);
111228730bd3SSimon Schubert }
111328730bd3SSimon Schubert 
111428730bd3SSimon Schubert /*
111528730bd3SSimon Schubert  * Symbol lookup function that can be used when the symbol index is known (ie
111628730bd3SSimon Schubert  * in relocations). It uses the symbol index instead of doing a fully fledged
111728730bd3SSimon Schubert  * hash table based lookup when such is valid. For example for local symbols.
111828730bd3SSimon Schubert  * This is not only more efficient, it's also more correct. It's not always
111928730bd3SSimon Schubert  * the case that the symbol can be found through the hash table.
112028730bd3SSimon Schubert  */
112128730bd3SSimon Schubert static int
112228730bd3SSimon Schubert elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *result)
112328730bd3SSimon Schubert {
112428730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
112528730bd3SSimon Schubert 	const Elf_Sym *sym;
112628730bd3SSimon Schubert 	const char *symbol;
112728730bd3SSimon Schubert 
112828730bd3SSimon Schubert 	/* Don't even try to lookup the symbol if the index is bogus. */
112928730bd3SSimon Schubert 	if (symidx >= ef->ddbsymcnt)
113028730bd3SSimon Schubert 		return (ENOENT);
113128730bd3SSimon Schubert 
113228730bd3SSimon Schubert 	sym = ef->ddbsymtab + symidx;
113328730bd3SSimon Schubert 
113428730bd3SSimon Schubert 	/* Quick answer if there is a definition included. */
113528730bd3SSimon Schubert 	if (sym->st_shndx != SHN_UNDEF) {
113628730bd3SSimon Schubert 		*result = sym->st_value;
113728730bd3SSimon Schubert 		return (0);
113828730bd3SSimon Schubert 	}
113928730bd3SSimon Schubert 
114028730bd3SSimon Schubert 	/* If we get here, then it is undefined and needs a lookup. */
114128730bd3SSimon Schubert 	switch (ELF_ST_BIND(sym->st_info)) {
114228730bd3SSimon Schubert 	case STB_LOCAL:
114328730bd3SSimon Schubert 		/* Local, but undefined? huh? */
114428730bd3SSimon Schubert 		return (ENOENT);
114528730bd3SSimon Schubert 
114628730bd3SSimon Schubert 	case STB_GLOBAL:
114728730bd3SSimon Schubert 		/* Relative to Data or Function name */
114828730bd3SSimon Schubert 		symbol = ef->ddbstrtab + sym->st_name;
114928730bd3SSimon Schubert 
115028730bd3SSimon Schubert 		/* Force a lookup failure if the symbol name is bogus. */
115128730bd3SSimon Schubert 		if (*symbol == 0)
115228730bd3SSimon Schubert 			return (ENOENT);
115328730bd3SSimon Schubert 		return (linker_file_lookup_symbol(lf, symbol, deps, (caddr_t *)result));
115428730bd3SSimon Schubert 
115528730bd3SSimon Schubert 	case STB_WEAK:
115628730bd3SSimon Schubert 		kprintf("link_elf_obj_obj: Weak symbols not supported\n");
115728730bd3SSimon Schubert 		return (ENOENT);
115828730bd3SSimon Schubert 
115928730bd3SSimon Schubert 	default:
116028730bd3SSimon Schubert 		return (ENOENT);
116128730bd3SSimon Schubert 	}
116228730bd3SSimon Schubert }
116328730bd3SSimon Schubert 
116428730bd3SSimon Schubert static void
116528730bd3SSimon Schubert link_elf_obj_fix_link_set(elf_file_t ef)
116628730bd3SSimon Schubert {
116728730bd3SSimon Schubert 	static const char startn[] = "__start_";
116828730bd3SSimon Schubert 	static const char stopn[] = "__stop_";
116928730bd3SSimon Schubert 	Elf_Sym *sym;
117028730bd3SSimon Schubert 	const char *sym_name, *linkset_name;
117128730bd3SSimon Schubert 	Elf_Addr startp, stopp;
117228730bd3SSimon Schubert 	Elf_Size symidx;
117328730bd3SSimon Schubert 	int start, i;
117428730bd3SSimon Schubert 
117528730bd3SSimon Schubert 	startp = stopp = 0;
117628730bd3SSimon Schubert 	for (symidx = 1 /* zero entry is special */;
117728730bd3SSimon Schubert 		symidx < ef->ddbsymcnt; symidx++) {
117828730bd3SSimon Schubert 		sym = ef->ddbsymtab + symidx;
117928730bd3SSimon Schubert 		if (sym->st_shndx != SHN_UNDEF)
118028730bd3SSimon Schubert 			continue;
118128730bd3SSimon Schubert 
118228730bd3SSimon Schubert 		sym_name = ef->ddbstrtab + sym->st_name;
118328730bd3SSimon Schubert 		if (strncmp(sym_name, startn, sizeof(startn) - 1) == 0) {
118428730bd3SSimon Schubert 			start = 1;
118528730bd3SSimon Schubert 			linkset_name = sym_name + sizeof(startn) - 1;
118628730bd3SSimon Schubert 		}
118728730bd3SSimon Schubert 		else if (strncmp(sym_name, stopn, sizeof(stopn) - 1) == 0) {
118828730bd3SSimon Schubert 			start = 0;
118928730bd3SSimon Schubert 			linkset_name = sym_name + sizeof(stopn) - 1;
119028730bd3SSimon Schubert 		}
119128730bd3SSimon Schubert 		else
119228730bd3SSimon Schubert 			continue;
119328730bd3SSimon Schubert 
119428730bd3SSimon Schubert 		for (i = 0; i < ef->nprogtab; i++) {
119528730bd3SSimon Schubert 			if (strcmp(ef->progtab[i].name, linkset_name) == 0) {
119628730bd3SSimon Schubert 				startp = (Elf_Addr)ef->progtab[i].addr;
119728730bd3SSimon Schubert 				stopp = (Elf_Addr)(startp + ef->progtab[i].size);
119828730bd3SSimon Schubert 				break;
119928730bd3SSimon Schubert 			}
120028730bd3SSimon Schubert 		}
120128730bd3SSimon Schubert 		if (i == ef->nprogtab)
120228730bd3SSimon Schubert 			continue;
120328730bd3SSimon Schubert 
120428730bd3SSimon Schubert 		sym->st_value = start ? startp : stopp;
120528730bd3SSimon Schubert 		sym->st_shndx = i;
120628730bd3SSimon Schubert 	}
120728730bd3SSimon Schubert }
120828730bd3SSimon Schubert 
120928730bd3SSimon Schubert static void
121028730bd3SSimon Schubert link_elf_obj_reloc_local(linker_file_t lf)
121128730bd3SSimon Schubert {
121228730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
121328730bd3SSimon Schubert 	const Elf_Rel *rellim;
121428730bd3SSimon Schubert 	const Elf_Rel *rel;
121528730bd3SSimon Schubert 	const Elf_Rela *relalim;
121628730bd3SSimon Schubert 	const Elf_Rela *rela;
121728730bd3SSimon Schubert 	const Elf_Sym *sym;
121828730bd3SSimon Schubert 	Elf_Addr base;
121928730bd3SSimon Schubert 	int i;
122028730bd3SSimon Schubert 	Elf_Size symidx;
122128730bd3SSimon Schubert 
122228730bd3SSimon Schubert 	link_elf_obj_fix_link_set(ef);
122328730bd3SSimon Schubert 
122428730bd3SSimon Schubert 	/* Perform relocations without addend if there are any: */
122528730bd3SSimon Schubert 	for (i = 0; i < ef->nreltab; i++) {
122628730bd3SSimon Schubert 		rel = ef->reltab[i].rel;
122728730bd3SSimon Schubert 		if (rel == NULL)
122828730bd3SSimon Schubert 			panic("lost a reltab!");
122928730bd3SSimon Schubert 		rellim = rel + ef->reltab[i].nrel;
123028730bd3SSimon Schubert 		base = findbase(ef, ef->reltab[i].sec);
123128730bd3SSimon Schubert 		if (base == 0)
123228730bd3SSimon Schubert 			panic("lost base for reltab");
123328730bd3SSimon Schubert 		for ( ; rel < rellim; rel++) {
123428730bd3SSimon Schubert 			symidx = ELF_R_SYM(rel->r_info);
123528730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
123628730bd3SSimon Schubert 				continue;
123728730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
123828730bd3SSimon Schubert 			/* Only do local relocs */
123928730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) != STB_LOCAL)
124028730bd3SSimon Schubert 				continue;
124128730bd3SSimon Schubert 			elf_reloc_local(lf, base, rel, ELF_RELOC_REL,
124228730bd3SSimon Schubert 			    elf_obj_lookup);
124328730bd3SSimon Schubert 		}
124428730bd3SSimon Schubert 	}
124528730bd3SSimon Schubert 
124628730bd3SSimon Schubert 	/* Perform relocations with addend if there are any: */
124728730bd3SSimon Schubert 	for (i = 0; i < ef->nrelatab; i++) {
124828730bd3SSimon Schubert 		rela = ef->relatab[i].rela;
124928730bd3SSimon Schubert 		if (rela == NULL)
125028730bd3SSimon Schubert 			panic("lost a relatab!");
125128730bd3SSimon Schubert 		relalim = rela + ef->relatab[i].nrela;
125228730bd3SSimon Schubert 		base = findbase(ef, ef->relatab[i].sec);
125328730bd3SSimon Schubert 		if (base == 0)
125428730bd3SSimon Schubert 			panic("lost base for relatab");
125528730bd3SSimon Schubert 		for ( ; rela < relalim; rela++) {
125628730bd3SSimon Schubert 			symidx = ELF_R_SYM(rela->r_info);
125728730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
125828730bd3SSimon Schubert 				continue;
125928730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
126028730bd3SSimon Schubert 			/* Only do local relocs */
126128730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) != STB_LOCAL)
126228730bd3SSimon Schubert 				continue;
126328730bd3SSimon Schubert 			elf_reloc_local(lf, base, rela, ELF_RELOC_RELA,
126428730bd3SSimon Schubert 			    elf_obj_lookup);
126528730bd3SSimon Schubert 		}
126628730bd3SSimon Schubert 	}
126728730bd3SSimon Schubert }
1268