From e9eabf5983586a1e8468d8ba2e3307240dec90d0 Mon Sep 17 00:00:00 2001 From: Peter Wemm Date: Fri, 30 Apr 2004 16:32:40 +0000 Subject: [PATCH] Checkpoint commit for an alternative WIP kernel module loader that isn't as dependent on binutils features/quirks as the current one. This one loads plain .o files without having to mess with shared object mode. This happens to be essential on amd64, because binutils hasn't implemented all the quirks/features that we need for producing the hack non-PIC shared objects. As it turned out, .o format isn't all that inconvenient after all. It looks like the ability to use the same .o files for linking directly into a static kernel or loading as a module might be worth it. It is still very much a work-in-progress, but it is almost usable. Other changes are still needed in order to use it though, these have not been committed yet. There is still a memory corruption/overrun bug somewhere. For example, test modules load and work, but the machine explodes a few minutes later in vm_forkproc() or the like. Notable missing things include kldxref support, and loader(8) support. I wanted to figure out a working baseline set of code first. --- sys/kern/link_elf_obj.c | 1765 ++++++++++++++++----------------------- 1 file changed, 701 insertions(+), 1064 deletions(-) diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index f05cab162b1f..b84aecd76e2b 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 1998-2000 Doug Rabson + * Copyright (c) 2004 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,17 +45,12 @@ __FBSDID("$FreeBSD$"); #include #include -#ifdef GPROF -#include -#endif #include #include -#ifdef SPARSE_MAPPING #include #include #include -#endif #include #include @@ -62,1041 +58,703 @@ __FBSDID("$FreeBSD$"); #include "linker_if.h" +typedef struct { + void *addr; + Elf_Off fileoff; + Elf_Off filesz; + int align; + int flags; + int sec; /* Original section */ + char *name; +} Elf_progent; + +typedef struct { + void *addr; + Elf_Off memsz; + int align; + int flags; + int sec; /* Original section */ + char *name; +} Elf_nobitent; + +typedef struct { + Elf_Rel *rel; + Elf_Off fileoff; + Elf_Off filesz; + int sec; +} Elf_relent; + +typedef struct { + Elf_Rela *rela; + Elf_Off fileoff; + Elf_Off filesz; + int sec; +} Elf_relaent; + + typedef struct elf_file { - struct linker_file lf; /* Common fields */ - int preloaded; /* Was file pre-loaded */ - caddr_t address; /* Relocation address */ -#ifdef SPARSE_MAPPING - vm_object_t object; /* VM object to hold file pages */ -#endif - Elf_Dyn* dynamic; /* Symbol table etc. */ - Elf_Hashelt nbuckets; /* DT_HASH info */ - Elf_Hashelt nchains; - const Elf_Hashelt* buckets; - const Elf_Hashelt* chains; - caddr_t hash; - caddr_t strtab; /* DT_STRTAB */ - int strsz; /* DT_STRSZ */ - const Elf_Sym* symtab; /* DT_SYMTAB */ - Elf_Addr* got; /* DT_PLTGOT */ - const Elf_Rel* pltrel; /* DT_JMPREL */ - int pltrelsize; /* DT_PLTRELSZ */ - const Elf_Rela* pltrela; /* DT_JMPREL */ - int pltrelasize; /* DT_PLTRELSZ */ - const Elf_Rel* rel; /* DT_REL */ - int relsize; /* DT_RELSZ */ - const Elf_Rela* rela; /* DT_RELA */ - int relasize; /* DT_RELASZ */ - caddr_t modptr; - const Elf_Sym* ddbsymtab; /* The symbol table we are using */ - long ddbsymcnt; /* Number of symbols */ - caddr_t ddbstrtab; /* String table */ - long ddbstrcnt; /* number of bytes in string table */ - caddr_t symbase; /* malloc'ed symbold base */ - caddr_t strbase; /* malloc'ed string base */ -#ifdef DDB - struct link_map gdb; /* hooks for gdb */ -#endif + struct linker_file lf; /* Common fields */ + caddr_t address; /* Relocation address */ + vm_object_t object; /* VM object to hold file pages */ + Elf_Shdr *e_shdr; + + Elf_progent *progtab; + int nprogtab; + + Elf_nobitent *nobittab; + int nnobittab; + + Elf_relaent *relatab; + int nrela; + + Elf_relent *reltab; + int nrel; + + Elf_Sym *ddbsymtab; /* The symbol table we are using */ + long ddbsymcnt; /* Number of symbols */ + caddr_t ddbstrtab; /* String table */ + long ddbstrcnt; /* number of bytes in string table */ + + caddr_t shstrtab; /* Section name string table */ + long shstrcnt; /* number of bytes in string table */ + } *elf_file_t; -static int link_elf_link_common_finish(linker_file_t); static int link_elf_link_preload(linker_class_t cls, - const char*, linker_file_t*); + const char *, linker_file_t *); static int link_elf_link_preload_finish(linker_file_t); -static int link_elf_load_file(linker_class_t, const char*, linker_file_t*); -static int link_elf_lookup_symbol(linker_file_t, const char*, - c_linker_sym_t*); -static int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); +static int link_elf_load_file(linker_class_t, const char *, linker_file_t *); +static int link_elf_lookup_symbol(linker_file_t, const char *, + c_linker_sym_t *); +static int link_elf_symbol_values(linker_file_t, c_linker_sym_t, + linker_symval_t *); static int link_elf_search_symbol(linker_file_t, caddr_t value, - c_linker_sym_t* sym, long* diffp); + c_linker_sym_t *sym, long *diffp); static void link_elf_unload_file(linker_file_t); -static void link_elf_unload_preload(linker_file_t); static int link_elf_lookup_set(linker_file_t, const char *, - void ***, void ***, int *); + void ***, void ***, int *); static int link_elf_each_function_name(linker_file_t, - int (*)(const char *, void *), - void *); + int (*)(const char *, void *), void *); static void link_elf_reloc_local(linker_file_t); +static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Word symidx, int deps); + static kobj_method_t link_elf_methods[] = { - KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), - KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), - KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), - KOBJMETHOD(linker_unload, link_elf_unload_file), - KOBJMETHOD(linker_load_file, link_elf_load_file), - KOBJMETHOD(linker_link_preload, link_elf_link_preload), - KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), - KOBJMETHOD(linker_lookup_set, link_elf_lookup_set), - KOBJMETHOD(linker_each_function_name, link_elf_each_function_name), - { 0, 0 } + KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), + KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), + KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), + KOBJMETHOD(linker_unload, link_elf_unload_file), + KOBJMETHOD(linker_load_file, link_elf_load_file), + KOBJMETHOD(linker_link_preload, link_elf_link_preload), + KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), + KOBJMETHOD(linker_lookup_set, link_elf_lookup_set), + KOBJMETHOD(linker_each_function_name, link_elf_each_function_name), + { 0, 0 } }; static struct linker_class link_elf_class = { #if ELF_TARG_CLASS == ELFCLASS32 - "elf32", + "elf32_obj", #else - "elf64", + "elf64_obj", #endif - link_elf_methods, sizeof(struct elf_file) + link_elf_methods, sizeof(struct elf_file) }; -static int parse_dynamic(elf_file_t ef); -static int relocate_file(elf_file_t ef); -static int link_elf_preload_parse_symbols(elf_file_t ef); - -#ifdef DDB -static void r_debug_state(struct r_debug *dummy_one, - struct link_map *dummy_two); - -/* - * A list of loaded modules for GDB to use for loading symbols. - */ -struct r_debug r_debug; - -#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL); - -/* - * Function for the debugger to set a breakpoint on to gain control. - */ -static void -r_debug_state(struct r_debug *dummy_one __unused, - struct link_map *dummy_two __unused) -{ -} - -static void -link_elf_add_gdb(struct link_map *l) -{ - struct link_map *prev; - - l->l_next = NULL; - - if (r_debug.r_map == NULL) { - /* Add first. */ - l->l_prev = NULL; - r_debug.r_map = l; - } else { - /* Append to list. */ - for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) - ; - l->l_prev = prev; - prev->l_next = l; - } -} - -static void -link_elf_delete_gdb(struct link_map *l) -{ - if (l->l_prev == NULL) { - /* Remove first. */ - if ((r_debug.r_map = l->l_next) != NULL) - l->l_next->l_prev = NULL; - } else { - /* Remove any but first. */ - if ((l->l_prev->l_next = l->l_next) != NULL) - l->l_next->l_prev = l->l_prev; - } -} -#endif /* DDB */ - -#ifdef __ia64__ -Elf_Addr link_elf_get_gp(linker_file_t); -#endif - -/* - * The kernel symbol table starts here. - */ -extern struct _dynamic _DYNAMIC; +static int relocate_file(elf_file_t ef); static void link_elf_error(const char *s) { - printf("kldload: %s\n", s); -} - -/* - * Actions performed after linking/loading both the preloaded kernel and any - * modules; whether preloaded or dynamicly loaded. - */ -static int -link_elf_link_common_finish(linker_file_t lf) -{ -#ifdef DDB - elf_file_t ef = (elf_file_t)lf; - char *newfilename; -#endif - int error; - - /* Notify MD code that a module is being loaded. */ - error = elf_cpu_load_file(lf); - if (error) - return (error); - -#ifdef DDB - GDB_STATE(RT_ADD); - ef->gdb.l_addr = lf->address; - newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK); - strcpy(newfilename, lf->filename); - ef->gdb.l_name = newfilename; - ef->gdb.l_ld = ef->dynamic; - link_elf_add_gdb(&ef->gdb); - GDB_STATE(RT_CONSISTENT); -#endif - - return (0); + printf("kldload: %s\n", s); } static void -link_elf_init(void* arg) +link_elf_init(void *arg) { - Elf_Dyn *dp; - caddr_t modptr, baseptr, sizeptr; - elf_file_t ef; - char *modname; - linker_add_class(&link_elf_class); - - dp = (Elf_Dyn*) &_DYNAMIC; - modname = NULL; - modptr = preload_search_by_type("elf" __XSTRING(__ELF_WORD_SIZE) " kernel"); - if (modptr == NULL) - modptr = preload_search_by_type("elf kernel"); - if (modptr) - modname = (char *)preload_search_info(modptr, MODINFO_NAME); - if (modname == NULL) - modname = "kernel"; - linker_kernel_file = linker_make_file(modname, &link_elf_class); - if (linker_kernel_file == NULL) - panic("link_elf_init: Can't create linker structures for kernel"); - - ef = (elf_file_t) linker_kernel_file; - ef->preloaded = 1; - ef->address = 0; -#ifdef SPARSE_MAPPING - ef->object = 0; -#endif - ef->dynamic = dp; - - if (dp) - parse_dynamic(ef); - linker_kernel_file->address = (caddr_t) KERNBASE; - linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; - - if (modptr) { - ef->modptr = modptr; - baseptr = preload_search_info(modptr, MODINFO_ADDR); - if (baseptr) - linker_kernel_file->address = *(caddr_t *)baseptr; - sizeptr = preload_search_info(modptr, MODINFO_SIZE); - if (sizeptr) - linker_kernel_file->size = *(size_t *)sizeptr; - } - (void)link_elf_preload_parse_symbols(ef); - -#ifdef DDB - r_debug.r_map = NULL; - r_debug.r_brk = r_debug_state; - r_debug.r_state = RT_CONSISTENT; -#endif - - (void)link_elf_link_common_finish(linker_kernel_file); + linker_add_class(&link_elf_class); } -SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); +SYSINIT(link_elf_obj, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); static int -link_elf_preload_parse_symbols(elf_file_t ef) +link_elf_link_preload(linker_class_t cls, const char *filename, + linker_file_t *result) { - caddr_t pointer; - caddr_t ssym, esym, base; - caddr_t strtab; - int strcnt; - Elf_Sym* symtab; - int symcnt; - - if (ef->modptr == NULL) - return 0; - pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); - if (pointer == NULL) - return 0; - ssym = *(caddr_t *)pointer; - pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); - if (pointer == NULL) - return 0; - esym = *(caddr_t *)pointer; - - base = ssym; - - symcnt = *(long *)base; - base += sizeof(long); - symtab = (Elf_Sym *)base; - base += roundup(symcnt, sizeof(long)); - - if (base > esym || base < ssym) { - printf("Symbols are corrupt!\n"); - return EINVAL; - } - - strcnt = *(long *)base; - base += sizeof(long); - strtab = base; - base += roundup(strcnt, sizeof(long)); - - if (base > esym || base < ssym) { - printf("Symbols are corrupt!\n"); - return EINVAL; - } - - ef->ddbsymtab = symtab; - ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); - ef->ddbstrtab = strtab; - ef->ddbstrcnt = strcnt; - - return 0; -} - -static int -parse_dynamic(elf_file_t ef) -{ - Elf_Dyn *dp; - int plttype = DT_REL; - - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - switch (dp->d_tag) { - case DT_HASH: - { - /* From src/libexec/rtld-elf/rtld.c */ - const Elf_Hashelt *hashtab = (const Elf_Hashelt *) - (ef->address + dp->d_un.d_ptr); - ef->nbuckets = hashtab[0]; - ef->nchains = hashtab[1]; - ef->buckets = hashtab + 2; - ef->chains = ef->buckets + ef->nbuckets; - break; - } - case DT_STRTAB: - ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); - break; - case DT_STRSZ: - ef->strsz = dp->d_un.d_val; - break; - case DT_SYMTAB: - ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); - break; - case DT_SYMENT: - if (dp->d_un.d_val != sizeof(Elf_Sym)) - return ENOEXEC; - break; - case DT_PLTGOT: - ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); - break; - case DT_REL: - ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); - break; - case DT_RELSZ: - ef->relsize = dp->d_un.d_val; - break; - case DT_RELENT: - if (dp->d_un.d_val != sizeof(Elf_Rel)) - return ENOEXEC; - break; - case DT_JMPREL: - ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); - break; - case DT_PLTRELSZ: - ef->pltrelsize = dp->d_un.d_val; - break; - case DT_RELA: - ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); - break; - case DT_RELASZ: - ef->relasize = dp->d_un.d_val; - break; - case DT_RELAENT: - if (dp->d_un.d_val != sizeof(Elf_Rela)) - return ENOEXEC; - break; - case DT_PLTREL: - plttype = dp->d_un.d_val; - if (plttype != DT_REL && plttype != DT_RELA) - return ENOEXEC; - break; -#ifdef DDB - case DT_DEBUG: - dp->d_un.d_ptr = (Elf_Addr) &r_debug; - break; -#endif - } - } - - if (plttype == DT_RELA) { - ef->pltrela = (const Elf_Rela *) ef->pltrel; - ef->pltrel = NULL; - ef->pltrelasize = ef->pltrelsize; - ef->pltrelsize = 0; - } - - ef->ddbsymtab = ef->symtab; - ef->ddbsymcnt = ef->nchains; - ef->ddbstrtab = ef->strtab; - ef->ddbstrcnt = ef->strsz; - - return 0; -} - -static int -link_elf_link_preload(linker_class_t cls, - const char* filename, linker_file_t *result) -{ - caddr_t modptr, baseptr, sizeptr, dynptr; - char *type; - elf_file_t ef; - linker_file_t lf; - int error; - vm_offset_t dp; - - /* Look to see if we have the file preloaded */ - modptr = preload_search_by_name(filename); - if (modptr == NULL) - return ENOENT; - - type = (char *)preload_search_info(modptr, MODINFO_TYPE); - baseptr = preload_search_info(modptr, MODINFO_ADDR); - sizeptr = preload_search_info(modptr, MODINFO_SIZE); - dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); - if (type == NULL || - (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " module") != 0 && - strcmp(type, "elf module") != 0)) + /* preload not done this way */ return (EFTYPE); - if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) - return (EINVAL); - - lf = linker_make_file(filename, &link_elf_class); - if (lf == NULL) { - return ENOMEM; - } - - ef = (elf_file_t) lf; - ef->preloaded = 1; - ef->modptr = modptr; - ef->address = *(caddr_t *)baseptr; -#ifdef SPARSE_MAPPING - ef->object = 0; -#endif - dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; - ef->dynamic = (Elf_Dyn *)dp; - lf->address = ef->address; - lf->size = *(size_t *)sizeptr; - - error = parse_dynamic(ef); - if (error) { - linker_file_unload(lf); - return error; - } - link_elf_reloc_local(lf); - *result = lf; - return (0); } static int link_elf_link_preload_finish(linker_file_t lf) { - elf_file_t ef; - int error; - - ef = (elf_file_t) lf; -#if 0 /* this will be more trouble than it's worth for now */ - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - if (dp->d_tag != DT_NEEDED) - continue; - modname = ef->strtab + dp->d_un.d_val; - error = linker_load_module(modname, lf); - if (error) - goto out; - } -#endif - error = relocate_file(ef); - if (error) - return error; - (void)link_elf_preload_parse_symbols(ef); - - return (link_elf_link_common_finish(lf)); + /* preload not done this way */ + return (EFTYPE); } static int -link_elf_load_file(linker_class_t cls, const char* filename, - linker_file_t* result) +link_elf_load_file(linker_class_t cls, const char *filename, + linker_file_t *result) { - struct nameidata nd; - struct thread* td = curthread; /* XXX */ - Elf_Ehdr *hdr; - caddr_t firstpage; - int nbytes, i; - Elf_Phdr *phdr; - Elf_Phdr *phlimit; - Elf_Phdr *segs[2]; - int nsegs; - Elf_Phdr *phdyn; - Elf_Phdr *phphdr; - caddr_t mapbase; - size_t mapsize; - Elf_Off base_offset; - Elf_Addr base_vaddr; - Elf_Addr base_vlimit; - int error = 0; - int resid, flags; - elf_file_t ef; - linker_file_t lf; - Elf_Shdr *shdr; - int symtabindex; - int symstrindex; - int symcnt; - int strcnt; + struct nameidata nd; + struct thread *td = curthread; /* XXX */ + Elf_Ehdr *hdr; + Elf_Shdr *shdr; + int nbytes, i; + caddr_t mapbase; + size_t mapsize; + int error = 0; + int resid, flags; + elf_file_t ef; + linker_file_t lf; + int symtabindex; + int symstrindex; + int shstrindex; + int nsym; + int pb, nb, rl, ra; + int alignmask; - GIANT_REQUIRED; + GIANT_REQUIRED; - shdr = NULL; - lf = NULL; + shdr = NULL; + lf = NULL; + mapsize = 0; + hdr = NULL; - NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); - flags = FREAD; - error = vn_open(&nd, &flags, 0, -1); - if (error) - return error; - NDFREE(&nd, NDF_ONLY_PNBUF); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); + flags = FREAD; + error = vn_open(&nd, &flags, 0, -1); + if (error) + return error; + NDFREE(&nd, NDF_ONLY_PNBUF); #ifdef MAC - error = mac_check_kld_load(curthread->td_ucred, nd.ni_vp); - if (error) { - firstpage = NULL; - goto out; - } + error = mac_check_kld_load(td->td_ucred, nd.ni_vp); + if (error) { + goto out; + } #endif - /* - * Read the elf header from the file. - */ - firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); - if (firstpage == NULL) { - error = ENOMEM; - goto out; - } - hdr = (Elf_Ehdr *)firstpage; - error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); - nbytes = PAGE_SIZE - resid; - if (error) - goto out; - - if (!IS_ELF(*hdr)) { - error = ENOEXEC; - goto out; - } - - if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS - || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { - link_elf_error("Unsupported file layout"); - error = ENOEXEC; - goto out; - } - if (hdr->e_ident[EI_VERSION] != EV_CURRENT - || hdr->e_version != EV_CURRENT) { - link_elf_error("Unsupported file version"); - error = ENOEXEC; - goto out; - } - if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { - link_elf_error("Unsupported file type"); - error = ENOEXEC; - goto out; - } - if (hdr->e_machine != ELF_TARG_MACH) { - link_elf_error("Unsupported machine"); - error = ENOEXEC; - goto out; - } - - /* - * We rely on the program header being in the first page. This is - * not strictly required by the ABI specification, but it seems to - * always true in practice. And, it simplifies things considerably. - */ - if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && - (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && - (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) - link_elf_error("Unreadable program headers"); - - /* - * Scan the program header entries, and save key information. - * - * We rely on there being exactly two load segments, text and data, - * in that order. - */ - phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); - phlimit = phdr + hdr->e_phnum; - nsegs = 0; - phdyn = NULL; - phphdr = NULL; - while (phdr < phlimit) { - switch (phdr->p_type) { - - case PT_LOAD: - if (nsegs == 2) { - link_elf_error("Too many sections"); + /* Read the elf header from the file. */ + hdr = malloc(sizeof(*hdr), M_LINKER, M_WAITOK); + if (hdr == NULL) { + error = ENOMEM; + goto out; + } + error = vn_rdwr(UIO_READ, nd.ni_vp, (void *)hdr, sizeof(*hdr), 0, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + if (resid != 0){ error = ENOEXEC; goto out; - } - /* - * XXX: We just trust they come in right order ?? - */ - segs[nsegs] = phdr; - ++nsegs; - break; - - case PT_PHDR: - phphdr = phdr; - break; - - case PT_DYNAMIC: - phdyn = phdr; - break; - - case PT_INTERP: - link_elf_error("Unsupported file type"); - error = ENOEXEC; - goto out; } - ++phdr; - } - if (phdyn == NULL) { - link_elf_error("Object is not dynamically-linked"); - error = ENOEXEC; - goto out; - } - if (nsegs != 2) { - link_elf_error("Too few sections"); - error = ENOEXEC; - goto out; - } - - /* - * Allocate the entire address space of the object, to stake out our - * contiguous region, and to establish the base address for relocation. - */ - base_offset = trunc_page(segs[0]->p_offset); - base_vaddr = trunc_page(segs[0]->p_vaddr); - base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); - mapsize = base_vlimit - base_vaddr; - - lf = linker_make_file(filename, &link_elf_class); - if (!lf) { - error = ENOMEM; - goto out; - } - - ef = (elf_file_t) lf; -#ifdef SPARSE_MAPPING - ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); - if (ef->object == NULL) { - error = ENOMEM; - goto out; - } - vm_object_reference(ef->object); - ef->address = (caddr_t) vm_map_min(kernel_map); - error = vm_map_find(kernel_map, ef->object, 0, - (vm_offset_t *) &ef->address, - mapsize, 1, - VM_PROT_ALL, VM_PROT_ALL, 0); - if (error) { - vm_object_deallocate(ef->object); - ef->object = 0; - goto out; - } -#else - ef->address = malloc(mapsize, M_LINKER, M_WAITOK); - if (!ef->address) { - error = ENOMEM; - goto out; - } -#endif - mapbase = ef->address; - - /* - * Read the text and data sections and zero the bss. - */ - for (i = 0; i < 2; i++) { - caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; - error = vn_rdwr(UIO_READ, nd.ni_vp, - segbase, segs[i]->p_filesz, segs[i]->p_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); - if (error) { - goto out; + if (!IS_ELF(*hdr)) { + error = ENOEXEC; + goto out; } - bzero(segbase + segs[i]->p_filesz, - segs[i]->p_memsz - segs[i]->p_filesz); -#ifdef SPARSE_MAPPING - /* - * Wire down the pages - */ - vm_map_wire(kernel_map, - (vm_offset_t) segbase, - (vm_offset_t) segbase + segs[i]->p_memsz, - VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES); -#endif - } + if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS + || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { + link_elf_error("Unsupported file layout"); + error = ENOEXEC; + goto out; + } + if (hdr->e_ident[EI_VERSION] != EV_CURRENT + || hdr->e_version != EV_CURRENT) { + link_elf_error("Unsupported file version"); + error = ENOEXEC; + goto out; + } + if (hdr->e_type != ET_REL) { + link_elf_error("Unsupported file type"); + error = ENOEXEC; + goto out; + } + if (hdr->e_machine != ELF_TARG_MACH) { + link_elf_error("Unsupported machine"); + error = ENOEXEC; + goto out; + } -#ifdef GPROF - /* Update profiling information with the new text segment. */ - kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr + - segs[0]->p_memsz)); -#endif + lf = linker_make_file(filename, &link_elf_class); + if (!lf) { + error = ENOMEM; + goto out; + } + ef = (elf_file_t) lf; + ef->nprogtab = 0; + ef->nnobittab = 0; + ef->e_shdr = 0; + ef->nrel = 0; + ef->nrela = 0; - ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); - - lf->address = ef->address; - lf->size = mapsize; - - error = parse_dynamic(ef); - if (error) - goto out; - link_elf_reloc_local(lf); - - error = linker_load_dependencies(lf); - if (error) - goto out; -#if 0 /* this will be more trouble than it's worth for now */ - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - if (dp->d_tag != DT_NEEDED) - continue; - modname = ef->strtab + dp->d_un.d_val; - error = linker_load_module(modname, lf); + /* Allocate and read in the section header */ + nbytes = hdr->e_shnum * hdr->e_shentsize; + if (nbytes == 0 || hdr->e_shoff == 0 || + hdr->e_shentsize != sizeof(Elf_Shdr)) { + error = ENOEXEC; + goto out; + } + shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); + if (shdr == NULL) { + error = ENOMEM; + goto out; + } + ef->e_shdr = shdr; + error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)shdr, nbytes, hdr->e_shoff, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td); if (error) - goto out; - } -#endif - error = relocate_file(ef); - if (error) - goto out; - - /* Try and load the symbol table if it's present. (you can strip it!) */ - nbytes = hdr->e_shnum * hdr->e_shentsize; - if (nbytes == 0 || hdr->e_shoff == 0) - goto nosyms; - shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); - if (shdr == NULL) { - error = ENOMEM; - goto out; - } - error = vn_rdwr(UIO_READ, nd.ni_vp, - (caddr_t)shdr, nbytes, hdr->e_shoff, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); - if (error) - goto out; - symtabindex = -1; - symstrindex = -1; - for (i = 0; i < hdr->e_shnum; i++) { - if (shdr[i].sh_type == SHT_SYMTAB) { - symtabindex = i; - symstrindex = shdr[i].sh_link; + goto out; + if (resid) { + error = ENOEXEC; + goto out; } - } - if (symtabindex < 0 || symstrindex < 0) - goto nosyms; - symcnt = shdr[symtabindex].sh_size; - ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); - strcnt = shdr[symstrindex].sh_size; - ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); + /* Scan the section header for information and table sizing. */ + nsym = 0; + symtabindex = -1; + symstrindex = -1; + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + ef->nprogtab++; + break; + case SHT_NOBITS: + ef->nnobittab++; + break; + case SHT_SYMTAB: + nsym++; + symtabindex = i; + symstrindex = shdr[i].sh_link; + break; + case SHT_REL: + ef->nrel++; + break; + case SHT_RELA: + ef->nrela++; + break; + case SHT_STRTAB: + break; + } + } + if (ef->nprogtab == 0 && ef->nnobittab == 0) { + link_elf_error("file has no contents"); + error = ENOEXEC; + goto out; + } + if (nsym != 1) { + /* Only allow one symbol table for now */ + link_elf_error("file has no valid symbol table"); + error = ENOEXEC; + goto out; + } + if (symstrindex < 0 || symstrindex > hdr->e_shnum || + shdr[symstrindex].sh_type != SHT_STRTAB) { + link_elf_error("file has invalid symbol strings"); + error = ENOEXEC; + goto out; + } - if (ef->symbase == NULL || ef->strbase == NULL) { - error = ENOMEM; - goto out; - } - error = vn_rdwr(UIO_READ, nd.ni_vp, - ef->symbase, symcnt, shdr[symtabindex].sh_offset, + /* Allocate space for tracking the load chunks */ + /* XXX - maybe unneeded. might be able to use the shdr directly */ + if (ef->nprogtab != 0) + ef->progtab = malloc(ef->nprogtab * sizeof(*ef->progtab), M_LINKER, M_WAITOK); + if (ef->nnobittab != 0) + ef->nobittab = malloc(ef->nnobittab * sizeof(*ef->nobittab), M_LINKER, M_WAITOK); + if (ef->nrel != 0) + ef->reltab = malloc(ef->nrel * sizeof(*ef->reltab), M_LINKER, M_WAITOK); + if (ef->nrela != 0) + ef->relatab = malloc(ef->nrela * sizeof(*ef->relatab), M_LINKER, M_WAITOK); + /* XXX check for failures */ + + /* Space for symbol table */ + ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym); + ef->ddbsymtab = malloc(shdr[symtabindex].sh_size, M_LINKER, M_WAITOK); + + ef->ddbstrcnt = shdr[symstrindex].sh_size; + ef->ddbstrtab = malloc(shdr[symstrindex].sh_size, M_LINKER, M_WAITOK); + + if (ef->ddbsymtab == NULL || ef->ddbstrtab == NULL) { + error = ENOMEM; + goto out; + } + + /* Do we have a string table for the section names? */ + shstrindex = -1; + if (hdr->e_shstrndx != 0 && shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { + shstrindex = hdr->e_shstrndx; + ef->shstrcnt = shdr[shstrindex].sh_size; + ef->shstrtab = malloc(shdr[shstrindex].sh_size, M_LINKER, M_WAITOK); + if (ef->shstrtab == NULL) { + error = ENOMEM; + goto out; + } + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->shstrtab, shdr[shstrindex].sh_size, shdr[shstrindex].sh_offset, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td); - if (error) - goto out; - error = vn_rdwr(UIO_READ, nd.ni_vp, - ef->strbase, strcnt, shdr[symstrindex].sh_offset, + if (error) + goto out; + } + + /* Size code/data(progbits) and bss(nobits). allocate space for relocs */ + pb = 0; + nb = 0; + rl = 0; + ra = 0; + alignmask = 0; + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + case SHT_NOBITS: + alignmask = shdr[i].sh_addralign - 1; + mapsize += alignmask; + mapsize &= ~alignmask; + break; + } + + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + ef->progtab[pb].addr = (void *)(uintptr_t)mapsize; + ef->progtab[pb].fileoff = shdr[i].sh_offset; + ef->progtab[pb].filesz = shdr[i].sh_size; + ef->progtab[pb].align = shdr[i].sh_addralign; + ef->progtab[pb].sec = i; + if (ef->shstrtab && shdr[i].sh_name != 0) + ef->progtab[pb].name = ef->shstrtab + shdr[i].sh_name; + else + ef->progtab[pb].name = "<>"; + mapsize += shdr[i].sh_size; + pb++; + break; + case SHT_NOBITS: + ef->nobittab[nb].addr = (void *)(uintptr_t)mapsize; + ef->nobittab[nb].memsz = shdr[i].sh_size; + ef->nobittab[nb].align = shdr[i].sh_addralign; + ef->nobittab[nb].sec = i; + if (ef->shstrtab && shdr[i].sh_name != 0) + ef->nobittab[nb].name = ef->shstrtab + shdr[i].sh_name; + else + ef->nobittab[nb].name = "<>"; + mapsize += shdr[i].sh_size; + nb++; + break; + case SHT_REL: + ef->reltab[rl].rel = malloc(shdr[i].sh_size, M_LINKER, M_WAITOK); + ef->reltab[rl].fileoff = shdr[i].sh_offset; + ef->reltab[rl].filesz = shdr[i].sh_size; + ef->reltab[rl].sec = shdr[i].sh_info; + rl++; + break; + case SHT_RELA: + ef->relatab[ra].rela = malloc(shdr[i].sh_size, M_LINKER, M_WAITOK); + ef->relatab[ra].fileoff = shdr[i].sh_offset; + ef->relatab[ra].filesz = shdr[i].sh_size; + ef->relatab[ra].sec = shdr[i].sh_info; + ra++; + break; + } + } + if (pb != ef->nprogtab) + panic("lots progbits"); + if (nb != ef->nnobittab) + panic("lots nobits"); + if (rl != ef->nrel) + panic("lots rel"); + if (ra != ef->nrela) + panic("lots rela"); + + /* + * We know how much space we need for the text/data/bss/etc. + * This stuff needs to be in a single chunk so that profiling etc + * can get the bounds and gdb can associate offsets with modules + */ + ef->object = vm_object_allocate(OBJT_DEFAULT, round_page(mapsize) >> PAGE_SHIFT); + if (ef->object == NULL) { + error = ENOMEM; + goto out; + } + vm_object_reference(ef->object); + ef->address = (caddr_t) vm_map_min(kernel_map); + error = vm_map_find(kernel_map, ef->object, 0, + (vm_offset_t *) &ef->address, mapsize, TRUE, VM_PROT_ALL, VM_PROT_ALL, FALSE); + if (error) { + vm_object_deallocate(ef->object); + ef->object = 0; + goto out; + } + mapbase = ef->address; + /* Wire the pages */ + vm_map_wire(kernel_map, (vm_offset_t)mapbase, + (vm_offset_t)mapbase + round_page(mapsize), + VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES); + + /* Add the base address to the previously calculated/aligned offsets */ + for (i = 0; i < ef->nprogtab; i++) + ef->progtab[i].addr = mapbase + (uintptr_t)ef->progtab[i].addr; + + for (i = 0; i < ef->nnobittab; i++) + ef->nobittab[i].addr = mapbase + (uintptr_t)ef->nobittab[i].addr; + + + /* Load the symbol table. */ + error = vn_rdwr(UIO_READ, nd.ni_vp, + (void *)ef->ddbsymtab, shdr[symtabindex].sh_size, shdr[symtabindex].sh_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->ddbstrtab, shdr[symstrindex].sh_size, shdr[symstrindex].sh_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + + /* Read in the text/data/set/etc sections */ + for (i = 0; i < ef->nprogtab; i++) { + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->progtab[i].addr, + ef->progtab[i].filesz, + ef->progtab[i].fileoff, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td); - if (error) - goto out; + if (error) + goto out; + } - ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); - ef->ddbsymtab = (const Elf_Sym *)ef->symbase; - ef->ddbstrcnt = strcnt; - ef->ddbstrtab = ef->strbase; + /* + * Read in relocation tables. Platforms use rel or rela, but + * usually not both. + */ + for (i = 0; i < ef->nrel; i++) { + error = vn_rdwr(UIO_READ, nd.ni_vp, + (void *)ef->reltab[i].rel, + ef->reltab[i].filesz, + ef->reltab[i].fileoff, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + } + for (i = 0; i < ef->nrela; i++) { + error = vn_rdwr(UIO_READ, nd.ni_vp, + (void *)ef->relatab[i].rela, + ef->relatab[i].filesz, + ef->relatab[i].fileoff, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + } - error = link_elf_link_common_finish(lf); - if (error) - goto out; + /* Inform the kld system about the situation */ + lf->address = ef->address = mapbase; + lf->size = mapsize; -nosyms: + /* Local intra-module relocations */ + link_elf_reloc_local(lf); - *result = lf; + /* Pull in dependencies */ + error = linker_load_dependencies(lf); + if (error) + goto out; + + /* External relocations */ + error = relocate_file(ef); + if (error) + goto out; + + /* Notify MD code that a module is being loaded. */ + error = elf_cpu_load_file(lf); + if (error) + goto out; + + *result = lf; out: - if (error && lf) - linker_file_unload(lf); - if (shdr) - free(shdr, M_LINKER); - if (firstpage) - free(firstpage, M_LINKER); - VOP_UNLOCK(nd.ni_vp, 0, td); - vn_close(nd.ni_vp, FREAD, td->td_ucred, td); + if (error && lf) + linker_file_unload(lf); + if (hdr) + free(hdr, M_LINKER); + VOP_UNLOCK(nd.ni_vp, 0, td); + vn_close(nd.ni_vp, FREAD, td->td_ucred, td); - return error; + return error; } static void link_elf_unload_file(linker_file_t file) { - elf_file_t ef = (elf_file_t) file; + elf_file_t ef = (elf_file_t) file; -#ifdef DDB - if (ef->gdb.l_ld) { - GDB_STATE(RT_DELETE); - free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER); - link_elf_delete_gdb(&ef->gdb); - GDB_STATE(RT_CONSISTENT); - } -#endif + /* Notify MD code that a module is being unloaded. */ + elf_cpu_unload_file(file); - /* Notify MD code that a module is being unloaded. */ - elf_cpu_unload_file(file); - - if (ef->preloaded) { - link_elf_unload_preload(file); - return; - } - -#ifdef SPARSE_MAPPING - if (ef->object) { - vm_map_remove(kernel_map, (vm_offset_t) ef->address, - (vm_offset_t) ef->address - + (ef->object->size << PAGE_SHIFT)); - vm_object_deallocate(ef->object); - } -#else - if (ef->address) - free(ef->address, M_LINKER); -#endif - if (ef->symbase) - free(ef->symbase, M_LINKER); - if (ef->strbase) - free(ef->strbase, M_LINKER); -} - -static void -link_elf_unload_preload(linker_file_t file) -{ - if (file->filename) - preload_delete_name(file->filename); + if (ef->object) { + vm_map_remove(kernel_map, (vm_offset_t) ef->address, + (vm_offset_t) ef->address + (ef->object->size << PAGE_SHIFT)); + vm_object_deallocate(ef->object); + } + if (ef->e_shdr) + free(ef->e_shdr, M_LINKER); + if (ef->ddbsymtab) + free(ef->ddbsymtab, M_LINKER); + if (ef->ddbstrtab) + free(ef->ddbstrtab, M_LINKER); } static const char * symbol_name(elf_file_t ef, Elf_Word r_info) { - const Elf_Sym *ref; + const Elf_Sym *ref; - if (ELF_R_SYM(r_info)) { - ref = ef->symtab + ELF_R_SYM(r_info); - return ef->strtab + ref->st_name; - } else - return NULL; + if (ELF_R_SYM(r_info)) { + ref = ef->ddbsymtab + ELF_R_SYM(r_info); + return ef->ddbstrtab + ref->st_name; + } else + return NULL; +} + +static Elf_Addr +findbase(elf_file_t ef, int sec) +{ + int i; + Elf_Addr base = 0; + + for (i = 0; i < ef->nprogtab; i++) { + if (sec == ef->progtab[i].sec) + base = (Elf_Addr)ef->progtab[i].addr; + } + if (base == 0) { + for (i = 0; i < ef->nnobittab; i++) { + if (sec == ef->nobittab[i].sec) + base = (Elf_Addr)ef->nobittab[i].addr; + } + } + if (base == 0) + base = (Elf_Addr)ef->address; + return base; } static int relocate_file(elf_file_t ef) { - const Elf_Rel *rellim; - const Elf_Rel *rel; - const Elf_Rela *relalim; - const Elf_Rela *rela; - const char *symname; + const Elf_Rel *rellim; + const Elf_Rel *rel; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const char *symname; + const Elf_Sym *sym; + int i; + Elf_Word symidx; + Elf_Addr base; - /* Perform relocations without addend if there are any: */ - rel = ef->rel; - if (rel) { - rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); - while (rel < rellim) { - if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) { - symname = symbol_name(ef, rel->r_info); - printf("link_elf: symbol %s undefined\n", symname); - return ENOENT; - } - rel++; + + /* Perform relocations without addend if there are any: */ + for (i = 0; i < ef->nrel; i++) { + rel = ef->reltab[i].rel; + if (rel) { + rellim = (const Elf_Rel *)((const char *)rel + ef->reltab[i].filesz); + base = findbase(ef, ef->reltab[i].sec); + while (rel < rellim) { + symidx = ELF_R_SYM(rel->r_info); + if (symidx < ef->ddbsymcnt) { + sym = ef->ddbsymtab + symidx; + if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) { + if (elf_reloc(&ef->lf, base, rel, ELF_RELOC_REL, elf_obj_lookup)) { + symname = symbol_name(ef, rel->r_info); + printf("link_elf: symbol %s undefined\n", symname); + return ENOENT; + } + } + } + rel++; + } + } } - } - /* Perform relocations with addend if there are any: */ - rela = ef->rela; - if (rela) { - relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); - while (rela < relalim) { - if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) { - symname = symbol_name(ef, rela->r_info); - printf("link_elf: symbol %s undefined\n", symname); - return ENOENT; - } - rela++; + /* Perform relocations with addend if there are any: */ + for (i = 0; i < ef->nrela; i++) { + rela = ef->relatab[i].rela; + if (rela) { + relalim = (const Elf_Rela *)((const char *)rela + ef->relatab[i].filesz); + base = findbase(ef, ef->relatab[i].sec); + while (rela < relalim) { + symidx = ELF_R_SYM(rela->r_info); + if (symidx < ef->ddbsymcnt) { + sym = ef->ddbsymtab + symidx; + if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) { + if (elf_reloc(&ef->lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup)) { + symname = symbol_name(ef, rela->r_info); + printf("link_elf: symbol %s undefined\n", symname); + return ENOENT; + } + } + } + rela++; + } + } } - } - /* Perform PLT relocations without addend if there are any: */ - rel = ef->pltrel; - if (rel) { - rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); - while (rel < rellim) { - if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) { - symname = symbol_name(ef, rel->r_info); - printf("link_elf: symbol %s undefined\n", symname); - return ENOENT; - } - rel++; - } - } - - /* Perform relocations with addend if there are any: */ - rela = ef->pltrela; - if (rela) { - relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); - while (rela < relalim) { - if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) { - symname = symbol_name(ef, rela->r_info); - printf("link_elf: symbol %s undefined\n", symname); - return ENOENT; - } - rela++; - } - } - - return 0; -} - -/* - * Hash function for symbol table lookup. Don't even think about changing - * this. It is specified by the System V ABI. - */ -static unsigned long -elf_hash(const char *name) -{ - const unsigned char *p = (const unsigned char *) name; - unsigned long h = 0; - unsigned long g; - - while (*p != '\0') { - h = (h << 4) + *p++; - if ((g = h & 0xf0000000) != 0) - h ^= g >> 24; - h &= ~g; - } - return h; + return 0; } static int -link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) -{ - elf_file_t ef = (elf_file_t) lf; - unsigned long symnum; - const Elf_Sym* symp; - const char *strp; - unsigned long hash; - int i; - - /* First, search hashed global symbols */ - hash = elf_hash(name); - symnum = ef->buckets[hash % ef->nbuckets]; - - while (symnum != STN_UNDEF) { - if (symnum >= ef->nchains) { - printf("link_elf_lookup_symbol: corrupt symbol table\n"); - return ENOENT; - } - - symp = ef->symtab + symnum; - if (symp->st_name == 0) { - printf("link_elf_lookup_symbol: corrupt symbol table\n"); - return ENOENT; - } - - strp = ef->strtab + symp->st_name; - - if (strcmp(name, strp) == 0) { - if (symp->st_shndx != SHN_UNDEF || - (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { - *sym = (c_linker_sym_t) symp; - return 0; - } else - return ENOENT; - } - - symnum = ef->chains[symnum]; - } - - /* If we have not found it, look at the full table (if loaded) */ - if (ef->symtab == ef->ddbsymtab) - return ENOENT; - - /* Exhaustive search */ - for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { - strp = ef->ddbstrtab + symp->st_name; - if (strcmp(name, strp) == 0) { - if (symp->st_shndx != SHN_UNDEF || - (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { - *sym = (c_linker_sym_t) symp; - return 0; - } else - return ENOENT; - } - } - - return ENOENT; -} - -static int -link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) +link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym) { elf_file_t ef = (elf_file_t) lf; - const Elf_Sym* es = (const Elf_Sym*) sym; + const Elf_Sym *symp; + const char *strp; + int i; - if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { - symval->name = ef->strtab + es->st_name; - symval->value = (caddr_t) ef->address + es->st_value; - symval->size = es->st_size; - return 0; +/* XXX search for globals first */ + /* Exhaustive search */ + for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { + strp = ef->ddbstrtab + symp->st_name; + if (strcmp(name, strp) == 0) { + if (symp->st_shndx != SHN_UNDEF || + (symp->st_value != 0 && + ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { + *sym = (c_linker_sym_t) symp; + return 0; + } else + return ENOENT; + } } - if (ef->symtab == ef->ddbsymtab) - return ENOENT; + + return ENOENT; +} + +static int +link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t *symval) +{ + elf_file_t ef = (elf_file_t) lf; + const Elf_Sym *es = (const Elf_Sym*) sym; + if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { - symval->name = ef->ddbstrtab + es->st_name; - symval->value = (caddr_t) ef->address + es->st_value; - symval->size = es->st_size; - return 0; + symval->name = ef->ddbstrtab + es->st_name; + symval->value = (caddr_t) ef->address + es->st_value; + symval->size = es->st_size; + return 0; } return ENOENT; } static int link_elf_search_symbol(linker_file_t lf, caddr_t value, - c_linker_sym_t* sym, long* diffp) + c_linker_sym_t *sym, long *diffp) { elf_file_t ef = (elf_file_t) lf; u_long off = (uintptr_t) (void *) value; u_long diff = off; u_long st_value; - const Elf_Sym* es; - const Elf_Sym* best = 0; + const Elf_Sym *es; + const Elf_Sym *best = 0; int i; for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { @@ -1128,113 +786,49 @@ link_elf_search_symbol(linker_file_t lf, caddr_t value, */ static int link_elf_lookup_set(linker_file_t lf, const char *name, - void ***startp, void ***stopp, int *countp) + void ***startp, void ***stopp, int *countp) { - c_linker_sym_t sym; - linker_symval_t symval; - char *setsym; + elf_file_t ef = (elf_file_t)lf; void **start, **stop; - int len, error = 0, count; + int i, count; - len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ - setsym = malloc(len, M_LINKER, M_WAITOK); - if (setsym == NULL) - return ENOMEM; - - /* get address of first entry */ - snprintf(setsym, len, "%s%s", "__start_set_", name); - error = link_elf_lookup_symbol(lf, setsym, &sym); - if (error) - goto out; - link_elf_symbol_values(lf, sym, &symval); - if (symval.value == 0) { - error = ESRCH; - goto out; + /* Relative to section number */ + for (i = 0; i < ef->nprogtab; i++) { + if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) && + strcmp(ef->progtab[i].name + 4, name) == 0) { + start = (void **)ef->progtab[i].addr; + stop = (void **)((char *)ef->progtab[i].addr + ef->progtab[i].filesz); + count = stop - start; + if (startp) + *startp = start; + if (stopp) + *stopp = stop; + if (countp) + *countp = count; + return (0); + } } - start = (void **)symval.value; - - /* get address of last entry */ - snprintf(setsym, len, "%s%s", "__stop_set_", name); - error = link_elf_lookup_symbol(lf, setsym, &sym); - if (error) - goto out; - link_elf_symbol_values(lf, sym, &symval); - if (symval.value == 0) { - error = ESRCH; - goto out; - } - stop = (void **)symval.value; - - /* and the number of entries */ - count = stop - start; - - /* and copy out */ - if (startp) - *startp = start; - if (stopp) - *stopp = stop; - if (countp) - *countp = count; - -out: - free(setsym, M_LINKER); - return error; + return (ESRCH); } static int link_elf_each_function_name(linker_file_t file, - int (*callback)(const char *, void *), void *opaque) { - elf_file_t ef = (elf_file_t)file; - const Elf_Sym* symp; - int i, error; + int (*callback)(const char *, void *), void *opaque) +{ + elf_file_t ef = (elf_file_t)file; + const Elf_Sym *symp; + int i, error; - /* Exhaustive search */ - for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { - if (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC) { - error = callback(ef->ddbstrtab + symp->st_name, opaque); - if (error) - return (error); + /* Exhaustive search */ + for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { + if (symp->st_value != 0 && + ELF_ST_TYPE(symp->st_info) == STT_FUNC) { + error = callback(ef->ddbstrtab + symp->st_name, opaque); + if (error) + return (error); + } } - } - return (0); -} - -#ifdef __ia64__ -/* - * Each KLD has its own GP. The GP value for each load module is given by - * DT_PLTGOT on ia64. We need GP to construct function descriptors, but - * don't have direct access to the ELF file structure. The link_elf_get_gp() - * function returns the GP given a pointer to a generic linker file struct. - */ -Elf_Addr -link_elf_get_gp(linker_file_t lf) -{ - elf_file_t ef = (elf_file_t)lf; - return (Elf_Addr)ef->got; -} -#endif - -const Elf_Sym * -elf_get_sym(linker_file_t lf, Elf_Word symidx) -{ - elf_file_t ef = (elf_file_t)lf; - - if (symidx >= ef->nchains) - return (NULL); - return (ef->symtab + symidx); -} - -const char * -elf_get_symname(linker_file_t lf, Elf_Word symidx) -{ - elf_file_t ef = (elf_file_t)lf; - const Elf_Sym *sym; - - if (symidx >= ef->nchains) - return (NULL); - sym = ef->symtab + symidx; - return (ef->strtab + sym->st_name); + return (0); } /* @@ -1244,70 +838,113 @@ elf_get_symname(linker_file_t lf, Elf_Word symidx) * This is not only more efficient, it's also more correct. It's not always * the case that the symbol can be found through the hash table. */ -Elf_Addr -elf_lookup(linker_file_t lf, Elf_Word symidx, int deps) +static Elf_Addr +elf_obj_lookup(linker_file_t lf, Elf_Word symidx, int deps) { elf_file_t ef = (elf_file_t)lf; const Elf_Sym *sym; const char *symbol; + Elf_Addr ret; + int i; /* Don't even try to lookup the symbol if the index is bogus. */ - if (symidx >= ef->nchains) + if (symidx >= ef->ddbsymcnt) return (0); - sym = ef->symtab + symidx; + sym = ef->ddbsymtab + symidx; - /* - * Don't do a full lookup when the symbol is local. It may even - * fail because it may not be found through the hash table. - */ - if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) { - /* Force lookup failure when we have an insanity. */ - if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) + /* Theoretically we can avoid a lookup for some locals */ + switch (ELF64_ST_BIND(sym->st_info)) { + case STB_LOCAL: + /* Local, but undefined? huh? */ + if (sym->st_shndx == SHN_UNDEF) return (0); - return ((Elf_Addr)ef->address + sym->st_value); - } + ret = 0; + /* Relative to section number */ + for (i = 0; i < ef->nprogtab; i++) { + if (sym->st_shndx == ef->progtab[i].sec) { + ret = (Elf_Addr)ef->progtab[i].addr; + break; + } + } + if (ret == 0) { + for (i = 0; i < ef->nnobittab; i++) { + if (sym->st_shndx == ef->nobittab[i].sec) { + ret = (Elf_Addr)ef->nobittab[i].addr; + break; + } + } + } + return ret + sym->st_value; - /* - * XXX we can avoid doing a hash table based lookup for global - * symbols as well. This however is not always valid, so we'll - * just do it the hard way for now. Performance tweaks can - * always be added. - */ + case STB_GLOBAL: + /* Relative to Data or Function name */ + symbol = ef->ddbstrtab + sym->st_name; - symbol = ef->strtab + sym->st_name; + /* Force a lookup failure if the symbol name is bogus. */ + if (*symbol == 0) + return (0); + ret = ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); + return ret; - /* Force a lookup failure if the symbol name is bogus. */ - if (*symbol == 0) + case STB_WEAK: + printf("Weak symbols not supported\n"); return (0); - return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); + default: + return (0); + } } + + static void link_elf_reloc_local(linker_file_t lf) { - const Elf_Rel *rellim; - const Elf_Rel *rel; - const Elf_Rela *relalim; - const Elf_Rela *rela; - elf_file_t ef = (elf_file_t)lf; + elf_file_t ef = (elf_file_t)lf; + const Elf_Rel *rellim; + const Elf_Rel *rel; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *sym; + Elf_Addr base; + int i; + Elf_Word symidx; - /* Perform relocations without addend if there are any: */ - if ((rel = ef->rel) != NULL) { - rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); - while (rel < rellim) { - elf_reloc_local(lf, rel, ELF_RELOC_REL); - rel++; - } - } - /* Perform relocations with addend if there are any: */ - if ((rela = ef->rela) != NULL) { - relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); - while (rela < relalim) { - elf_reloc_local(lf, rela, ELF_RELOC_RELA); - rela++; + /* Perform relocations without addend if there are any: */ + for (i = 0; i < ef->nrel; i++) { + rel = ef->reltab[i].rel; + if (rel) { + rellim = (const Elf_Rel *)((const char *)rel + ef->reltab[i].filesz); + base = findbase(ef, ef->reltab[i].sec); + while (rel < rellim) { + symidx = ELF_R_SYM(rel->r_info); + if (symidx < ef->ddbsymcnt) { + sym = ef->ddbsymtab + symidx; + if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL) + elf_reloc_local(lf, base, rel, ELF_RELOC_REL, elf_obj_lookup); + } + rel++; + } + } + } + + /* Perform relocations with addend if there are any: */ + for (i = 0; i < ef->nrela; i++) { + rela = ef->relatab[i].rela; + if (rela) { + relalim = (const Elf_Rela *)((const char *)rela + ef->relatab[i].filesz); + base = findbase(ef, ef->relatab[i].sec); + while (rela < relalim) { + symidx = ELF_R_SYM(rela->r_info); + if (symidx < ef->ddbsymcnt) { + sym = ef->ddbsymtab + symidx; + if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL) + elf_reloc_local(lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup); + } + rela++; + } + } } - } }