xref: /dragonfly/sys/kern/link_elf_obj.c (revision 135d05a5)
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  */
2928730bd3SSimon Schubert 
3028730bd3SSimon Schubert #include <sys/param.h>
3128730bd3SSimon Schubert #include <sys/kernel.h>
3228730bd3SSimon Schubert #include <sys/systm.h>
3328730bd3SSimon Schubert #include <sys/malloc.h>
3428730bd3SSimon Schubert #include <sys/proc.h>
3528730bd3SSimon Schubert #include <sys/nlookup.h>
3628730bd3SSimon Schubert #include <sys/fcntl.h>
3728730bd3SSimon Schubert #include <sys/vnode.h>
3828730bd3SSimon Schubert #include <sys/linker.h>
3928730bd3SSimon Schubert #include <machine/elf.h>
4028730bd3SSimon Schubert 
4128730bd3SSimon Schubert #include <vm/vm.h>
4228730bd3SSimon Schubert #include <vm/vm_param.h>
4328730bd3SSimon Schubert #include <vm/vm_zone.h>
4428730bd3SSimon Schubert #include <vm/vm_object.h>
4528730bd3SSimon Schubert #include <vm/vm_kern.h>
4628730bd3SSimon Schubert #include <vm/vm_extern.h>
4728730bd3SSimon Schubert #include <sys/lock.h>
4828730bd3SSimon Schubert #include <vm/pmap.h>
4928730bd3SSimon Schubert #include <vm/vm_map.h>
5028730bd3SSimon Schubert 
5128730bd3SSimon Schubert static int	link_elf_obj_preload_file(const char *, linker_file_t *);
5228730bd3SSimon Schubert static int	link_elf_obj_preload_finish(linker_file_t);
5328730bd3SSimon Schubert static int	link_elf_obj_load_file(const char *, linker_file_t *);
5428730bd3SSimon Schubert static int
5528730bd3SSimon Schubert link_elf_obj_lookup_symbol(linker_file_t, const char *,
5628730bd3SSimon Schubert 		       c_linker_sym_t *);
5728730bd3SSimon Schubert static int	link_elf_obj_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t *);
5828730bd3SSimon Schubert static int
5928730bd3SSimon Schubert link_elf_obj_search_symbol(linker_file_t, caddr_t value,
6028730bd3SSimon Schubert 		       c_linker_sym_t * sym, long *diffp);
6128730bd3SSimon Schubert 
6228730bd3SSimon Schubert static void	link_elf_obj_unload_file(linker_file_t);
6328730bd3SSimon Schubert static int
6428730bd3SSimon Schubert link_elf_obj_lookup_set(linker_file_t, const char *,
6528730bd3SSimon Schubert 		    void ***, void ***, int *);
6628730bd3SSimon Schubert static void	link_elf_obj_reloc_local(linker_file_t lf);
6728730bd3SSimon Schubert static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *);
6828730bd3SSimon Schubert 
6928730bd3SSimon Schubert static struct linker_class_ops link_elf_obj_class_ops = {
7028730bd3SSimon Schubert 	link_elf_obj_load_file,
7128730bd3SSimon Schubert 	link_elf_obj_preload_file,
7228730bd3SSimon Schubert };
7328730bd3SSimon Schubert 
7428730bd3SSimon Schubert static struct linker_file_ops link_elf_obj_file_ops = {
7528730bd3SSimon Schubert 	.lookup_symbol = link_elf_obj_lookup_symbol,
7628730bd3SSimon Schubert 	.symbol_values = link_elf_obj_symbol_values,
7728730bd3SSimon Schubert 	.search_symbol = link_elf_obj_search_symbol,
7828730bd3SSimon Schubert 	.preload_finish = link_elf_obj_preload_finish,
7928730bd3SSimon Schubert 	.unload = link_elf_obj_unload_file,
8028730bd3SSimon Schubert 	.lookup_set = link_elf_obj_lookup_set,
8128730bd3SSimon Schubert };
8228730bd3SSimon Schubert 
8328730bd3SSimon Schubert typedef struct {
8428730bd3SSimon Schubert 	void           *addr;
8528730bd3SSimon Schubert 	Elf_Off		size;
8628730bd3SSimon Schubert 	int		flags;
8728730bd3SSimon Schubert 	int		sec;		/* Original section */
8828730bd3SSimon Schubert 	char           *name;
8928730bd3SSimon Schubert }		Elf_progent;
9028730bd3SSimon Schubert 
9128730bd3SSimon Schubert typedef struct {
9228730bd3SSimon Schubert 	Elf_Rel        *rel;
9328730bd3SSimon Schubert 	int		nrel;
9428730bd3SSimon Schubert 	int		sec;
9528730bd3SSimon Schubert }		Elf_relent;
9628730bd3SSimon Schubert 
9728730bd3SSimon Schubert typedef struct {
9828730bd3SSimon Schubert 	Elf_Rela       *rela;
9928730bd3SSimon Schubert 	int		nrela;
10028730bd3SSimon Schubert 	int		sec;
10128730bd3SSimon Schubert }		Elf_relaent;
10228730bd3SSimon Schubert 
10328730bd3SSimon Schubert 
10428730bd3SSimon Schubert typedef struct elf_file {
10528730bd3SSimon Schubert 	int		preloaded;
10628730bd3SSimon Schubert 
10728730bd3SSimon Schubert 	caddr_t		address;	/* Relocation address */
108d63ed24bSMatthew Dillon 	size_t		bytes;		/* Chunk size in bytes */
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
link_elf_obj_init(void * arg)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
link_elf_obj_error(const char * file,const char * s)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
link_elf_obj_preload_file(const char * filename,linker_file_t * result)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;
201d63ed24bSMatthew Dillon 	ef->bytes = 0;
20228730bd3SSimon Schubert 	lf = linker_make_file(filename, ef, &link_elf_obj_file_ops);
20328730bd3SSimon Schubert 	if (lf == NULL) {
20428730bd3SSimon Schubert 		kfree(ef, M_LINKER);
20528730bd3SSimon Schubert 		return ENOMEM;
20628730bd3SSimon Schubert 	}
20728730bd3SSimon Schubert 	lf->address = ef->address;
20828730bd3SSimon Schubert 	lf->size = *(size_t *) sizeptr;
20928730bd3SSimon Schubert 
21028730bd3SSimon Schubert 	if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
21128730bd3SSimon Schubert 	    hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
21228730bd3SSimon Schubert 	    hdr->e_ident[EI_VERSION] != EV_CURRENT ||
21328730bd3SSimon Schubert 	    hdr->e_version != EV_CURRENT ||
21428730bd3SSimon Schubert 	    hdr->e_type != ET_REL ||
21528730bd3SSimon Schubert 	    hdr->e_machine != ELF_TARG_MACH) {
21628730bd3SSimon Schubert 		error = EFTYPE;
21728730bd3SSimon Schubert 		goto out;
21828730bd3SSimon Schubert 	}
21928730bd3SSimon Schubert 	ef->e_shdr = shdr;
22028730bd3SSimon Schubert 
22128730bd3SSimon Schubert 	/* Scan the section header for information and table sizing. */
22228730bd3SSimon Schubert 	symtabindex = -1;
22328730bd3SSimon Schubert 	symstrindex = -1;
22428730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
22528730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
22628730bd3SSimon Schubert 		case SHT_PROGBITS:
22728730bd3SSimon Schubert 		case SHT_NOBITS:
22828730bd3SSimon Schubert 			ef->nprogtab++;
22928730bd3SSimon Schubert 			break;
23028730bd3SSimon Schubert 		case SHT_SYMTAB:
23128730bd3SSimon Schubert 			symtabindex = i;
23228730bd3SSimon Schubert 			symstrindex = shdr[i].sh_link;
23328730bd3SSimon Schubert 			break;
23428730bd3SSimon Schubert 		case SHT_REL:
23528730bd3SSimon Schubert 			ef->nreltab++;
23628730bd3SSimon Schubert 			break;
23728730bd3SSimon Schubert 		case SHT_RELA:
23828730bd3SSimon Schubert 			ef->nrelatab++;
23928730bd3SSimon Schubert 			break;
24028730bd3SSimon Schubert 		}
24128730bd3SSimon Schubert 	}
24228730bd3SSimon Schubert 
24328730bd3SSimon Schubert 	shstrindex = hdr->e_shstrndx;
24428730bd3SSimon Schubert 	if (ef->nprogtab == 0 || symstrindex < 0 ||
24528730bd3SSimon Schubert 	    symstrindex >= hdr->e_shnum ||
24628730bd3SSimon Schubert 	    shdr[symstrindex].sh_type != SHT_STRTAB || shstrindex == 0 ||
24728730bd3SSimon Schubert 	    shstrindex >= hdr->e_shnum ||
24828730bd3SSimon Schubert 	    shdr[shstrindex].sh_type != SHT_STRTAB) {
24928730bd3SSimon Schubert 		error = ENOEXEC;
25028730bd3SSimon Schubert 		goto out;
25128730bd3SSimon Schubert 	}
25228730bd3SSimon Schubert 	/* Allocate space for tracking the load chunks */
25328730bd3SSimon Schubert 	if (ef->nprogtab != 0)
25428730bd3SSimon Schubert 		ef->progtab = kmalloc(ef->nprogtab * sizeof(*ef->progtab),
25528730bd3SSimon Schubert 				     M_LINKER, M_WAITOK | M_ZERO);
25628730bd3SSimon Schubert 	if (ef->nreltab != 0)
25728730bd3SSimon Schubert 		ef->reltab = kmalloc(ef->nreltab * sizeof(*ef->reltab),
25828730bd3SSimon Schubert 				    M_LINKER, M_WAITOK | M_ZERO);
25928730bd3SSimon Schubert 	if (ef->nrelatab != 0)
26028730bd3SSimon Schubert 		ef->relatab = kmalloc(ef->nrelatab * sizeof(*ef->relatab),
26128730bd3SSimon Schubert 				     M_LINKER, M_WAITOK | M_ZERO);
26228730bd3SSimon Schubert 	if ((ef->nprogtab != 0 && ef->progtab == NULL) ||
26328730bd3SSimon Schubert 	    (ef->nreltab != 0 && ef->reltab == NULL) ||
26428730bd3SSimon Schubert 	    (ef->nrelatab != 0 && ef->relatab == NULL)) {
26528730bd3SSimon Schubert 		error = ENOMEM;
26628730bd3SSimon Schubert 		goto out;
26728730bd3SSimon Schubert 	}
26828730bd3SSimon Schubert 	/* XXX, relocate the sh_addr fields saved by the loader. */
26928730bd3SSimon Schubert 	off = 0;
27028730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
27128730bd3SSimon Schubert 		if (shdr[i].sh_addr != 0 && (off == 0 || shdr[i].sh_addr < off))
27228730bd3SSimon Schubert 			off = shdr[i].sh_addr;
27328730bd3SSimon Schubert 	}
27428730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
27528730bd3SSimon Schubert 		if (shdr[i].sh_addr != 0)
27628730bd3SSimon Schubert 			shdr[i].sh_addr = shdr[i].sh_addr - off +
27728730bd3SSimon Schubert 				(Elf_Addr) ef->address;
27828730bd3SSimon Schubert 	}
27928730bd3SSimon Schubert 
28028730bd3SSimon Schubert 	ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
28128730bd3SSimon Schubert 	ef->ddbsymtab = (Elf_Sym *) shdr[symtabindex].sh_addr;
28228730bd3SSimon Schubert 	ef->ddbstrcnt = shdr[symstrindex].sh_size;
28328730bd3SSimon Schubert 	ef->ddbstrtab = (char *)shdr[symstrindex].sh_addr;
28428730bd3SSimon Schubert 	ef->shstrcnt = shdr[shstrindex].sh_size;
28528730bd3SSimon Schubert 	ef->shstrtab = (char *)shdr[shstrindex].sh_addr;
28628730bd3SSimon Schubert 
28728730bd3SSimon Schubert 	/* Now fill out progtab and the relocation tables. */
28828730bd3SSimon Schubert 	pb = 0;
28928730bd3SSimon Schubert 	rl = 0;
29028730bd3SSimon Schubert 	ra = 0;
29128730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
29228730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
29328730bd3SSimon Schubert 		case SHT_PROGBITS:
29428730bd3SSimon Schubert 		case SHT_NOBITS:
29528730bd3SSimon Schubert 			ef->progtab[pb].addr = (void *)shdr[i].sh_addr;
29628730bd3SSimon Schubert 			if (shdr[i].sh_type == SHT_PROGBITS)
29728730bd3SSimon Schubert 				ef->progtab[pb].name = "<<PROGBITS>>";
29828730bd3SSimon Schubert 			else
29928730bd3SSimon Schubert 				ef->progtab[pb].name = "<<NOBITS>>";
30028730bd3SSimon Schubert 			ef->progtab[pb].size = shdr[i].sh_size;
30128730bd3SSimon Schubert 			ef->progtab[pb].sec = i;
30228730bd3SSimon Schubert 			if (ef->shstrtab && shdr[i].sh_name != 0)
30328730bd3SSimon Schubert 				ef->progtab[pb].name =
30428730bd3SSimon Schubert 					ef->shstrtab + shdr[i].sh_name;
30528730bd3SSimon Schubert #if 0
30628730bd3SSimon Schubert 			if (ef->progtab[pb].name != NULL &&
30728730bd3SSimon Schubert 			    !strcmp(ef->progtab[pb].name, "set_pcpu")) {
30828730bd3SSimon Schubert 				void           *dpcpu;
30928730bd3SSimon Schubert 
31028730bd3SSimon Schubert 				dpcpu = dpcpu_alloc(shdr[i].sh_size);
31128730bd3SSimon Schubert 				if (dpcpu == NULL) {
31228730bd3SSimon Schubert 					error = ENOSPC;
31328730bd3SSimon Schubert 					goto out;
31428730bd3SSimon Schubert 				}
31528730bd3SSimon Schubert 				memcpy(dpcpu, ef->progtab[pb].addr,
31628730bd3SSimon Schubert 				       ef->progtab[pb].size);
31728730bd3SSimon Schubert 				dpcpu_copy(dpcpu, shdr[i].sh_size);
31828730bd3SSimon Schubert 				ef->progtab[pb].addr = dpcpu;
31928730bd3SSimon Schubert #ifdef VIMAGE
32028730bd3SSimon Schubert 			} else if (ef->progtab[pb].name != NULL &&
32128730bd3SSimon Schubert 				   !strcmp(ef->progtab[pb].name, VNET_SETNAME)) {
32228730bd3SSimon Schubert 				void           *vnet_data;
32328730bd3SSimon Schubert 
32428730bd3SSimon Schubert 				vnet_data = vnet_data_alloc(shdr[i].sh_size);
32528730bd3SSimon Schubert 				if (vnet_data == NULL) {
32628730bd3SSimon Schubert 					error = ENOSPC;
32728730bd3SSimon Schubert 					goto out;
32828730bd3SSimon Schubert 				}
32928730bd3SSimon Schubert 				memcpy(vnet_data, ef->progtab[pb].addr,
33028730bd3SSimon Schubert 				       ef->progtab[pb].size);
33128730bd3SSimon Schubert 				vnet_data_copy(vnet_data, shdr[i].sh_size);
33228730bd3SSimon Schubert 				ef->progtab[pb].addr = vnet_data;
33328730bd3SSimon Schubert #endif
33428730bd3SSimon Schubert 			}
33528730bd3SSimon Schubert #endif
33628730bd3SSimon Schubert 			/* Update all symbol values with the offset. */
33728730bd3SSimon Schubert 			for (j = 0; j < ef->ddbsymcnt; j++) {
33828730bd3SSimon Schubert 				es = &ef->ddbsymtab[j];
33928730bd3SSimon Schubert 				if (es->st_shndx != i)
34028730bd3SSimon Schubert 					continue;
34128730bd3SSimon Schubert 				es->st_value += (Elf_Addr) ef->progtab[pb].addr;
34228730bd3SSimon Schubert 			}
34328730bd3SSimon Schubert 			pb++;
34428730bd3SSimon Schubert 			break;
34528730bd3SSimon Schubert 		case SHT_REL:
34628730bd3SSimon Schubert 			ef->reltab[rl].rel = (Elf_Rel *) shdr[i].sh_addr;
34728730bd3SSimon Schubert 			ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
34828730bd3SSimon Schubert 			ef->reltab[rl].sec = shdr[i].sh_info;
34928730bd3SSimon Schubert 			rl++;
35028730bd3SSimon Schubert 			break;
35128730bd3SSimon Schubert 		case SHT_RELA:
35228730bd3SSimon Schubert 			ef->relatab[ra].rela = (Elf_Rela *) shdr[i].sh_addr;
35328730bd3SSimon Schubert 			ef->relatab[ra].nrela =
35428730bd3SSimon Schubert 				shdr[i].sh_size / sizeof(Elf_Rela);
35528730bd3SSimon Schubert 			ef->relatab[ra].sec = shdr[i].sh_info;
35628730bd3SSimon Schubert 			ra++;
35728730bd3SSimon Schubert 			break;
35828730bd3SSimon Schubert 		}
35928730bd3SSimon Schubert 	}
36028730bd3SSimon Schubert 	if (pb != ef->nprogtab)
36128730bd3SSimon Schubert 		panic("lost progbits");
36228730bd3SSimon Schubert 	if (rl != ef->nreltab)
36328730bd3SSimon Schubert 		panic("lost reltab");
36428730bd3SSimon Schubert 	if (ra != ef->nrelatab)
36528730bd3SSimon Schubert 		panic("lost relatab");
36628730bd3SSimon Schubert 
36728730bd3SSimon Schubert 	/* Local intra-module relocations */
36828730bd3SSimon Schubert 	link_elf_obj_reloc_local(lf);
36928730bd3SSimon Schubert 
37028730bd3SSimon Schubert 	*result = lf;
37128730bd3SSimon Schubert 	return (0);
37228730bd3SSimon Schubert 
37328730bd3SSimon Schubert out:
37428730bd3SSimon Schubert 	/* preload not done this way */
37528730bd3SSimon Schubert 	linker_file_unload(lf /* , LINKER_UNLOAD_FORCE */ );
37628730bd3SSimon Schubert 	return (error);
37728730bd3SSimon Schubert }
37828730bd3SSimon Schubert 
37928730bd3SSimon Schubert static int
link_elf_obj_preload_finish(linker_file_t lf)38028730bd3SSimon Schubert link_elf_obj_preload_finish(linker_file_t lf)
38128730bd3SSimon Schubert {
38228730bd3SSimon Schubert 	int error;
38328730bd3SSimon Schubert 
38428730bd3SSimon Schubert 	error = relocate_file(lf);
38528730bd3SSimon Schubert 
38628730bd3SSimon Schubert 	return (error);
38728730bd3SSimon Schubert }
38828730bd3SSimon Schubert 
38928730bd3SSimon Schubert static int
link_elf_obj_load_file(const char * filename,linker_file_t * result)39028730bd3SSimon Schubert link_elf_obj_load_file(const char *filename, linker_file_t * result)
39128730bd3SSimon Schubert {
39228730bd3SSimon Schubert 	struct nlookupdata nd;
39328730bd3SSimon Schubert 	struct thread  *td = curthread;	/* XXX */
39428730bd3SSimon Schubert 	struct proc    *p = td->td_proc;
39528730bd3SSimon Schubert 	char           *pathname;
39628730bd3SSimon Schubert 	struct vnode   *vp;
39728730bd3SSimon Schubert 	Elf_Ehdr       *hdr;
39828730bd3SSimon Schubert 	Elf_Shdr       *shdr;
39928730bd3SSimon Schubert 	Elf_Sym        *es;
40028730bd3SSimon Schubert 	int		nbytes, i, j;
40128730bd3SSimon Schubert 	vm_offset_t	mapbase;
40228730bd3SSimon Schubert 	size_t		mapsize;
40328730bd3SSimon Schubert 	int		error = 0;
40428730bd3SSimon Schubert 	int		resid;
40528730bd3SSimon Schubert 	elf_file_t	ef;
40628730bd3SSimon Schubert 	linker_file_t	lf;
40728730bd3SSimon Schubert 	int		symtabindex;
40828730bd3SSimon Schubert 	int		symstrindex;
40928730bd3SSimon Schubert 	int		shstrindex;
41028730bd3SSimon Schubert 	int		nsym;
41128730bd3SSimon Schubert 	int		pb, rl, ra;
41228730bd3SSimon Schubert 	int		alignmask;
41328730bd3SSimon Schubert 
41425dac377SJoe Talbott 	/* XXX Hack for firmware loading where p == NULL */
41525dac377SJoe Talbott 	if (p == NULL) {
41625dac377SJoe Talbott 		p = &proc0;
41725dac377SJoe Talbott 	}
41825dac377SJoe Talbott 
41928730bd3SSimon Schubert 	KKASSERT(p != NULL);
42028730bd3SSimon Schubert 	if (p->p_ucred == NULL) {
42128730bd3SSimon Schubert 		kprintf("link_elf_obj_load_file: cannot load '%s' from filesystem"
42228730bd3SSimon Schubert 			" this early\n", filename);
42328730bd3SSimon Schubert 		return ENOENT;
42428730bd3SSimon Schubert 	}
42528730bd3SSimon Schubert 	shdr = NULL;
42628730bd3SSimon Schubert 	lf = NULL;
42728730bd3SSimon Schubert 	mapsize = 0;
42828730bd3SSimon Schubert 	hdr = NULL;
42928730bd3SSimon Schubert 	pathname = linker_search_path(filename);
43028730bd3SSimon Schubert 	if (pathname == NULL)
43128730bd3SSimon Schubert 		return ENOENT;
43228730bd3SSimon Schubert 
43328730bd3SSimon Schubert 	error = nlookup_init(&nd, pathname, UIO_SYSSPACE, NLC_FOLLOW | NLC_LOCKVP);
43428730bd3SSimon Schubert 	if (error == 0)
43528730bd3SSimon Schubert 		error = vn_open(&nd, NULL, FREAD, 0);
43628730bd3SSimon Schubert 	kfree(pathname, M_LINKER);
43728730bd3SSimon Schubert 	if (error) {
43828730bd3SSimon Schubert 		nlookup_done(&nd);
43928730bd3SSimon Schubert 		return error;
44028730bd3SSimon Schubert 	}
44128730bd3SSimon Schubert 	vp = nd.nl_open_vp;
44228730bd3SSimon Schubert 	nd.nl_open_vp = NULL;
44328730bd3SSimon Schubert 	nlookup_done(&nd);
44428730bd3SSimon Schubert 
44528730bd3SSimon Schubert 	/*
44628730bd3SSimon Schubert 	 * Read the elf header from the file.
44728730bd3SSimon Schubert 	 */
44828730bd3SSimon Schubert 	hdr = kmalloc(sizeof(*hdr), M_LINKER, M_WAITOK);
44928730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, (void *)hdr, sizeof(*hdr), 0,
45028730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
45128730bd3SSimon Schubert 	if (error)
45228730bd3SSimon Schubert 		goto out;
45328730bd3SSimon Schubert 	if (resid != 0) {
45428730bd3SSimon Schubert 		error = ENOEXEC;
45528730bd3SSimon Schubert 		goto out;
45628730bd3SSimon Schubert 	}
45728730bd3SSimon Schubert 	if (!IS_ELF(*hdr)) {
45828730bd3SSimon Schubert 		error = ENOEXEC;
45928730bd3SSimon Schubert 		goto out;
46028730bd3SSimon Schubert 	}
46128730bd3SSimon Schubert 
46228730bd3SSimon Schubert 	if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
46328730bd3SSimon Schubert 	    || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
46428730bd3SSimon Schubert 		link_elf_obj_error(filename, "Unsupported file layout");
46528730bd3SSimon Schubert 		error = ENOEXEC;
46628730bd3SSimon Schubert 		goto out;
46728730bd3SSimon Schubert 	}
46828730bd3SSimon Schubert 	if (hdr->e_ident[EI_VERSION] != EV_CURRENT
46928730bd3SSimon Schubert 	    || hdr->e_version != EV_CURRENT) {
47028730bd3SSimon Schubert 		link_elf_obj_error(filename, "Unsupported file version");
47128730bd3SSimon Schubert 		error = ENOEXEC;
47228730bd3SSimon Schubert 		goto out;
47328730bd3SSimon Schubert 	}
47428730bd3SSimon Schubert 	if (hdr->e_type != ET_REL) {
47528730bd3SSimon Schubert 		error = ENOSYS;
47628730bd3SSimon Schubert 		goto out;
47728730bd3SSimon Schubert 	}
47828730bd3SSimon Schubert 	if (hdr->e_machine != ELF_TARG_MACH) {
47928730bd3SSimon Schubert 		link_elf_obj_error(filename, "Unsupported machine");
48028730bd3SSimon Schubert 		error = ENOEXEC;
48128730bd3SSimon Schubert 		goto out;
48228730bd3SSimon Schubert 	}
48328730bd3SSimon Schubert 
48428730bd3SSimon Schubert 	ef = kmalloc(sizeof(struct elf_file), M_LINKER, M_WAITOK | M_ZERO);
48528730bd3SSimon Schubert 	lf = linker_make_file(filename, ef, &link_elf_obj_file_ops);
48628730bd3SSimon Schubert 	if (lf == NULL) {
48728730bd3SSimon Schubert 		kfree(ef, M_LINKER);
48828730bd3SSimon Schubert 		error = ENOMEM;
48928730bd3SSimon Schubert 		goto out;
49028730bd3SSimon Schubert 	}
49128730bd3SSimon Schubert 	ef->nprogtab = 0;
4924090d6ffSSascha Wildner 	ef->e_shdr = NULL;
49328730bd3SSimon Schubert 	ef->nreltab = 0;
49428730bd3SSimon Schubert 	ef->nrelatab = 0;
49528730bd3SSimon Schubert 
49628730bd3SSimon Schubert 	/* Allocate and read in the section header */
49728730bd3SSimon Schubert 	nbytes = hdr->e_shnum * hdr->e_shentsize;
49828730bd3SSimon Schubert 	if (nbytes == 0 || hdr->e_shoff == 0 ||
49928730bd3SSimon Schubert 	    hdr->e_shentsize != sizeof(Elf_Shdr)) {
50028730bd3SSimon Schubert 		error = ENOEXEC;
50128730bd3SSimon Schubert 		goto out;
50228730bd3SSimon Schubert 	}
50328730bd3SSimon Schubert 	shdr = kmalloc(nbytes, M_LINKER, M_WAITOK);
50428730bd3SSimon Schubert 	ef->e_shdr = shdr;
50528730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, (caddr_t) shdr, nbytes, hdr->e_shoff,
50628730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
50728730bd3SSimon Schubert 	if (error)
50828730bd3SSimon Schubert 		goto out;
50928730bd3SSimon Schubert 	if (resid) {
51028730bd3SSimon Schubert 		error = ENOEXEC;
51128730bd3SSimon Schubert 		goto out;
51228730bd3SSimon Schubert 	}
51328730bd3SSimon Schubert 	/* Scan the section header for information and table sizing. */
51428730bd3SSimon Schubert 	nsym = 0;
51528730bd3SSimon Schubert 	symtabindex = -1;
51628730bd3SSimon Schubert 	symstrindex = -1;
51728730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
51896e935d2SJohn Marino 		if (shdr[i].sh_size == 0)
51996e935d2SJohn Marino 			continue;
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 	error = vn_rdwr(UIO_READ, vp, (void *)ef->ddbsymtab,
57928730bd3SSimon Schubert 			shdr[symtabindex].sh_size, shdr[symtabindex].sh_offset,
58028730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
58128730bd3SSimon Schubert 	if (error)
58228730bd3SSimon Schubert 		goto out;
58328730bd3SSimon Schubert 	if (resid != 0) {
58428730bd3SSimon Schubert 		error = EINVAL;
58528730bd3SSimon Schubert 		goto out;
58628730bd3SSimon Schubert 	}
58728730bd3SSimon Schubert 	if (symstrindex == -1)
58828730bd3SSimon Schubert 		panic("lost symbol string index");
58928730bd3SSimon Schubert 	/* Allocate space for and load the symbol strings */
59028730bd3SSimon Schubert 	ef->ddbstrcnt = shdr[symstrindex].sh_size;
59128730bd3SSimon Schubert 	ef->ddbstrtab = kmalloc(shdr[symstrindex].sh_size, M_LINKER, M_WAITOK);
59228730bd3SSimon Schubert 	error = vn_rdwr(UIO_READ, vp, ef->ddbstrtab,
59328730bd3SSimon Schubert 			shdr[symstrindex].sh_size, shdr[symstrindex].sh_offset,
59428730bd3SSimon Schubert 			UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
59528730bd3SSimon Schubert 	if (error)
59628730bd3SSimon Schubert 		goto out;
59728730bd3SSimon Schubert 	if (resid != 0) {
59828730bd3SSimon Schubert 		error = EINVAL;
59928730bd3SSimon Schubert 		goto out;
60028730bd3SSimon Schubert 	}
60128730bd3SSimon Schubert 	/* Do we have a string table for the section names?  */
60228730bd3SSimon Schubert 	shstrindex = -1;
60328730bd3SSimon Schubert 	if (hdr->e_shstrndx != 0 &&
60428730bd3SSimon Schubert 	    shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
60528730bd3SSimon Schubert 		shstrindex = hdr->e_shstrndx;
60628730bd3SSimon Schubert 		ef->shstrcnt = shdr[shstrindex].sh_size;
60728730bd3SSimon Schubert 		ef->shstrtab = kmalloc(shdr[shstrindex].sh_size, M_LINKER,
60828730bd3SSimon Schubert 				       M_WAITOK);
60928730bd3SSimon Schubert 		error = vn_rdwr(UIO_READ, vp, ef->shstrtab,
61028730bd3SSimon Schubert 				shdr[shstrindex].sh_size, shdr[shstrindex].sh_offset,
61128730bd3SSimon Schubert 				UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
61228730bd3SSimon Schubert 		if (error)
61328730bd3SSimon Schubert 			goto out;
61428730bd3SSimon Schubert 		if (resid != 0) {
61528730bd3SSimon Schubert 			error = EINVAL;
61628730bd3SSimon Schubert 			goto out;
61728730bd3SSimon Schubert 		}
61828730bd3SSimon Schubert 	}
61928730bd3SSimon Schubert 	/* Size up code/data(progbits) and bss(nobits). */
62028730bd3SSimon Schubert 	alignmask = 0;
62128730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
62296e935d2SJohn Marino 		if (shdr[i].sh_size == 0)
62396e935d2SJohn Marino 			continue;
62428730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
62528730bd3SSimon Schubert 		case SHT_PROGBITS:
62628730bd3SSimon Schubert 		case SHT_NOBITS:
62728730bd3SSimon Schubert 			alignmask = shdr[i].sh_addralign - 1;
62828730bd3SSimon Schubert 			mapsize += alignmask;
62928730bd3SSimon Schubert 			mapsize &= ~alignmask;
63028730bd3SSimon Schubert 			mapsize += shdr[i].sh_size;
63128730bd3SSimon Schubert 			break;
63228730bd3SSimon Schubert 		}
63328730bd3SSimon Schubert 	}
63428730bd3SSimon Schubert 
63528730bd3SSimon Schubert 	/*
63628730bd3SSimon Schubert 	 * We know how much space we need for the text/data/bss/etc. This
63728730bd3SSimon Schubert 	 * stuff needs to be in a single chunk so that profiling etc can get
63828730bd3SSimon Schubert 	 * the bounds and gdb can associate offsets with modules
63928730bd3SSimon Schubert 	 */
64028730bd3SSimon Schubert 	ef->object = vm_object_allocate(OBJT_DEFAULT,
64128730bd3SSimon Schubert 					round_page(mapsize) >> PAGE_SHIFT);
64228730bd3SSimon Schubert 	if (ef->object == NULL) {
64328730bd3SSimon Schubert 		error = ENOMEM;
64428730bd3SSimon Schubert 		goto out;
64528730bd3SSimon Schubert 	}
646b12defdcSMatthew Dillon 	vm_object_hold(ef->object);
647b12defdcSMatthew Dillon 	vm_object_reference_locked(ef->object);
6481eeaf6b2SAaron LI 	ef->address = (caddr_t) vm_map_min(kernel_map);
649d63ed24bSMatthew Dillon 	ef->bytes = 0;
65028730bd3SSimon Schubert 
65128730bd3SSimon Schubert 	/*
652b2b3ffcdSSimon Schubert 	 * In order to satisfy x86_64's architectural requirements on the
65328730bd3SSimon Schubert 	 * location of code and data in the kernel's address space, request a
65428730bd3SSimon Schubert 	 * mapping that is above the kernel.
655d63ed24bSMatthew Dillon 	 *
656d63ed24bSMatthew Dillon 	 * vkernel64's text+data is outside the managed VM space entirely.
65728730bd3SSimon Schubert 	 */
658dbefba87SSascha Wildner #if defined(__x86_64__) && defined(_KERNEL_VIRTUAL)
659d63ed24bSMatthew Dillon 	error = vkernel_module_memory_alloc(&mapbase, round_page(mapsize));
660086a8efcSMatthew Dillon 	vm_object_drop(ef->object);
661d63ed24bSMatthew Dillon #else
66228730bd3SSimon Schubert 	mapbase = KERNBASE;
6631eeaf6b2SAaron LI 	error = vm_map_find(kernel_map, ef->object, NULL,
6640adbcbd6SMatthew Dillon 			    0, &mapbase, round_page(mapsize),
6653091de50SMatthew Dillon 			    PAGE_SIZE, TRUE,
6663091de50SMatthew Dillon 			    VM_MAPTYPE_NORMAL, VM_SUBSYS_IMGACT,
66728730bd3SSimon Schubert 			    VM_PROT_ALL, VM_PROT_ALL, FALSE);
668b12defdcSMatthew Dillon 	vm_object_drop(ef->object);
66928730bd3SSimon Schubert 	if (error) {
67028730bd3SSimon Schubert 		vm_object_deallocate(ef->object);
671d63ed24bSMatthew Dillon 		ef->object = NULL;
67228730bd3SSimon Schubert 		goto out;
67328730bd3SSimon Schubert 	}
674949c56f8SMatthew Dillon 
67528730bd3SSimon Schubert 	/* Wire the pages */
676949c56f8SMatthew Dillon 	error = vm_map_kernel_wiring(kernel_map, mapbase,
67728730bd3SSimon Schubert 				     mapbase + round_page(mapsize), 0);
678d63ed24bSMatthew Dillon #endif
67928730bd3SSimon Schubert 	if (error != KERN_SUCCESS) {
68028730bd3SSimon Schubert 		error = ENOMEM;
68128730bd3SSimon Schubert 		goto out;
68228730bd3SSimon Schubert 	}
68328730bd3SSimon Schubert 	/* Inform the kld system about the situation */
68428730bd3SSimon Schubert 	lf->address = ef->address = (caddr_t) mapbase;
685d63ed24bSMatthew Dillon 	lf->size = round_page(mapsize);
686d63ed24bSMatthew Dillon 	ef->bytes = mapsize;
68728730bd3SSimon Schubert 
68828730bd3SSimon Schubert 	/*
68928730bd3SSimon Schubert 	 * Now load code/data(progbits), zero bss(nobits), allocate space for
69028730bd3SSimon Schubert 	 * and load relocs
69128730bd3SSimon Schubert 	 */
69228730bd3SSimon Schubert 	pb = 0;
69328730bd3SSimon Schubert 	rl = 0;
69428730bd3SSimon Schubert 	ra = 0;
69528730bd3SSimon Schubert 	alignmask = 0;
69628730bd3SSimon Schubert 	for (i = 0; i < hdr->e_shnum; i++) {
69796e935d2SJohn Marino 		if (shdr[i].sh_size == 0)
69896e935d2SJohn Marino 			continue;
69928730bd3SSimon Schubert 		switch (shdr[i].sh_type) {
70028730bd3SSimon Schubert 		case SHT_PROGBITS:
70128730bd3SSimon Schubert 		case SHT_NOBITS:
7024cd87d98SJohn Marino 			alignmask = shdr[i].sh_addralign - 1;
70328730bd3SSimon Schubert 			mapbase += alignmask;
70428730bd3SSimon Schubert 			mapbase &= ~alignmask;
70528730bd3SSimon Schubert 			if (ef->shstrtab && shdr[i].sh_name != 0)
70628730bd3SSimon Schubert 				ef->progtab[pb].name =
70728730bd3SSimon Schubert 					ef->shstrtab + shdr[i].sh_name;
70828730bd3SSimon Schubert 			else if (shdr[i].sh_type == SHT_PROGBITS)
70928730bd3SSimon Schubert 				ef->progtab[pb].name = "<<PROGBITS>>";
71028730bd3SSimon Schubert 			else
71128730bd3SSimon Schubert 				ef->progtab[pb].name = "<<NOBITS>>";
71228730bd3SSimon Schubert #if 0
71328730bd3SSimon Schubert 			if (ef->progtab[pb].name != NULL &&
71428730bd3SSimon Schubert 			    !strcmp(ef->progtab[pb].name, "set_pcpu"))
71528730bd3SSimon Schubert 				ef->progtab[pb].addr =
71628730bd3SSimon Schubert 					dpcpu_alloc(shdr[i].sh_size);
71728730bd3SSimon Schubert #ifdef VIMAGE
71828730bd3SSimon Schubert 			else if (ef->progtab[pb].name != NULL &&
71928730bd3SSimon Schubert 				 !strcmp(ef->progtab[pb].name, VNET_SETNAME))
72028730bd3SSimon Schubert 				ef->progtab[pb].addr =
72128730bd3SSimon Schubert 					vnet_data_alloc(shdr[i].sh_size);
72228730bd3SSimon Schubert #endif
72328730bd3SSimon Schubert 			else
72428730bd3SSimon Schubert #endif
72528730bd3SSimon Schubert 				ef->progtab[pb].addr =
72628730bd3SSimon Schubert 					(void *)(uintptr_t) mapbase;
72728730bd3SSimon Schubert 			if (ef->progtab[pb].addr == NULL) {
72828730bd3SSimon Schubert 				error = ENOSPC;
72928730bd3SSimon Schubert 				goto out;
73028730bd3SSimon Schubert 			}
73128730bd3SSimon Schubert 			ef->progtab[pb].size = shdr[i].sh_size;
73228730bd3SSimon Schubert 			ef->progtab[pb].sec = i;
73328730bd3SSimon Schubert 			if (shdr[i].sh_type == SHT_PROGBITS) {
73428730bd3SSimon Schubert 				error = vn_rdwr(UIO_READ, vp,
73528730bd3SSimon Schubert 						ef->progtab[pb].addr,
73628730bd3SSimon Schubert 						shdr[i].sh_size, shdr[i].sh_offset,
73728730bd3SSimon Schubert 						UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred,
73828730bd3SSimon Schubert 						&resid);
73928730bd3SSimon Schubert 				if (error)
74028730bd3SSimon Schubert 					goto out;
74128730bd3SSimon Schubert 				if (resid != 0) {
74228730bd3SSimon Schubert 					error = EINVAL;
74328730bd3SSimon Schubert 					goto out;
74428730bd3SSimon Schubert 				}
74528730bd3SSimon Schubert #if 0
74628730bd3SSimon Schubert 				/* Initialize the per-cpu or vnet area. */
74728730bd3SSimon Schubert 				if (ef->progtab[pb].addr != (void *)mapbase &&
74828730bd3SSimon Schubert 				    !strcmp(ef->progtab[pb].name, "set_pcpu"))
74928730bd3SSimon Schubert 					dpcpu_copy(ef->progtab[pb].addr,
75028730bd3SSimon Schubert 						   shdr[i].sh_size);
75128730bd3SSimon Schubert #ifdef VIMAGE
75228730bd3SSimon Schubert 				else if (ef->progtab[pb].addr !=
75328730bd3SSimon Schubert 					 (void *)mapbase &&
75428730bd3SSimon Schubert 					 !strcmp(ef->progtab[pb].name, VNET_SETNAME))
75528730bd3SSimon Schubert 					vnet_data_copy(ef->progtab[pb].addr,
75628730bd3SSimon Schubert 						       shdr[i].sh_size);
75728730bd3SSimon Schubert #endif
75828730bd3SSimon Schubert #endif
75928730bd3SSimon Schubert 			} else
76028730bd3SSimon Schubert 				bzero(ef->progtab[pb].addr, shdr[i].sh_size);
76128730bd3SSimon Schubert 
76228730bd3SSimon Schubert 			/* Update all symbol values with the offset. */
76328730bd3SSimon Schubert 			for (j = 0; j < ef->ddbsymcnt; j++) {
76428730bd3SSimon Schubert 				es = &ef->ddbsymtab[j];
76528730bd3SSimon Schubert 				if (es->st_shndx != i)
76628730bd3SSimon Schubert 					continue;
76728730bd3SSimon Schubert 				es->st_value += (Elf_Addr) ef->progtab[pb].addr;
76828730bd3SSimon Schubert 			}
76928730bd3SSimon Schubert 			mapbase += shdr[i].sh_size;
77028730bd3SSimon Schubert 			pb++;
77128730bd3SSimon Schubert 			break;
77228730bd3SSimon Schubert 		case SHT_REL:
77328730bd3SSimon Schubert 			ef->reltab[rl].rel = kmalloc(shdr[i].sh_size, M_LINKER, M_WAITOK);
77428730bd3SSimon Schubert 			ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
77528730bd3SSimon Schubert 			ef->reltab[rl].sec = shdr[i].sh_info;
77628730bd3SSimon Schubert 			error = vn_rdwr(UIO_READ, vp,
77728730bd3SSimon Schubert 					(void *)ef->reltab[rl].rel,
77828730bd3SSimon Schubert 					shdr[i].sh_size, shdr[i].sh_offset,
77928730bd3SSimon Schubert 					UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
78028730bd3SSimon Schubert 			if (error)
78128730bd3SSimon Schubert 				goto out;
78228730bd3SSimon Schubert 			if (resid != 0) {
78328730bd3SSimon Schubert 				error = EINVAL;
78428730bd3SSimon Schubert 				goto out;
78528730bd3SSimon Schubert 			}
78628730bd3SSimon Schubert 			rl++;
78728730bd3SSimon Schubert 			break;
78828730bd3SSimon Schubert 		case SHT_RELA:
78928730bd3SSimon Schubert 			ef->relatab[ra].rela = kmalloc(shdr[i].sh_size, M_LINKER, M_WAITOK);
79028730bd3SSimon Schubert 			ef->relatab[ra].nrela = shdr[i].sh_size / sizeof(Elf_Rela);
79128730bd3SSimon Schubert 			ef->relatab[ra].sec = shdr[i].sh_info;
79228730bd3SSimon Schubert 			error = vn_rdwr(UIO_READ, vp,
79328730bd3SSimon Schubert 					(void *)ef->relatab[ra].rela,
79428730bd3SSimon Schubert 					shdr[i].sh_size, shdr[i].sh_offset,
79528730bd3SSimon Schubert 					UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid);
79628730bd3SSimon Schubert 			if (error)
79728730bd3SSimon Schubert 				goto out;
79828730bd3SSimon Schubert 			if (resid != 0) {
79928730bd3SSimon Schubert 				error = EINVAL;
80028730bd3SSimon Schubert 				goto out;
80128730bd3SSimon Schubert 			}
80228730bd3SSimon Schubert 			ra++;
80328730bd3SSimon Schubert 			break;
80428730bd3SSimon Schubert 		}
80528730bd3SSimon Schubert 	}
80628730bd3SSimon Schubert 	if (pb != ef->nprogtab)
80728730bd3SSimon Schubert 		panic("lost progbits");
80828730bd3SSimon Schubert 	if (rl != ef->nreltab)
80928730bd3SSimon Schubert 		panic("lost reltab");
81028730bd3SSimon Schubert 	if (ra != ef->nrelatab)
81128730bd3SSimon Schubert 		panic("lost relatab");
81228730bd3SSimon Schubert 	if (mapbase != (vm_offset_t) ef->address + mapsize)
813ed20d0e3SSascha Wildner 		panic("mapbase 0x%lx != address %p + mapsize 0x%lx (0x%lx)",
81428730bd3SSimon Schubert 		      mapbase, ef->address, mapsize,
81528730bd3SSimon Schubert 		      (vm_offset_t) ef->address + mapsize);
81628730bd3SSimon Schubert 
81728730bd3SSimon Schubert 	/* Local intra-module relocations */
81828730bd3SSimon Schubert 	link_elf_obj_reloc_local(lf);
81928730bd3SSimon Schubert 
82028730bd3SSimon Schubert 	/* Pull in dependencies */
82128730bd3SSimon Schubert 	error = linker_load_dependencies(lf);
82228730bd3SSimon Schubert 	if (error)
82328730bd3SSimon Schubert 		goto out;
82428730bd3SSimon Schubert 
82528730bd3SSimon Schubert 	/* External relocations */
82628730bd3SSimon Schubert 	error = relocate_file(lf);
82728730bd3SSimon Schubert 	if (error)
82828730bd3SSimon Schubert 		goto out;
82928730bd3SSimon Schubert 
83028730bd3SSimon Schubert 	*result = lf;
83128730bd3SSimon Schubert 
83228730bd3SSimon Schubert out:
83328730bd3SSimon Schubert 	if (error && lf)
83428730bd3SSimon Schubert 		linker_file_unload(lf /*, LINKER_UNLOAD_FORCE */);
83528730bd3SSimon Schubert 	if (hdr)
83628730bd3SSimon Schubert 		kfree(hdr, M_LINKER);
83728730bd3SSimon Schubert 	vn_unlock(vp);
8383596743eSMarkus Pfeiffer 	vn_close(vp, FREAD, NULL);
83928730bd3SSimon Schubert 
84028730bd3SSimon Schubert 	return error;
84128730bd3SSimon Schubert }
84228730bd3SSimon Schubert 
84328730bd3SSimon Schubert static void
link_elf_obj_unload_file(linker_file_t file)84428730bd3SSimon Schubert link_elf_obj_unload_file(linker_file_t file)
84528730bd3SSimon Schubert {
84628730bd3SSimon Schubert 	elf_file_t	ef = file->priv;
84728730bd3SSimon Schubert 	int i;
84828730bd3SSimon Schubert 
84928730bd3SSimon Schubert 	if (ef->progtab) {
85028730bd3SSimon Schubert 		for (i = 0; i < ef->nprogtab; i++) {
85128730bd3SSimon Schubert 			if (ef->progtab[i].size == 0)
85228730bd3SSimon Schubert 				continue;
85328730bd3SSimon Schubert 			if (ef->progtab[i].name == NULL)
85428730bd3SSimon Schubert 				continue;
85528730bd3SSimon Schubert #if 0
85628730bd3SSimon Schubert 			if (!strcmp(ef->progtab[i].name, "set_pcpu"))
85728730bd3SSimon Schubert 				dpcpu_free(ef->progtab[i].addr,
85828730bd3SSimon Schubert 				    ef->progtab[i].size);
85928730bd3SSimon Schubert #ifdef VIMAGE
86028730bd3SSimon Schubert 			else if (!strcmp(ef->progtab[i].name, VNET_SETNAME))
86128730bd3SSimon Schubert 				vnet_data_free(ef->progtab[i].addr,
86228730bd3SSimon Schubert 				    ef->progtab[i].size);
86328730bd3SSimon Schubert #endif
86428730bd3SSimon Schubert #endif
86528730bd3SSimon Schubert 		}
86628730bd3SSimon Schubert 	}
86728730bd3SSimon Schubert 	if (ef->preloaded) {
86828730bd3SSimon Schubert 		if (ef->reltab)
86928730bd3SSimon Schubert 			kfree(ef->reltab, M_LINKER);
87028730bd3SSimon Schubert 		if (ef->relatab)
87128730bd3SSimon Schubert 			kfree(ef->relatab, M_LINKER);
87228730bd3SSimon Schubert 		if (ef->progtab)
87328730bd3SSimon Schubert 			kfree(ef->progtab, M_LINKER);
87428730bd3SSimon Schubert 		if (ef->ctftab)
87528730bd3SSimon Schubert 			kfree(ef->ctftab, M_LINKER);
87628730bd3SSimon Schubert 		if (ef->ctfoff)
87728730bd3SSimon Schubert 			kfree(ef->ctfoff, M_LINKER);
87828730bd3SSimon Schubert 		if (ef->typoff)
87928730bd3SSimon Schubert 			kfree(ef->typoff, M_LINKER);
880*135d05a5SAaron LI 		if (file->pathname != NULL)
881*135d05a5SAaron LI 			preload_delete_name(file->pathname);
88228730bd3SSimon Schubert 		kfree(ef, M_LINKER);
88328730bd3SSimon Schubert 		/* XXX reclaim module memory? */
88428730bd3SSimon Schubert 		return;
88528730bd3SSimon Schubert 	}
88628730bd3SSimon Schubert 
88728730bd3SSimon Schubert 	for (i = 0; i < ef->nreltab; i++)
88828730bd3SSimon Schubert 		if (ef->reltab[i].rel)
88928730bd3SSimon Schubert 			kfree(ef->reltab[i].rel, M_LINKER);
89028730bd3SSimon Schubert 	for (i = 0; i < ef->nrelatab; i++)
89128730bd3SSimon Schubert 		if (ef->relatab[i].rela)
89228730bd3SSimon Schubert 			kfree(ef->relatab[i].rela, M_LINKER);
89328730bd3SSimon Schubert 	if (ef->reltab)
89428730bd3SSimon Schubert 		kfree(ef->reltab, M_LINKER);
89528730bd3SSimon Schubert 	if (ef->relatab)
89628730bd3SSimon Schubert 		kfree(ef->relatab, M_LINKER);
89728730bd3SSimon Schubert 	if (ef->progtab)
89828730bd3SSimon Schubert 		kfree(ef->progtab, M_LINKER);
89928730bd3SSimon Schubert 
90028730bd3SSimon Schubert 	if (ef->object) {
901dbefba87SSascha Wildner #if defined(__x86_64__) && defined(_KERNEL_VIRTUAL)
902d63ed24bSMatthew Dillon 		vkernel_module_memory_free((vm_offset_t)ef->address, ef->bytes);
903d63ed24bSMatthew Dillon #else
9041eeaf6b2SAaron LI 		vm_map_remove(kernel_map, (vm_offset_t) ef->address,
90528730bd3SSimon Schubert 		    (vm_offset_t) ef->address +
90628730bd3SSimon Schubert 		    (ef->object->size << PAGE_SHIFT));
907d63ed24bSMatthew Dillon #endif
908d63ed24bSMatthew Dillon 		vm_object_deallocate(ef->object);
909d63ed24bSMatthew Dillon 		ef->object = NULL;
91028730bd3SSimon Schubert 	}
91128730bd3SSimon Schubert 	if (ef->e_shdr)
91228730bd3SSimon Schubert 		kfree(ef->e_shdr, M_LINKER);
91328730bd3SSimon Schubert 	if (ef->ddbsymtab)
91428730bd3SSimon Schubert 		kfree(ef->ddbsymtab, M_LINKER);
91528730bd3SSimon Schubert 	if (ef->ddbstrtab)
91628730bd3SSimon Schubert 		kfree(ef->ddbstrtab, M_LINKER);
91728730bd3SSimon Schubert 	if (ef->shstrtab)
91828730bd3SSimon Schubert 		kfree(ef->shstrtab, M_LINKER);
91928730bd3SSimon Schubert 	if (ef->ctftab)
92028730bd3SSimon Schubert 		kfree(ef->ctftab, M_LINKER);
92128730bd3SSimon Schubert 	if (ef->ctfoff)
92228730bd3SSimon Schubert 		kfree(ef->ctfoff, M_LINKER);
92328730bd3SSimon Schubert 	if (ef->typoff)
92428730bd3SSimon Schubert 		kfree(ef->typoff, M_LINKER);
92528730bd3SSimon Schubert 	kfree(ef, M_LINKER);
92628730bd3SSimon Schubert }
92728730bd3SSimon Schubert 
92828730bd3SSimon Schubert static const char *
symbol_name(elf_file_t ef,Elf_Size r_info)92928730bd3SSimon Schubert symbol_name(elf_file_t ef, Elf_Size r_info)
93028730bd3SSimon Schubert {
93128730bd3SSimon Schubert 	const Elf_Sym  *ref;
93228730bd3SSimon Schubert 
93328730bd3SSimon Schubert 	if (ELF_R_SYM(r_info)) {
93428730bd3SSimon Schubert 		ref = ef->ddbsymtab + ELF_R_SYM(r_info);
93528730bd3SSimon Schubert 		return ef->ddbstrtab + ref->st_name;
93628730bd3SSimon Schubert 	} else
93728730bd3SSimon Schubert 		return NULL;
93828730bd3SSimon Schubert }
93928730bd3SSimon Schubert 
94028730bd3SSimon Schubert static Elf_Addr
findbase(elf_file_t ef,int sec)94128730bd3SSimon Schubert findbase(elf_file_t ef, int sec)
94228730bd3SSimon Schubert {
94328730bd3SSimon Schubert 	int i;
94428730bd3SSimon Schubert 	Elf_Addr base = 0;
94528730bd3SSimon Schubert 
94628730bd3SSimon Schubert 	for (i = 0; i < ef->nprogtab; i++) {
94728730bd3SSimon Schubert 		if (sec == ef->progtab[i].sec) {
94828730bd3SSimon Schubert 			base = (Elf_Addr)ef->progtab[i].addr;
94928730bd3SSimon Schubert 			break;
95028730bd3SSimon Schubert 		}
95128730bd3SSimon Schubert 	}
95228730bd3SSimon Schubert 	return base;
95328730bd3SSimon Schubert }
95428730bd3SSimon Schubert 
95528730bd3SSimon Schubert static int
relocate_file(linker_file_t lf)95628730bd3SSimon Schubert relocate_file(linker_file_t lf)
95728730bd3SSimon Schubert {
95828730bd3SSimon Schubert 	elf_file_t	ef = lf->priv;
95928730bd3SSimon Schubert 	const Elf_Rel *rellim;
96028730bd3SSimon Schubert 	const Elf_Rel *rel;
96128730bd3SSimon Schubert 	const Elf_Rela *relalim;
96228730bd3SSimon Schubert 	const Elf_Rela *rela;
96328730bd3SSimon Schubert 	const char *symname;
96428730bd3SSimon Schubert 	const Elf_Sym *sym;
96528730bd3SSimon Schubert 	int i;
96628730bd3SSimon Schubert 	Elf_Size symidx;
96728730bd3SSimon Schubert 	Elf_Addr base;
96828730bd3SSimon Schubert 
96928730bd3SSimon Schubert 	/* Perform relocations without addend if there are any: */
97028730bd3SSimon Schubert 	for (i = 0; i < ef->nreltab; i++) {
97128730bd3SSimon Schubert 		rel = ef->reltab[i].rel;
97228730bd3SSimon Schubert 		if (rel == NULL)
97328730bd3SSimon Schubert 			panic("lost a reltab!");
97428730bd3SSimon Schubert 		rellim = rel + ef->reltab[i].nrel;
97528730bd3SSimon Schubert 		base = findbase(ef, ef->reltab[i].sec);
97628730bd3SSimon Schubert 		if (base == 0)
97728730bd3SSimon Schubert 			panic("lost base for reltab");
97828730bd3SSimon Schubert 		for ( ; rel < rellim; rel++) {
97928730bd3SSimon Schubert 			symidx = ELF_R_SYM(rel->r_info);
98028730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
98128730bd3SSimon Schubert 				continue;
98228730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
98328730bd3SSimon Schubert 			/* Local relocs are already done */
98428730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL)
98528730bd3SSimon Schubert 				continue;
98628730bd3SSimon Schubert 			if (elf_reloc(lf, base, rel, ELF_RELOC_REL,
98728730bd3SSimon Schubert 			    elf_obj_lookup)) {
98828730bd3SSimon Schubert 				symname = symbol_name(ef, rel->r_info);
98928730bd3SSimon Schubert 				kprintf("link_elf_obj_obj: symbol %s undefined\n",
99028730bd3SSimon Schubert 				    symname);
99128730bd3SSimon Schubert 				return ENOENT;
99228730bd3SSimon Schubert 			}
99328730bd3SSimon Schubert 		}
99428730bd3SSimon Schubert 	}
99528730bd3SSimon Schubert 
99628730bd3SSimon Schubert 	/* Perform relocations with addend if there are any: */
99728730bd3SSimon Schubert 	for (i = 0; i < ef->nrelatab; i++) {
99828730bd3SSimon Schubert 		rela = ef->relatab[i].rela;
99928730bd3SSimon Schubert 		if (rela == NULL)
100028730bd3SSimon Schubert 			panic("lost a relatab!");
100128730bd3SSimon Schubert 		relalim = rela + ef->relatab[i].nrela;
100228730bd3SSimon Schubert 		base = findbase(ef, ef->relatab[i].sec);
100328730bd3SSimon Schubert 		if (base == 0)
100428730bd3SSimon Schubert 			panic("lost base for relatab");
100528730bd3SSimon Schubert 		for ( ; rela < relalim; rela++) {
100628730bd3SSimon Schubert 			symidx = ELF_R_SYM(rela->r_info);
100728730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
100828730bd3SSimon Schubert 				continue;
100928730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
101028730bd3SSimon Schubert 			/* Local relocs are already done */
101128730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL)
101228730bd3SSimon Schubert 				continue;
101328730bd3SSimon Schubert 			if (elf_reloc(lf, base, rela, ELF_RELOC_RELA,
101428730bd3SSimon Schubert 			    elf_obj_lookup)) {
101528730bd3SSimon Schubert 				symname = symbol_name(ef, rela->r_info);
101628730bd3SSimon Schubert 				kprintf("link_elf_obj_obj: symbol %s undefined\n",
101728730bd3SSimon Schubert 				    symname);
101828730bd3SSimon Schubert 				return ENOENT;
101928730bd3SSimon Schubert 			}
102028730bd3SSimon Schubert 		}
102128730bd3SSimon Schubert 	}
102228730bd3SSimon Schubert 
102328730bd3SSimon Schubert 	return 0;
102428730bd3SSimon Schubert }
102528730bd3SSimon Schubert 
102628730bd3SSimon Schubert static int
link_elf_obj_lookup_symbol(linker_file_t lf,const char * name,c_linker_sym_t * sym)102728730bd3SSimon Schubert link_elf_obj_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
102828730bd3SSimon Schubert {
102928730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
103028730bd3SSimon Schubert 	const Elf_Sym *symp;
103128730bd3SSimon Schubert 	const char *strp;
103228730bd3SSimon Schubert 	int i;
103328730bd3SSimon Schubert 
103428730bd3SSimon Schubert 	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
103528730bd3SSimon Schubert 		strp = ef->ddbstrtab + symp->st_name;
103628730bd3SSimon Schubert 		if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
103728730bd3SSimon Schubert 			*sym = (c_linker_sym_t) symp;
103828730bd3SSimon Schubert 			return 0;
103928730bd3SSimon Schubert 		}
104028730bd3SSimon Schubert 	}
104128730bd3SSimon Schubert 	return ENOENT;
104228730bd3SSimon Schubert }
104328730bd3SSimon Schubert 
104428730bd3SSimon Schubert static int
link_elf_obj_symbol_values(linker_file_t lf,c_linker_sym_t sym,linker_symval_t * symval)104528730bd3SSimon Schubert link_elf_obj_symbol_values(linker_file_t lf, c_linker_sym_t sym,
104628730bd3SSimon Schubert     linker_symval_t *symval)
104728730bd3SSimon Schubert {
104828730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
104928730bd3SSimon Schubert 	const Elf_Sym *es = (const Elf_Sym*) sym;
105028730bd3SSimon Schubert 
105128730bd3SSimon Schubert 	if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
105228730bd3SSimon Schubert 		symval->name = ef->ddbstrtab + es->st_name;
105328730bd3SSimon Schubert 		symval->value = (caddr_t)es->st_value;
105428730bd3SSimon Schubert 		symval->size = es->st_size;
105528730bd3SSimon Schubert 		return 0;
105628730bd3SSimon Schubert 	}
105728730bd3SSimon Schubert 	return ENOENT;
105828730bd3SSimon Schubert }
105928730bd3SSimon Schubert 
106028730bd3SSimon Schubert static int
link_elf_obj_search_symbol(linker_file_t lf,caddr_t value,c_linker_sym_t * sym,long * diffp)106128730bd3SSimon Schubert link_elf_obj_search_symbol(linker_file_t lf, caddr_t value,
106228730bd3SSimon Schubert     c_linker_sym_t *sym, long *diffp)
106328730bd3SSimon Schubert {
106428730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
106528730bd3SSimon Schubert 	u_long off = (uintptr_t) (void *) value;
106628730bd3SSimon Schubert 	u_long diff = off;
106728730bd3SSimon Schubert 	u_long st_value;
106828730bd3SSimon Schubert 	const Elf_Sym *es;
10694090d6ffSSascha Wildner 	const Elf_Sym *best = NULL;
107028730bd3SSimon Schubert 	int i;
107128730bd3SSimon Schubert 
107228730bd3SSimon Schubert 	for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
107328730bd3SSimon Schubert 		if (es->st_name == 0)
107428730bd3SSimon Schubert 			continue;
107528730bd3SSimon Schubert 		st_value = es->st_value;
107628730bd3SSimon Schubert 		if (off >= st_value) {
107728730bd3SSimon Schubert 			if (off - st_value < diff) {
107828730bd3SSimon Schubert 				diff = off - st_value;
107928730bd3SSimon Schubert 				best = es;
108028730bd3SSimon Schubert 				if (diff == 0)
108128730bd3SSimon Schubert 					break;
108228730bd3SSimon Schubert 			} else if (off - st_value == diff) {
108328730bd3SSimon Schubert 				best = es;
108428730bd3SSimon Schubert 			}
108528730bd3SSimon Schubert 		}
108628730bd3SSimon Schubert 	}
10874090d6ffSSascha Wildner 	if (best == NULL)
108828730bd3SSimon Schubert 		*diffp = off;
108928730bd3SSimon Schubert 	else
109028730bd3SSimon Schubert 		*diffp = diff;
109128730bd3SSimon Schubert 	*sym = (c_linker_sym_t) best;
109228730bd3SSimon Schubert 
109328730bd3SSimon Schubert 	return 0;
109428730bd3SSimon Schubert }
109528730bd3SSimon Schubert 
109628730bd3SSimon Schubert /*
109728730bd3SSimon Schubert  * Look up a linker set on an ELF system.
109828730bd3SSimon Schubert  */
109928730bd3SSimon Schubert static int
link_elf_obj_lookup_set(linker_file_t lf,const char * name,void *** startp,void *** stopp,int * countp)110028730bd3SSimon Schubert link_elf_obj_lookup_set(linker_file_t lf, const char *name,
110128730bd3SSimon Schubert     void ***startp, void ***stopp, int *countp)
110228730bd3SSimon Schubert {
110328730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
110428730bd3SSimon Schubert 	void **start, **stop;
110528730bd3SSimon Schubert 	int i, count;
110628730bd3SSimon Schubert 
110728730bd3SSimon Schubert 	/* Relative to section number */
110828730bd3SSimon Schubert 	for (i = 0; i < ef->nprogtab; i++) {
110928730bd3SSimon Schubert 		if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
111028730bd3SSimon Schubert 		    strcmp(ef->progtab[i].name + 4, name) == 0) {
111128730bd3SSimon Schubert 			start  = (void **)ef->progtab[i].addr;
111228730bd3SSimon Schubert 			stop = (void **)((char *)ef->progtab[i].addr +
111328730bd3SSimon Schubert 			    ef->progtab[i].size);
111428730bd3SSimon Schubert 			count = stop - start;
111528730bd3SSimon Schubert 			if (startp)
111628730bd3SSimon Schubert 				*startp = start;
111728730bd3SSimon Schubert 			if (stopp)
111828730bd3SSimon Schubert 				*stopp = stop;
111928730bd3SSimon Schubert 			if (countp)
112028730bd3SSimon Schubert 				*countp = count;
112128730bd3SSimon Schubert 			return (0);
112228730bd3SSimon Schubert 		}
112328730bd3SSimon Schubert 	}
112428730bd3SSimon Schubert 	return (ESRCH);
112528730bd3SSimon Schubert }
112628730bd3SSimon Schubert 
112728730bd3SSimon Schubert /*
112828730bd3SSimon Schubert  * Symbol lookup function that can be used when the symbol index is known (ie
112928730bd3SSimon Schubert  * in relocations). It uses the symbol index instead of doing a fully fledged
113028730bd3SSimon Schubert  * hash table based lookup when such is valid. For example for local symbols.
113128730bd3SSimon Schubert  * This is not only more efficient, it's also more correct. It's not always
113228730bd3SSimon Schubert  * the case that the symbol can be found through the hash table.
113328730bd3SSimon Schubert  */
113428730bd3SSimon Schubert static int
elf_obj_lookup(linker_file_t lf,Elf_Size symidx,int deps,Elf_Addr * result)113528730bd3SSimon Schubert elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *result)
113628730bd3SSimon Schubert {
113728730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
113828730bd3SSimon Schubert 	const Elf_Sym *sym;
113928730bd3SSimon Schubert 	const char *symbol;
114028730bd3SSimon Schubert 
114128730bd3SSimon Schubert 	/* Don't even try to lookup the symbol if the index is bogus. */
114228730bd3SSimon Schubert 	if (symidx >= ef->ddbsymcnt)
114328730bd3SSimon Schubert 		return (ENOENT);
114428730bd3SSimon Schubert 
114528730bd3SSimon Schubert 	sym = ef->ddbsymtab + symidx;
114628730bd3SSimon Schubert 
114728730bd3SSimon Schubert 	/* Quick answer if there is a definition included. */
114828730bd3SSimon Schubert 	if (sym->st_shndx != SHN_UNDEF) {
114928730bd3SSimon Schubert 		*result = sym->st_value;
115028730bd3SSimon Schubert 		return (0);
115128730bd3SSimon Schubert 	}
115228730bd3SSimon Schubert 
115328730bd3SSimon Schubert 	/* If we get here, then it is undefined and needs a lookup. */
115428730bd3SSimon Schubert 	switch (ELF_ST_BIND(sym->st_info)) {
115528730bd3SSimon Schubert 	case STB_LOCAL:
115628730bd3SSimon Schubert 		/* Local, but undefined? huh? */
115728730bd3SSimon Schubert 		return (ENOENT);
115828730bd3SSimon Schubert 
115928730bd3SSimon Schubert 	case STB_GLOBAL:
116028730bd3SSimon Schubert 		/* Relative to Data or Function name */
116128730bd3SSimon Schubert 		symbol = ef->ddbstrtab + sym->st_name;
116228730bd3SSimon Schubert 
116328730bd3SSimon Schubert 		/* Force a lookup failure if the symbol name is bogus. */
116428730bd3SSimon Schubert 		if (*symbol == 0)
116528730bd3SSimon Schubert 			return (ENOENT);
116628730bd3SSimon Schubert 		return (linker_file_lookup_symbol(lf, symbol, deps, (caddr_t *)result));
116728730bd3SSimon Schubert 
116828730bd3SSimon Schubert 	case STB_WEAK:
116928730bd3SSimon Schubert 		kprintf("link_elf_obj_obj: Weak symbols not supported\n");
117028730bd3SSimon Schubert 		return (ENOENT);
117128730bd3SSimon Schubert 
117228730bd3SSimon Schubert 	default:
117328730bd3SSimon Schubert 		return (ENOENT);
117428730bd3SSimon Schubert 	}
117528730bd3SSimon Schubert }
117628730bd3SSimon Schubert 
117728730bd3SSimon Schubert static void
link_elf_obj_fix_link_set(elf_file_t ef)117828730bd3SSimon Schubert link_elf_obj_fix_link_set(elf_file_t ef)
117928730bd3SSimon Schubert {
118028730bd3SSimon Schubert 	static const char startn[] = "__start_";
118128730bd3SSimon Schubert 	static const char stopn[] = "__stop_";
118228730bd3SSimon Schubert 	Elf_Sym *sym;
118328730bd3SSimon Schubert 	const char *sym_name, *linkset_name;
118428730bd3SSimon Schubert 	Elf_Addr startp, stopp;
118528730bd3SSimon Schubert 	Elf_Size symidx;
118628730bd3SSimon Schubert 	int start, i;
118728730bd3SSimon Schubert 
118828730bd3SSimon Schubert 	startp = stopp = 0;
118928730bd3SSimon Schubert 	for (symidx = 1 /* zero entry is special */;
119028730bd3SSimon Schubert 		symidx < ef->ddbsymcnt; symidx++) {
119128730bd3SSimon Schubert 		sym = ef->ddbsymtab + symidx;
119228730bd3SSimon Schubert 		if (sym->st_shndx != SHN_UNDEF)
119328730bd3SSimon Schubert 			continue;
119428730bd3SSimon Schubert 
119528730bd3SSimon Schubert 		sym_name = ef->ddbstrtab + sym->st_name;
119628730bd3SSimon Schubert 		if (strncmp(sym_name, startn, sizeof(startn) - 1) == 0) {
119728730bd3SSimon Schubert 			start = 1;
119828730bd3SSimon Schubert 			linkset_name = sym_name + sizeof(startn) - 1;
119928730bd3SSimon Schubert 		}
120028730bd3SSimon Schubert 		else if (strncmp(sym_name, stopn, sizeof(stopn) - 1) == 0) {
120128730bd3SSimon Schubert 			start = 0;
120228730bd3SSimon Schubert 			linkset_name = sym_name + sizeof(stopn) - 1;
120328730bd3SSimon Schubert 		}
120428730bd3SSimon Schubert 		else
120528730bd3SSimon Schubert 			continue;
120628730bd3SSimon Schubert 
120728730bd3SSimon Schubert 		for (i = 0; i < ef->nprogtab; i++) {
120828730bd3SSimon Schubert 			if (strcmp(ef->progtab[i].name, linkset_name) == 0) {
120928730bd3SSimon Schubert 				startp = (Elf_Addr)ef->progtab[i].addr;
121028730bd3SSimon Schubert 				stopp = (Elf_Addr)(startp + ef->progtab[i].size);
121128730bd3SSimon Schubert 				break;
121228730bd3SSimon Schubert 			}
121328730bd3SSimon Schubert 		}
121428730bd3SSimon Schubert 		if (i == ef->nprogtab)
121528730bd3SSimon Schubert 			continue;
121628730bd3SSimon Schubert 
121728730bd3SSimon Schubert 		sym->st_value = start ? startp : stopp;
121828730bd3SSimon Schubert 		sym->st_shndx = i;
121928730bd3SSimon Schubert 	}
122028730bd3SSimon Schubert }
122128730bd3SSimon Schubert 
122228730bd3SSimon Schubert static void
link_elf_obj_reloc_local(linker_file_t lf)122328730bd3SSimon Schubert link_elf_obj_reloc_local(linker_file_t lf)
122428730bd3SSimon Schubert {
122528730bd3SSimon Schubert 	elf_file_t ef = lf->priv;
122628730bd3SSimon Schubert 	const Elf_Rel *rellim;
122728730bd3SSimon Schubert 	const Elf_Rel *rel;
122828730bd3SSimon Schubert 	const Elf_Rela *relalim;
122928730bd3SSimon Schubert 	const Elf_Rela *rela;
123028730bd3SSimon Schubert 	const Elf_Sym *sym;
123128730bd3SSimon Schubert 	Elf_Addr base;
123228730bd3SSimon Schubert 	int i;
123328730bd3SSimon Schubert 	Elf_Size symidx;
123428730bd3SSimon Schubert 
123528730bd3SSimon Schubert 	link_elf_obj_fix_link_set(ef);
123628730bd3SSimon Schubert 
123728730bd3SSimon Schubert 	/* Perform relocations without addend if there are any: */
123828730bd3SSimon Schubert 	for (i = 0; i < ef->nreltab; i++) {
123928730bd3SSimon Schubert 		rel = ef->reltab[i].rel;
124028730bd3SSimon Schubert 		if (rel == NULL)
124128730bd3SSimon Schubert 			panic("lost a reltab!");
124228730bd3SSimon Schubert 		rellim = rel + ef->reltab[i].nrel;
124328730bd3SSimon Schubert 		base = findbase(ef, ef->reltab[i].sec);
124428730bd3SSimon Schubert 		if (base == 0)
124528730bd3SSimon Schubert 			panic("lost base for reltab");
124628730bd3SSimon Schubert 		for ( ; rel < rellim; rel++) {
124728730bd3SSimon Schubert 			symidx = ELF_R_SYM(rel->r_info);
124828730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
124928730bd3SSimon Schubert 				continue;
125028730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
125128730bd3SSimon Schubert 			/* Only do local relocs */
125228730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) != STB_LOCAL)
125328730bd3SSimon Schubert 				continue;
125428730bd3SSimon Schubert 			elf_reloc_local(lf, base, rel, ELF_RELOC_REL,
125528730bd3SSimon Schubert 			    elf_obj_lookup);
125628730bd3SSimon Schubert 		}
125728730bd3SSimon Schubert 	}
125828730bd3SSimon Schubert 
125928730bd3SSimon Schubert 	/* Perform relocations with addend if there are any: */
126028730bd3SSimon Schubert 	for (i = 0; i < ef->nrelatab; i++) {
126128730bd3SSimon Schubert 		rela = ef->relatab[i].rela;
126228730bd3SSimon Schubert 		if (rela == NULL)
126328730bd3SSimon Schubert 			panic("lost a relatab!");
126428730bd3SSimon Schubert 		relalim = rela + ef->relatab[i].nrela;
126528730bd3SSimon Schubert 		base = findbase(ef, ef->relatab[i].sec);
126628730bd3SSimon Schubert 		if (base == 0)
126728730bd3SSimon Schubert 			panic("lost base for relatab");
126828730bd3SSimon Schubert 		for ( ; rela < relalim; rela++) {
126928730bd3SSimon Schubert 			symidx = ELF_R_SYM(rela->r_info);
127028730bd3SSimon Schubert 			if (symidx >= ef->ddbsymcnt)
127128730bd3SSimon Schubert 				continue;
127228730bd3SSimon Schubert 			sym = ef->ddbsymtab + symidx;
127328730bd3SSimon Schubert 			/* Only do local relocs */
127428730bd3SSimon Schubert 			if (ELF_ST_BIND(sym->st_info) != STB_LOCAL)
127528730bd3SSimon Schubert 				continue;
127628730bd3SSimon Schubert 			elf_reloc_local(lf, base, rela, ELF_RELOC_RELA,
127728730bd3SSimon Schubert 			    elf_obj_lookup);
127828730bd3SSimon Schubert 		}
127928730bd3SSimon Schubert 	}
128028730bd3SSimon Schubert }
1281