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