2d636ab077
Drastically quieten down the verbose load progress messages. They were more useful for debugging than anything, but are beyond a joke when loading a few dozen modules. Simplify the ELF extended symbol table load format. Just take the main symbol table and the string table that corresponds. This is what we will be getting local symbols from. (needed for the alpha stack tracebacks). Use the (optional) full symbol tables in lookups. This means we have to furhter distinguish between symbols that can come from the dynamic linking table and the complete table. The alpha boot code now needs to be adapted as ddb/db_elf.c cannot use the simpler format. I have not implemented loading the extended symbol tables from the syscall interface yet, just for preloaded modules. I am not sure about the symbol resolution. I *think* it's possible that a local symbol can be found in preference to a global, depending on the search sequence and dependency tree.
904 lines
23 KiB
C
904 lines
23 KiB
C
/*-
|
|
* Copyright (c) 1998 Doug Rabson
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Id: link_elf.c,v 1.3 1998/10/09 23:55:31 peter Exp $
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/linker.h>
|
|
#include <machine/elf.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/vm_prot.h>
|
|
#include <vm/vm_param.h>
|
|
#include <sys/lock.h>
|
|
#include <vm/vm_object.h>
|
|
#include <vm/vm_kern.h>
|
|
#include <vm/vm_extern.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/vm_map.h>
|
|
|
|
static int link_elf_load_module(const char*, linker_file_t*);
|
|
static int link_elf_load_file(const char*, linker_file_t*);
|
|
static int link_elf_lookup_symbol(linker_file_t, const char*,
|
|
linker_sym_t*);
|
|
static int link_elf_symbol_values(linker_file_t, linker_sym_t, linker_symval_t*);
|
|
static int link_elf_search_symbol(linker_file_t, caddr_t value,
|
|
linker_sym_t* sym, long* diffp);
|
|
|
|
static void link_elf_unload_file(linker_file_t);
|
|
static void link_elf_unload_module(linker_file_t);
|
|
|
|
/*
|
|
* The file representing the currently running kernel. This contains
|
|
* the global symbol table.
|
|
*/
|
|
|
|
linker_file_t linker_kernel_file;
|
|
|
|
static struct linker_class_ops link_elf_class_ops = {
|
|
link_elf_load_module,
|
|
};
|
|
|
|
static struct linker_file_ops link_elf_file_ops = {
|
|
link_elf_lookup_symbol,
|
|
link_elf_symbol_values,
|
|
link_elf_search_symbol,
|
|
link_elf_unload_file,
|
|
};
|
|
|
|
static struct linker_file_ops link_elf_module_ops = {
|
|
link_elf_lookup_symbol,
|
|
link_elf_symbol_values,
|
|
link_elf_search_symbol,
|
|
link_elf_unload_module,
|
|
};
|
|
typedef struct elf_file {
|
|
caddr_t address; /* Relocation address */
|
|
#ifdef SPARSE_MAPPING
|
|
vm_object_t object; /* VM object to hold file pages */
|
|
#endif
|
|
const Elf_Dyn* dynamic; /* Symbol table etc. */
|
|
Elf_Off nbuckets; /* DT_HASH info */
|
|
Elf_Off nchains;
|
|
const Elf_Off* buckets;
|
|
const Elf_Off* 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 */
|
|
} *elf_file_t;
|
|
|
|
static int parse_dynamic(linker_file_t lf);
|
|
static int load_dependancies(linker_file_t lf);
|
|
static int relocate_file(linker_file_t lf);
|
|
static int parse_module_symbols(linker_file_t lf);
|
|
|
|
/*
|
|
* The kernel symbol table starts here.
|
|
*/
|
|
extern struct _dynamic _DYNAMIC;
|
|
|
|
static void
|
|
link_elf_init(void* arg)
|
|
{
|
|
#ifdef __ELF__
|
|
Elf_Dyn *dp;
|
|
caddr_t modptr, baseptr, sizeptr;
|
|
elf_file_t ef;
|
|
char *modname;
|
|
#endif
|
|
|
|
#if ELF_TARG_CLASS == ELFCLASS32
|
|
linker_add_class("elf32", NULL, &link_elf_class_ops);
|
|
#else
|
|
linker_add_class("elf64", NULL, &link_elf_class_ops);
|
|
#endif
|
|
|
|
#ifdef __ELF__
|
|
dp = (Elf_Dyn*) &_DYNAMIC;
|
|
if (dp) {
|
|
ef = malloc(sizeof(struct elf_file), M_LINKER, M_NOWAIT);
|
|
if (ef == NULL)
|
|
panic("link_elf_init: Can't create linker structures for kernel");
|
|
|
|
ef->address = 0;
|
|
#ifdef SPARSE_MAPPING
|
|
ef->object = 0;
|
|
#endif
|
|
ef->dynamic = dp;
|
|
modname = 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, ef, &link_elf_file_ops);
|
|
if (linker_kernel_file == NULL)
|
|
panic("link_elf_init: Can't create linker structures for kernel");
|
|
parse_dynamic(linker_kernel_file);
|
|
/* Sigh, magic constants. */
|
|
#ifdef __alpha__
|
|
linker_kernel_file->address = (caddr_t) 0xfffffc0000300000;
|
|
#else
|
|
linker_kernel_file->address = (caddr_t) 0xf0100000;
|
|
#endif
|
|
linker_kernel_file->size = -(long)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)parse_module_symbols(linker_kernel_file);
|
|
linker_current_file = linker_kernel_file;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
|
|
|
|
static int
|
|
parse_module_symbols(linker_file_t lf)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
caddr_t pointer;
|
|
caddr_t ssym, esym, base;
|
|
caddr_t strtab;
|
|
int strcnt;
|
|
Elf_Sym* symtab;
|
|
int symcnt;
|
|
|
|
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(linker_file_t lf)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
const 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_Off *hashtab = (const Elf_Off *)
|
|
(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;
|
|
}
|
|
}
|
|
|
|
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 void
|
|
link_elf_error(const char *s)
|
|
{
|
|
printf("kldload: %s\n", s);
|
|
}
|
|
|
|
static int
|
|
link_elf_load_module(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 module preloaded */
|
|
modptr = preload_search_by_name(filename);
|
|
if (modptr == NULL)
|
|
return (link_elf_load_file(filename, result));
|
|
|
|
/* It's preloaded, check we can handle it and collect information */
|
|
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 module") != 0)
|
|
return (EFTYPE);
|
|
if (baseptr == NULL || sizeptr == NULL || dynptr == NULL)
|
|
return (EINVAL);
|
|
|
|
ef = malloc(sizeof(struct elf_file), M_LINKER, M_WAITOK);
|
|
if (ef == NULL)
|
|
return (ENOMEM);
|
|
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 = linker_make_file(filename, ef, &link_elf_module_ops);
|
|
if (lf == NULL) {
|
|
free(ef, M_LINKER);
|
|
return ENOMEM;
|
|
}
|
|
lf->address = ef->address;
|
|
lf->size = *(size_t *)sizeptr;
|
|
|
|
error = parse_dynamic(lf);
|
|
if (error) {
|
|
linker_file_unload(lf);
|
|
return error;
|
|
}
|
|
error = load_dependancies(lf);
|
|
if (error) {
|
|
linker_file_unload(lf);
|
|
return error;
|
|
}
|
|
error = relocate_file(lf);
|
|
if (error) {
|
|
linker_file_unload(lf);
|
|
return error;
|
|
}
|
|
(void)parse_module_symbols(lf);
|
|
*result = lf;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
link_elf_load_file(const char* filename, linker_file_t* result)
|
|
{
|
|
struct nameidata nd;
|
|
struct proc* p = curproc; /* XXX */
|
|
union {
|
|
Elf_Ehdr hdr;
|
|
char buf[PAGE_SIZE];
|
|
} u;
|
|
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;
|
|
caddr_t base_addr;
|
|
Elf_Off data_offset;
|
|
Elf_Addr data_vaddr;
|
|
Elf_Addr data_vlimit;
|
|
caddr_t data_addr;
|
|
Elf_Addr clear_vaddr;
|
|
caddr_t clear_addr;
|
|
size_t nclear;
|
|
Elf_Addr bss_vaddr;
|
|
Elf_Addr bss_vlimit;
|
|
caddr_t bss_addr;
|
|
int error = 0;
|
|
int resid;
|
|
elf_file_t ef;
|
|
linker_file_t lf;
|
|
char *pathname;
|
|
|
|
pathname = linker_search_path(filename);
|
|
if (pathname == NULL)
|
|
return ENOENT;
|
|
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, pathname, p);
|
|
error = vn_open(&nd, FREAD, 0);
|
|
free(pathname, M_LINKER);
|
|
if (error)
|
|
return error;
|
|
|
|
/*
|
|
* Read the elf header from the file.
|
|
*/
|
|
error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) &u, sizeof u, 0,
|
|
UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
|
|
nbytes = sizeof u - resid;
|
|
if (error)
|
|
goto out;
|
|
|
|
if (!IS_ELF(u.hdr)) {
|
|
error = ENOEXEC;
|
|
goto out;
|
|
}
|
|
|
|
if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS
|
|
|| u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) {
|
|
link_elf_error("Unsupported file layout");
|
|
error = ENOEXEC;
|
|
goto out;
|
|
}
|
|
if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT
|
|
|| u.hdr.e_version != EV_CURRENT) {
|
|
link_elf_error("Unsupported file version");
|
|
error = ENOEXEC;
|
|
goto out;
|
|
}
|
|
if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) {
|
|
link_elf_error("Unsupported file type");
|
|
error = ENOEXEC;
|
|
goto out;
|
|
}
|
|
if (u.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 (!((u.hdr.e_phentsize == sizeof(Elf_Phdr))
|
|
|| (u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE)
|
|
|| (u.hdr.e_phoff + u.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 *) (u.buf + u.hdr.e_phoff);
|
|
phlimit = phdr + u.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");
|
|
error = ENOEXEC;
|
|
goto out;
|
|
}
|
|
segs[nsegs] = phdr;
|
|
++nsegs;
|
|
break;
|
|
|
|
case PT_PHDR:
|
|
phphdr = phdr;
|
|
break;
|
|
|
|
case PT_DYNAMIC:
|
|
phdyn = phdr;
|
|
break;
|
|
}
|
|
|
|
++phdr;
|
|
}
|
|
if (phdyn == NULL) {
|
|
link_elf_error("Object is not dynamically-linked");
|
|
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;
|
|
|
|
ef = malloc(sizeof(struct elf_file), M_LINKER, M_WAITOK);
|
|
#ifdef SPARSE_MAPPING
|
|
ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT);
|
|
if (ef->object == NULL) {
|
|
free(ef, M_LINKER);
|
|
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);
|
|
free(ef, M_LINKER);
|
|
goto out;
|
|
}
|
|
#else
|
|
ef->address = malloc(mapsize, M_LINKER, M_WAITOK);
|
|
#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, p->p_ucred, &resid, p);
|
|
if (error) {
|
|
#ifdef SPARSE_MAPPING
|
|
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
|
|
free(ef->address, M_LINKER);
|
|
#endif
|
|
free(ef, M_LINKER);
|
|
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_pageable(kernel_map,
|
|
(vm_offset_t) segbase,
|
|
(vm_offset_t) segbase + segs[i]->p_memsz,
|
|
FALSE);
|
|
#endif
|
|
}
|
|
|
|
ef->dynamic = (const Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
|
|
|
|
lf = linker_make_file(filename, ef, &link_elf_file_ops);
|
|
if (lf == NULL) {
|
|
#ifdef SPARSE_MAPPING
|
|
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
|
|
free(ef->address, M_LINKER);
|
|
#endif
|
|
free(ef, M_LINKER);
|
|
error = ENOMEM;
|
|
goto out;
|
|
}
|
|
lf->address = ef->address;
|
|
lf->size = mapsize;
|
|
|
|
error = parse_dynamic(lf);
|
|
if (error) {
|
|
linker_file_unload(lf);
|
|
goto out;
|
|
}
|
|
error = load_dependancies(lf);
|
|
if (error) {
|
|
linker_file_unload(lf);
|
|
goto out;
|
|
}
|
|
error = relocate_file(lf);
|
|
if (error) {
|
|
linker_file_unload(lf);
|
|
goto out;
|
|
}
|
|
|
|
*result = lf;
|
|
|
|
out:
|
|
VOP_UNLOCK(nd.ni_vp, 0, p);
|
|
vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
|
|
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
link_elf_unload_file(linker_file_t file)
|
|
{
|
|
elf_file_t ef = file->priv;
|
|
|
|
if (ef) {
|
|
#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
|
|
free(ef->address, M_LINKER);
|
|
#endif
|
|
free(ef, M_LINKER);
|
|
}
|
|
}
|
|
|
|
static void
|
|
link_elf_unload_module(linker_file_t file)
|
|
{
|
|
elf_file_t ef = file->priv;
|
|
|
|
if (ef)
|
|
free(ef, M_LINKER);
|
|
if (file->filename)
|
|
preload_delete_name(file->filename);
|
|
}
|
|
|
|
static int
|
|
load_dependancies(linker_file_t lf)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
linker_file_t lfdep;
|
|
char* name;
|
|
char* filename = 0;
|
|
const Elf_Dyn *dp;
|
|
int error = 0;
|
|
|
|
/*
|
|
* All files are dependant on /kernel.
|
|
*/
|
|
linker_kernel_file->refs++;
|
|
linker_file_add_dependancy(lf, linker_kernel_file);
|
|
|
|
|
|
for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
|
|
if (dp->d_tag == DT_NEEDED) {
|
|
name = ef->strtab + dp->d_un.d_val;
|
|
|
|
error = linker_load_file(name, &lfdep);
|
|
if (error)
|
|
goto out;
|
|
error = linker_file_add_dependancy(lf, lfdep);
|
|
if (error)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (filename)
|
|
free(filename, M_TEMP);
|
|
return error;
|
|
}
|
|
|
|
static const char *
|
|
symbol_name(elf_file_t ef, const Elf_Rela *rela)
|
|
{
|
|
const Elf_Sym *ref;
|
|
|
|
if (ELF_R_SYM(rela->r_info)) {
|
|
ref = ef->ddbsymtab + ELF_R_SYM(rela->r_info);
|
|
return ef->ddbstrtab + ref->st_name;
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
relocate_file(linker_file_t lf)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
const Elf_Rel *rellim;
|
|
const Elf_Rel *rel;
|
|
const Elf_Rela *relalim;
|
|
const Elf_Rela *rela;
|
|
|
|
/* Perform relocations without addend if there are any: */
|
|
rellim = (const Elf_Rel *) ((caddr_t) ef->rel + ef->relsize);
|
|
for (rel = ef->rel; ef->rel != NULL && rel < rellim; rel++) {
|
|
Elf_Rela locrela;
|
|
|
|
locrela.r_info = rel->r_info;
|
|
locrela.r_offset = rel->r_offset;
|
|
locrela.r_addend = 0;
|
|
if (elf_reloc(lf, &locrela, symbol_name(ef, &locrela)))
|
|
return ENOENT;
|
|
}
|
|
|
|
/* Perform relocations with addend if there are any: */
|
|
relalim = (const Elf_Rela *) ((caddr_t) ef->rela + ef->relasize);
|
|
for (rela = ef->rela; ef->rela != NULL && rela < relalim; rela++) {
|
|
if (elf_reloc(lf, rela, symbol_name(ef, rela)))
|
|
return ENOENT;
|
|
}
|
|
|
|
/* Perform PLT relocations without addend if there are any: */
|
|
rellim = (const Elf_Rel *) ((caddr_t) ef->pltrel + ef->pltrelsize);
|
|
for (rel = ef->pltrel; ef->pltrel != NULL && rel < rellim; rel++) {
|
|
Elf_Rela locrela;
|
|
|
|
locrela.r_info = rel->r_info;
|
|
locrela.r_offset = rel->r_offset;
|
|
locrela.r_addend = 0;
|
|
if (elf_reloc(lf, &locrela, symbol_name(ef, &locrela)))
|
|
return ENOENT;
|
|
}
|
|
|
|
/* Perform relocations with addend if there are any: */
|
|
relalim = (const Elf_Rela *) ((caddr_t) ef->pltrela + ef->pltrelasize);
|
|
for (rela = ef->pltrela; ef->pltrela != NULL && rela < relalim; rela++) {
|
|
if (elf_reloc(lf, rela, symbol_name(ef, rela)))
|
|
return ENOENT;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int
|
|
link_elf_lookup_symbol(linker_file_t lf, const char* name, linker_sym_t* sym)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
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 = (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 = (linker_sym_t) symp;
|
|
return 0;
|
|
} else
|
|
return ENOENT;
|
|
}
|
|
}
|
|
|
|
return ENOENT;
|
|
}
|
|
|
|
static int
|
|
link_elf_symbol_values(linker_file_t lf, linker_sym_t sym, linker_symval_t* symval)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
Elf_Sym* es = (Elf_Sym*) sym;
|
|
|
|
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;
|
|
}
|
|
if (ef->symtab == ef->ddbsymtab)
|
|
return ENOENT;
|
|
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;
|
|
}
|
|
return ENOENT;
|
|
}
|
|
|
|
static int
|
|
link_elf_search_symbol(linker_file_t lf, caddr_t value,
|
|
linker_sym_t* sym, long* diffp)
|
|
{
|
|
elf_file_t ef = lf->priv;
|
|
u_long off = (u_long) value;
|
|
u_long diff = off;
|
|
const Elf_Sym* es;
|
|
const Elf_Sym* best = 0;
|
|
int i;
|
|
|
|
for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
|
|
if (es->st_name == 0)
|
|
continue;
|
|
if (off >= es->st_value) {
|
|
if (off - es->st_value < diff) {
|
|
diff = off - es->st_value;
|
|
best = es;
|
|
if (diff == 0)
|
|
break;
|
|
} else if (off - es->st_value == diff) {
|
|
best = es;
|
|
}
|
|
}
|
|
}
|
|
if (best == 0)
|
|
*diffp = off;
|
|
else
|
|
*diffp = diff;
|
|
*sym = (linker_sym_t) best;
|
|
|
|
return 0;
|
|
}
|