link_elf.c revision 38514
138514Sdfr/*- 238514Sdfr * Copyright (c) 1998 Doug Rabson 338514Sdfr * All rights reserved. 438514Sdfr * 538514Sdfr * Redistribution and use in source and binary forms, with or without 638514Sdfr * modification, are permitted provided that the following conditions 738514Sdfr * are met: 838514Sdfr * 1. Redistributions of source code must retain the above copyright 938514Sdfr * notice, this list of conditions and the following disclaimer. 1038514Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1138514Sdfr * notice, this list of conditions and the following disclaimer in the 1238514Sdfr * documentation and/or other materials provided with the distribution. 1338514Sdfr * 1438514Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538514Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638514Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738514Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838514Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938514Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038514Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138514Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238514Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338514Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438514Sdfr * SUCH DAMAGE. 2538514Sdfr * 2638514Sdfr * $Id$ 2738514Sdfr */ 2838514Sdfr 2938514Sdfr#include <sys/param.h> 3038514Sdfr#include <sys/kernel.h> 3138514Sdfr#include <sys/systm.h> 3238514Sdfr#include <sys/malloc.h> 3338514Sdfr#include <sys/proc.h> 3438514Sdfr#include <sys/namei.h> 3538514Sdfr#include <sys/fcntl.h> 3638514Sdfr#include <sys/vnode.h> 3738514Sdfr#include <sys/linker.h> 3838514Sdfr#include <machine/elf.h> 3938514Sdfr 4038514Sdfrstatic int link_elf_load_file(const char*, linker_file_t*); 4138514Sdfrstatic int link_elf_lookup_symbol(linker_file_t, const char*, 4238514Sdfr linker_sym_t*); 4338514Sdfrstatic void link_elf_symbol_values(linker_file_t, linker_sym_t, linker_symval_t*); 4438514Sdfrstatic int link_elf_search_symbol(linker_file_t, caddr_t value, 4538514Sdfr linker_sym_t* sym, long* diffp); 4638514Sdfr 4738514Sdfrstatic void link_elf_unload(linker_file_t); 4838514Sdfr 4938514Sdfr/* 5038514Sdfr * The file representing the currently running kernel. This contains 5138514Sdfr * the global symbol table. 5238514Sdfr */ 5338514Sdfr 5438514Sdfrlinker_file_t linker_kernel_file; 5538514Sdfr 5638514Sdfrstatic struct linker_class_ops link_elf_class_ops = { 5738514Sdfr link_elf_load_file, 5838514Sdfr}; 5938514Sdfr 6038514Sdfrstatic struct linker_file_ops link_elf_file_ops = { 6138514Sdfr link_elf_lookup_symbol, 6238514Sdfr link_elf_symbol_values, 6338514Sdfr link_elf_search_symbol, 6438514Sdfr link_elf_unload, 6538514Sdfr}; 6638514Sdfr 6738514Sdfrtypedef struct elf_file { 6838514Sdfr caddr_t address; /* Load address */ 6938514Sdfr Elf_Dyn* dynamic; /* Symbol table etc. */ 7038514Sdfr Elf_Off nbuckets; /* DT_HASH info */ 7138514Sdfr Elf_Off nchains; 7238514Sdfr const Elf_Off* buckets; 7338514Sdfr const Elf_Off* chains; 7438514Sdfr caddr_t hash; 7538514Sdfr caddr_t strtab; /* DT_STRTAB */ 7638514Sdfr Elf_Sym* symtab; /* DT_SYMTAB */ 7738514Sdfr} *elf_file_t; 7838514Sdfr 7938514Sdfrstatic int parse_dynamic(linker_file_t lf); 8038514Sdfrstatic int load_dependancies(linker_file_t lf); 8138514Sdfrstatic int relocate_file(linker_file_t lf); 8238514Sdfr 8338514Sdfr/* 8438514Sdfr * The kernel symbol table starts here. 8538514Sdfr */ 8638514Sdfrextern struct _dynamic _DYNAMIC; 8738514Sdfr 8838514Sdfrstatic void 8938514Sdfrlink_elf_init(void* arg) 9038514Sdfr{ 9138514Sdfr Elf_Dyn* dp = (Elf_Dyn*) &_DYNAMIC; 9238514Sdfr 9338514Sdfr#if ELF_TARG_CLASS == ELFCLASS32 9438514Sdfr linker_add_class("elf32", NULL, &link_elf_class_ops); 9538514Sdfr#else 9638514Sdfr linker_add_class("elf64", NULL, &link_elf_class_ops); 9738514Sdfr#endif 9838514Sdfr 9938514Sdfr if (dp) { 10038514Sdfr elf_file_t ef; 10138514Sdfr 10238514Sdfr ef = malloc(sizeof(struct elf_file), M_LINKER, M_NOWAIT); 10338514Sdfr if (ef == NULL) 10438514Sdfr panic("link_elf_init: Can't create linker structures for kernel"); 10538514Sdfr 10638514Sdfr ef->address = 0; 10738514Sdfr ef->dynamic = dp; 10838514Sdfr linker_kernel_file = 10938514Sdfr linker_make_file(kernelname, ef, &link_elf_file_ops); 11038514Sdfr if (linker_kernel_file == NULL) 11138514Sdfr panic("link_elf_init: Can't create linker structures for kernel"); 11238514Sdfr parse_dynamic(linker_kernel_file); 11338514Sdfr /* 11438514Sdfr * XXX there must be a better way of getting these constants. 11538514Sdfr */ 11638514Sdfr#ifdef __alpha__ 11738514Sdfr linker_kernel_file->address = (caddr_t) 0xfffffc0000300000; 11838514Sdfr#else 11938514Sdfr linker_kernel_file->address = (caddr_t) 0xf0100000; 12038514Sdfr#endif 12138514Sdfr linker_kernel_file->size = -(long)linker_kernel_file->address; 12238514Sdfr linker_current_file = linker_kernel_file; 12338514Sdfr } 12438514Sdfr} 12538514Sdfr 12638514SdfrSYSINIT(link_elf, SI_SUB_KMEM, SI_ORDER_THIRD, link_elf_init, 0); 12738514Sdfr 12838514Sdfrstatic int 12938514Sdfrparse_dynamic(linker_file_t lf) 13038514Sdfr{ 13138514Sdfr elf_file_t ef = lf->priv; 13238514Sdfr Elf_Dyn *dp; 13338514Sdfr 13438514Sdfr for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 13538514Sdfr switch (dp->d_tag) { 13638514Sdfr case DT_HASH: 13738514Sdfr { 13838514Sdfr /* From src/libexec/rtld-elf/rtld.c */ 13938514Sdfr const Elf_Off *hashtab = (const Elf_Off *) 14038514Sdfr (ef->address + dp->d_un.d_ptr); 14138514Sdfr ef->nbuckets = hashtab[0]; 14238514Sdfr ef->nchains = hashtab[1]; 14338514Sdfr ef->buckets = hashtab + 2; 14438514Sdfr ef->chains = ef->buckets + ef->nbuckets; 14538514Sdfr break; 14638514Sdfr } 14738514Sdfr case DT_STRTAB: 14838514Sdfr ef->strtab = (caddr_t) dp->d_un.d_ptr; 14938514Sdfr break; 15038514Sdfr case DT_SYMTAB: 15138514Sdfr ef->symtab = (Elf_Sym*) dp->d_un.d_ptr; 15238514Sdfr break; 15338514Sdfr case DT_SYMENT: 15438514Sdfr if (dp->d_un.d_val != sizeof(Elf_Sym)) 15538514Sdfr return ENOEXEC; 15638514Sdfr } 15738514Sdfr } 15838514Sdfr return 0; 15938514Sdfr} 16038514Sdfr 16138514Sdfrstatic int 16238514Sdfrlink_elf_load_file(const char* filename, linker_file_t* result) 16338514Sdfr{ 16438514Sdfr#if 0 16538514Sdfr struct nameidata nd; 16638514Sdfr struct proc* p = curproc; /* XXX */ 16738514Sdfr int error = 0; 16838514Sdfr int resid; 16938514Sdfr struct exec header; 17038514Sdfr elf_file_t ef; 17138514Sdfr linker_file_t lf; 17238514Sdfr 17338514Sdfr NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p); 17438514Sdfr error = vn_open(&nd, FREAD, 0); 17538514Sdfr if (error) 17638514Sdfr return error; 17738514Sdfr 17838514Sdfr /* 17938514Sdfr * Read the a.out header from the file. 18038514Sdfr */ 18138514Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) &header, sizeof header, 0, 18238514Sdfr UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 18338514Sdfr if (error) 18438514Sdfr goto out; 18538514Sdfr 18638514Sdfr if (N_BADMAG(header) || !(N_GETFLAG(header) & EX_DYNAMIC)) 18738514Sdfr goto out; 18838514Sdfr 18938514Sdfr /* 19038514Sdfr * We have an a.out file, so make some space to read it in. 19138514Sdfr */ 19238514Sdfr ef = malloc(sizeof(struct elf_file), M_LINKER, M_WAITOK); 19338514Sdfr ef->address = malloc(header.a_text + header.a_data + header.a_bss, 19438514Sdfr M_LINKER, M_WAITOK); 19538514Sdfr 19638514Sdfr /* 19738514Sdfr * Read the text and data sections and zero the bss. 19838514Sdfr */ 19938514Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) ef->address, 20038514Sdfr header.a_text + header.a_data, 0, 20138514Sdfr UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 20238514Sdfr if (error) 20338514Sdfr goto out; 20438514Sdfr bzero(ef->address + header.a_text + header.a_data, header.a_bss); 20538514Sdfr 20638514Sdfr /* 20738514Sdfr * Assume _DYNAMIC is the first data item. 20838514Sdfr */ 20938514Sdfr ef->dynamic = (struct _dynamic*) (ef->address + header.a_text); 21038514Sdfr if (ef->dynamic->d_version != LD_VERSION_BSD) { 21138514Sdfr free(ef->address, M_LINKER); 21238514Sdfr free(ef, M_LINKER); 21338514Sdfr goto out; 21438514Sdfr } 21538514Sdfr (long) ef->dynamic->d_un.d_sdt += ef->address; 21638514Sdfr 21738514Sdfr lf = linker_make_file(filename, ef, &link_elf_file_ops); 21838514Sdfr if (lf == NULL) { 21938514Sdfr free(ef->address, M_LINKER); 22038514Sdfr free(ef, M_LINKER); 22138514Sdfr error = ENOMEM; 22238514Sdfr goto out; 22338514Sdfr } 22438514Sdfr lf->address = ef->address; 22538514Sdfr lf->size = header.a_text + header.a_data + header.a_bss; 22638514Sdfr 22738514Sdfr if ((error = load_dependancies(lf)) != 0 22838514Sdfr || (error = relocate_file(lf)) != 0) { 22938514Sdfr linker_file_unload(lf); 23038514Sdfr goto out; 23138514Sdfr } 23238514Sdfr 23338514Sdfr *result = lf; 23438514Sdfr 23538514Sdfrout: 23638514Sdfr VOP_UNLOCK(nd.ni_vp, 0, p); 23738514Sdfr vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 23838514Sdfr 23938514Sdfr return error; 24038514Sdfr#else 24138514Sdfr return ENOEXEC; 24238514Sdfr#endif 24338514Sdfr} 24438514Sdfr 24538514Sdfrstatic void 24638514Sdfrlink_elf_unload(linker_file_t file) 24738514Sdfr{ 24838514Sdfr elf_file_t ef = file->priv; 24938514Sdfr 25038514Sdfr if (ef) { 25138514Sdfr if (ef->address) 25238514Sdfr free(ef->address, M_LINKER); 25338514Sdfr free(ef, M_LINKER); 25438514Sdfr } 25538514Sdfr} 25638514Sdfr 25738514Sdfr#define ELF_RELOC(ef, type, off) (type*) ((ef)->address + (off)) 25838514Sdfr 25938514Sdfrstatic int 26038514Sdfrload_dependancies(linker_file_t lf) 26138514Sdfr{ 26238514Sdfr#if 0 26338514Sdfr elf_file_t ef = lf->priv; 26438514Sdfr linker_file_t lfdep; 26538514Sdfr long off; 26638514Sdfr struct sod* sodp; 26738514Sdfr char* name; 26838514Sdfr char* filename = 0; 26938514Sdfr int error = 0; 27038514Sdfr 27138514Sdfr /* 27238514Sdfr * All files are dependant on /kernel. 27338514Sdfr */ 27438514Sdfr linker_kernel_file->refs++; 27538514Sdfr linker_file_add_dependancy(lf, linker_kernel_file); 27638514Sdfr 27738514Sdfr off = LD_NEED(ef->dynamic); 27838514Sdfr 27938514Sdfr /* 28038514Sdfr * Load the dependancies. 28138514Sdfr */ 28238514Sdfr while (off != 0) { 28338514Sdfr sodp = ELF_RELOC(ef, struct sod, off); 28438514Sdfr name = ELF_RELOC(ef, char, sodp->sod_name); 28538514Sdfr 28638514Sdfr /* 28738514Sdfr * Prepend pathname if dep is not an absolute filename. 28838514Sdfr */ 28938514Sdfr if (name[0] != '/') { 29038514Sdfr char* p; 29138514Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 29238514Sdfr p = lf->filename + strlen(lf->filename) - 1; 29338514Sdfr while (p >= lf->filename && *p != '/') 29438514Sdfr p--; 29538514Sdfr if (p >= lf->filename) { 29638514Sdfr strncpy(filename, lf->filename, p - lf->filename); 29738514Sdfr filename[p - lf->filename] = '\0'; 29838514Sdfr strcat(filename, "/"); 29938514Sdfr strcat(filename, name); 30038514Sdfr name = filename; 30138514Sdfr } 30238514Sdfr } 30338514Sdfr error = linker_load_file(name, &lfdep); 30438514Sdfr if (error) 30538514Sdfr goto out; 30638514Sdfr error = linker_file_add_dependancy(lf, lfdep); 30738514Sdfr if (error) 30838514Sdfr goto out; 30938514Sdfr off = sodp->sod_next; 31038514Sdfr } 31138514Sdfr 31238514Sdfrout: 31338514Sdfr if (filename) 31438514Sdfr free(filename, M_TEMP); 31538514Sdfr return error; 31638514Sdfr#else 31738514Sdfr return ENOEXEC; 31838514Sdfr#endif 31938514Sdfr} 32038514Sdfr 32138514Sdfr#if 0 32238514Sdfr/* 32338514Sdfr * XXX i386 dependant. 32438514Sdfr */ 32538514Sdfrstatic long 32638514Sdfrread_relocation(struct relocation_info* r, char* addr) 32738514Sdfr{ 32838514Sdfr int length = r->r_length; 32938514Sdfr if (length == 0) 33038514Sdfr return *(u_char*) addr; 33138514Sdfr else if (length == 1) 33238514Sdfr return *(u_short*) addr; 33338514Sdfr else if (length == 2) 33438514Sdfr return *(u_int*) addr; 33538514Sdfr else 33638514Sdfr printf("link_elf: unsupported relocation size %d\n", r->r_length); 33738514Sdfr return 0; 33838514Sdfr} 33938514Sdfr 34038514Sdfrstatic void 34138514Sdfrwrite_relocation(struct relocation_info* r, char* addr, long value) 34238514Sdfr{ 34338514Sdfr int length = r->r_length; 34438514Sdfr if (length == 0) 34538514Sdfr *(u_char*) addr = value; 34638514Sdfr else if (length == 1) 34738514Sdfr *(u_short*) addr = value; 34838514Sdfr else if (length == 2) 34938514Sdfr *(u_int*) addr = value; 35038514Sdfr else 35138514Sdfr printf("link_elf: unsupported relocation size %d\n", r->r_length); 35238514Sdfr} 35338514Sdfr 35438514Sdfrstatic int 35538514Sdfrrelocate_file(linker_file_t lf) 35638514Sdfr{ 35738514Sdfr elf_file_t ef = lf->priv; 35838514Sdfr struct relocation_info* rel; 35938514Sdfr struct relocation_info* erel; 36038514Sdfr struct relocation_info* r; 36138514Sdfr struct nzlist* symbolbase; 36238514Sdfr char* stringbase; 36338514Sdfr struct nzlist* np; 36438514Sdfr char* sym; 36538514Sdfr long relocation; 36638514Sdfr 36738514Sdfr rel = ELF_RELOC(ef, struct relocation_info, LD_REL(ef->dynamic)); 36838514Sdfr erel = ELF_RELOC(ef, struct relocation_info, 36938514Sdfr LD_REL(ef->dynamic) + LD_RELSZ(ef->dynamic)); 37038514Sdfr symbolbase = ELF_RELOC(ef, struct nzlist, LD_SYMBOL(ef->dynamic)); 37138514Sdfr stringbase = ELF_RELOC(ef, char, LD_STRINGS(ef->dynamic)); 37238514Sdfr 37338514Sdfr for (r = rel; r < erel; r++) { 37438514Sdfr char* addr; 37538514Sdfr 37638514Sdfr if (r->r_address == 0) 37738514Sdfr break; 37838514Sdfr 37938514Sdfr addr = ELF_RELOC(ef, char, r->r_address); 38038514Sdfr if (r->r_extern) { 38138514Sdfr np = &symbolbase[r->r_symbolnum]; 38238514Sdfr sym = &stringbase[np->nz_strx]; 38338514Sdfr 38438514Sdfr if (sym[0] != '_') { 38538514Sdfr printf("link_elf: bad symbol name %s\n", sym); 38638514Sdfr relocation = 0; 38738514Sdfr } else 38838514Sdfr relocation = (long) 38938514Sdfr linker_file_lookup_symbol(lf, sym + 1, 39038514Sdfr np->nz_type != (N_SETV+N_EXT)); 39138514Sdfr if (!relocation) { 39238514Sdfr printf("link_elf: symbol %s not found\n", sym); 39338514Sdfr return ENOENT; 39438514Sdfr } 39538514Sdfr 39638514Sdfr relocation += read_relocation(r, addr); 39738514Sdfr 39838514Sdfr if (r->r_jmptable) { 39938514Sdfr printf("link_elf: can't cope with jump table relocations\n"); 40038514Sdfr continue; 40138514Sdfr } 40238514Sdfr 40338514Sdfr if (r->r_pcrel) 40438514Sdfr relocation -= (long) ef->address; 40538514Sdfr 40638514Sdfr if (r->r_copy) { 40738514Sdfr printf("link_elf: can't cope with copy relocations\n"); 40838514Sdfr continue; 40938514Sdfr } 41038514Sdfr 41138514Sdfr write_relocation(r, addr, relocation); 41238514Sdfr } else { 41338514Sdfr write_relocation(r, addr, 41438514Sdfr (long)(read_relocation(r, addr) + ef->address)); 41538514Sdfr } 41638514Sdfr 41738514Sdfr } 41838514Sdfr 41938514Sdfr return 0; 42038514Sdfr} 42138514Sdfr 42238514Sdfrstatic long 42338514Sdfrsymbol_hash_value(elf_file_t ef, const char* name) 42438514Sdfr{ 42538514Sdfr long hashval; 42638514Sdfr const char* p; 42738514Sdfr 42838514Sdfr hashval = '_'; /* fake a starting '_' for C symbols */ 42938514Sdfr for (p = name; *p; p++) 43038514Sdfr hashval = (hashval << 1) + *p; 43138514Sdfr 43238514Sdfr return (hashval & 0x7fffffff) % LD_BUCKETS(ef->dynamic); 43338514Sdfr} 43438514Sdfr 43538514Sdfr#endif 43638514Sdfr 43738514Sdfrint 43838514Sdfrlink_elf_lookup_symbol(linker_file_t lf, const char* name, linker_sym_t* sym) 43938514Sdfr{ 44038514Sdfr elf_file_t ef = lf->priv; 44138514Sdfr int symcount = ef->nchains; 44238514Sdfr Elf_Sym* es; 44338514Sdfr int i; 44438514Sdfr 44538514Sdfr /* XXX use hash table */ 44638514Sdfr for (i = 0, es = ef->symtab; i < ef->nchains; i++, es++) { 44738514Sdfr if (es->st_name == 0) 44838514Sdfr continue; 44938514Sdfr if (!strcmp(ef->strtab + es->st_name, name)) { 45038514Sdfr *sym = (linker_sym_t) es; 45138514Sdfr return 0; 45238514Sdfr } 45338514Sdfr } 45438514Sdfr 45538514Sdfr return ENOENT; 45638514Sdfr} 45738514Sdfr 45838514Sdfrstatic void 45938514Sdfrlink_elf_symbol_values(linker_file_t lf, linker_sym_t sym, linker_symval_t* symval) 46038514Sdfr{ 46138514Sdfr elf_file_t ef = lf->priv; 46238514Sdfr Elf_Sym* es = (Elf_Sym*) sym; 46338514Sdfr 46438514Sdfr symval->name = ef->strtab + es->st_name; 46538514Sdfr symval->value = (caddr_t) es->st_value; 46638514Sdfr symval->size = es->st_size; 46738514Sdfr} 46838514Sdfr 46938514Sdfrstatic int 47038514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value, 47138514Sdfr linker_sym_t* sym, long* diffp) 47238514Sdfr{ 47338514Sdfr elf_file_t ef = lf->priv; 47438514Sdfr u_long off = (u_long) value; 47538514Sdfr u_long diff = off; 47638514Sdfr int symcount = ef->nchains; 47738514Sdfr Elf_Sym* es; 47838514Sdfr Elf_Sym* best = 0; 47938514Sdfr int i; 48038514Sdfr 48138514Sdfr for (i = 0, es = ef->symtab; i < ef->nchains; i++, es++) { 48238514Sdfr if (es->st_name == 0) 48338514Sdfr continue; 48438514Sdfr if (off >= es->st_value) { 48538514Sdfr if (off - es->st_value < diff) { 48638514Sdfr diff = off - es->st_value; 48738514Sdfr best = es; 48838514Sdfr if (diff == 0) 48938514Sdfr break; 49038514Sdfr } else if (off - es->st_value == diff) { 49138514Sdfr best = es; 49238514Sdfr } 49338514Sdfr } 49438514Sdfr } 49538514Sdfr if (best == 0) 49638514Sdfr *diffp = off; 49738514Sdfr else 49838514Sdfr *diffp = diff; 49938514Sdfr *sym = (linker_sym_t) best; 50038514Sdfr 50138514Sdfr return 0; 50238514Sdfr} 50338514Sdfr 504