1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-22 11:17:19 +00:00
freebsd/sys/kern/link_elf.c

1175 lines
29 KiB
C
Raw Normal View History

/*-
* Copyright (c) 1998-2000 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.
*
1999-08-28 01:08:13 +00:00
* $FreeBSD$
*/
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.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>
#ifdef GPROF
#include <machine/profile.h>
#endif
1998-09-11 08:46:15 +00:00
#include <vm/vm.h>
#include <vm/vm_param.h>
#ifdef SPARSE_MAPPING
1998-09-11 08:46:15 +00:00
#include <vm/vm_object.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#endif
1998-09-11 08:46:15 +00:00
#include <vm/pmap.h>
#include <vm/vm_map.h>
#ifdef __AOUT__
#include <nlist.h>
#endif
#include <link.h>
1998-09-11 08:46:15 +00:00
#include "linker_if.h"
typedef struct elf_file {
struct linker_file lf; /* Common fields */
int preloaded; /* Was file pre-loaded */
1998-09-11 08:46:15 +00:00
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 */
1998-09-11 08:46:15 +00:00
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
} *elf_file_t;
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
static int link_elf_link_preload(linker_class_t cls,
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_search_symbol(linker_file_t, caddr_t value,
c_linker_sym_t* sym, long* diffp);
static void link_elf_unload_file(linker_file_t);
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
static void link_elf_unload_preload(linker_file_t);
static int link_elf_lookup_set(linker_file_t, const char *,
void ***, void ***, int *);
static int link_elf_each_function_name(linker_file_t,
int (*)(const char *, void *),
void *);
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),
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
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",
#else
"elf64",
#endif
link_elf_methods, sizeof(struct elf_file)
};
static int parse_dynamic(elf_file_t ef);
static int relocate_file(elf_file_t ef);
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
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.
*/
void
r_debug_state(struct r_debug *dummy_one __unused,
struct link_map *dummy_two __unused)
{
}
#endif
/*
* 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;
#ifdef DDB
char *newfilename;
#endif
#endif
linker_add_class(&link_elf_class);
#ifdef __ELF__
dp = (Elf_Dyn*) &_DYNAMIC;
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, &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
ef->gdb.l_addr = linker_kernel_file->address;
newfilename = malloc(strlen(modname) + 1, M_LINKER, M_WAITOK);
strcpy(newfilename, modname);
ef->gdb.l_name = newfilename;
ef->gdb.l_ld = dp;
ef->gdb.l_prev = 0;
ef->gdb.l_next = 0;
r_debug.r_map = &ef->gdb;
r_debug.r_brk = r_debug_state;
r_debug.r_state = RT_CONSISTENT;
r_debug_state(NULL, NULL); /* say hello to gdb! */
#endif
#endif
}
SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
static int
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
link_elf_preload_parse_symbols(elf_file_t ef)
{
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;
1998-09-11 08:46:15 +00:00
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:
1998-09-11 08:46:15 +00:00
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:
1998-09-11 08:46:15 +00:00
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;
1998-09-11 08:46:15 +00:00
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
}
}
1998-09-11 08:46:15 +00:00
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;
}
1998-09-11 08:46:15 +00:00
static void
link_elf_error(const char *s)
{
printf("kldload: %s\n", s);
}
#ifdef DDB
static void
link_elf_add_gdb(struct link_map *l)
{
struct link_map *prev;
/*
* Scan to the end of the list.
*/
for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next)
;
/* Link in the new entry. */
l->l_prev = prev;
l->l_next = prev->l_next;
prev->l_next = l;
}
static void
link_elf_delete_gdb(struct link_map *l)
{
if (l->l_prev == NULL) {
if ((r_debug.r_map = l->l_next) != NULL)
l->l_next->l_prev = NULL;
return;
}
if ((l->l_prev->l_next = l->l_next) != NULL)
l->l_next->l_prev = l->l_prev;
}
#endif /* DDB */
static int
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
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;
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
/* Look to see if we have the file preloaded */
modptr = preload_search_by_name(filename);
if (modptr == NULL)
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
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 module") != 0)
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;
}
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
*result = lf;
return (0);
}
static int
link_elf_link_preload_finish(linker_file_t lf)
{
elf_file_t ef;
int error;
#ifdef DDB
char *newfilename;
#endif
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
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;
}
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
#endif
error = relocate_file(ef);
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
if (error)
return error;
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
(void)link_elf_preload_parse_symbols(ef);
#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);
}
static int
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
link_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result)
{
struct nameidata nd;
struct thread* td = curthread; /* XXX */
struct proc* p = td->td_proc; /* XXX */
Elf_Ehdr *hdr;
caddr_t firstpage;
1998-09-11 08:46:15 +00:00
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;
#ifdef DDB
char *newfilename;
#endif
GIANT_REQUIRED;
shdr = NULL;
lf = NULL;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
flags = FREAD;
error = vn_open(&nd, &flags, 0);
if (error)
return error;
NDFREE(&nd, NDF_ONLY_PNBUF);
/*
1998-09-11 08:46:15 +00:00
* 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, p->p_ucred, &resid, td);
nbytes = PAGE_SIZE - resid;
if (error)
goto out;
if (!IS_ELF(*hdr)) {
1998-09-11 08:46:15 +00:00
error = ENOEXEC;
goto out;
}
if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
|| hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
1998-09-11 08:46:15 +00:00
link_elf_error("Unsupported file layout");
error = ENOEXEC;
goto out;
}
if (hdr->e_ident[EI_VERSION] != EV_CURRENT
|| hdr->e_version != EV_CURRENT) {
1998-09-11 08:46:15 +00:00
link_elf_error("Unsupported file version");
error = ENOEXEC;
goto out;
}
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
1998-09-11 08:46:15 +00:00
link_elf_error("Unsupported file type");
error = ENOEXEC;
goto out;
}
if (hdr->e_machine != ELF_TARG_MACH) {
1998-09-11 08:46:15 +00:00
link_elf_error("Unsupported machine");
error = ENOEXEC;
goto out;
}
/*
1998-09-11 08:46:15 +00:00
* 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)))
1998-09-11 08:46:15 +00:00
link_elf_error("Unreadable program headers");
/*
1998-09-11 08:46:15 +00:00
* 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;
1998-09-11 08:46:15 +00:00
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;
case PT_INTERP:
link_elf_error("Unsupported file type");
error = ENOEXEC;
goto out;
1998-09-11 08:46:15 +00:00
}
++phdr;
}
if (phdyn == NULL) {
link_elf_error("Object is not dynamically-linked");
error = ENOEXEC;
goto out;
1998-09-11 08:46:15 +00:00
}
/*
1998-09-11 08:46:15 +00:00
* Allocate the entire address space of the object, to stake out our
* contiguous region, and to establish the base address for relocation.
*/
1998-09-11 08:46:15 +00:00
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;
1998-09-11 08:46:15 +00:00
#ifdef SPARSE_MAPPING
ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT);
if (ef->object == NULL) {
free(ef, M_LINKER);
1998-09-11 08:46:15 +00:00
error = ENOMEM;
goto out;
}
1998-09-11 08:46:15 +00:00
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;
1998-09-11 08:46:15 +00:00
goto out;
}
#else
ef->address = malloc(mapsize, M_LINKER, M_WAITOK);
if (!ef->address) {
error = ENOMEM;
goto out;
}
1998-09-11 08:46:15 +00:00
#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, td);
1998-09-11 08:46:15 +00:00
if (error) {
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
}
#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
ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
lf->address = ef->address;
1998-09-11 08:46:15 +00:00
lf->size = mapsize;
error = parse_dynamic(ef);
if (error)
goto out;
error = linker_load_dependencies(lf);
if (error)
goto out;
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
#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)
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, p->p_ucred, &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;
}
}
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);
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,
UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
if (error)
goto out;
error = vn_rdwr(UIO_READ, nd.ni_vp,
ef->strbase, strcnt, shdr[symstrindex].sh_offset,
UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td);
if (error)
goto out;
ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
ef->ddbsymtab = (const Elf_Sym *)ef->symbase;
ef->ddbstrcnt = strcnt;
ef->ddbstrtab = ef->strbase;
#ifdef DDB
GDB_STATE(RT_ADD);
ef->gdb.l_addr = lf->address;
newfilename = malloc(strlen(filename) + 1, M_LINKER, M_WAITOK);
strcpy(newfilename, filename);
ef->gdb.l_name = (const char *)newfilename;
ef->gdb.l_ld = ef->dynamic;
link_elf_add_gdb(&ef->gdb);
GDB_STATE(RT_CONSISTENT);
#endif
nosyms:
*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, p->p_ucred, td);
return error;
}
static void
link_elf_unload_file(linker_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
if (ef->preloaded) {
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
link_elf_unload_preload(file);
return;
}
1998-09-11 08:46:15 +00:00
#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);
}
1998-09-11 08:46:15 +00:00
#else
if (ef->address)
free(ef->address, M_LINKER);
1998-09-11 08:46:15 +00:00
#endif
if (ef->symbase)
free(ef->symbase, M_LINKER);
if (ef->strbase)
free(ef->strbase, M_LINKER);
}
static void
First round implementation of a fine grain enhanced module to module version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
2000-04-29 13:19:31 +00:00
link_elf_unload_preload(linker_file_t file)
{
if (file->filename)
preload_delete_name(file->filename);
}
1998-09-11 08:46:15 +00:00
static const char *
symbol_name(elf_file_t ef, Elf_Word r_info)
{
1998-09-11 08:46:15 +00:00
const Elf_Sym *ref;
if (ELF_R_SYM(r_info)) {
ref = ef->symtab + ELF_R_SYM(r_info);
return ef->strtab + ref->st_name;
1998-09-11 08:46:15 +00:00
} else
return NULL;
}
static int
relocate_file(elf_file_t ef)
{
1998-09-11 08:46:15 +00:00
const Elf_Rel *rellim;
const Elf_Rel *rel;
const Elf_Rela *relalim;
const Elf_Rela *rela;
const char *symname;
1998-09-11 08:46:15 +00:00
/* 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) {
symname = symbol_name(ef, rel->r_info);
if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) {
printf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
rel++;
}
1998-09-11 08:46:15 +00:00
}
1998-09-11 08:46:15 +00:00
/* 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) {
symname = symbol_name(ef, rela->r_info);
if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) {
printf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
rela++;
}
1998-09-11 08:46:15 +00:00
}
1998-09-11 08:46:15 +00:00
/* 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) {
symname = symbol_name(ef, rel->r_info);
if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) {
printf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
rel++;
}
1998-09-11 08:46:15 +00:00
}
1998-09-11 08:46:15 +00:00
/* 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) {
symname = symbol_name(ef, rela->r_info);
if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) {
printf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
rela++;
}
}
return 0;
}
1998-09-11 08:46:15 +00:00
/*
* 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)
{
1998-09-11 08:46:15 +00:00
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, c_linker_sym_t* sym)
{
elf_file_t ef = (elf_file_t) lf;
1998-09-11 08:46:15 +00:00
unsigned long symnum;
const Elf_Sym* symp;
const char *strp;
1998-09-11 08:46:15 +00:00
unsigned long hash;
int i;
/* First, search hashed global symbols */
1998-09-11 08:46:15 +00:00
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;
}
1998-09-11 08:46:15 +00:00
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;
1998-09-11 08:46:15 +00:00
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;
}
}
1998-09-11 08:46:15 +00:00
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->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,
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;
1998-09-11 08:46:15 +00:00
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;
st_value = es->st_value + (uintptr_t) (void *) ef->address;
if (off >= st_value) {
if (off - st_value < diff) {
diff = off - st_value;
best = es;
if (diff == 0)
break;
} else if (off - st_value == diff) {
best = es;
}
}
}
if (best == 0)
*diffp = off;
else
*diffp = diff;
*sym = (c_linker_sym_t) best;
return 0;
}
/*
* Look up a linker set on an ELF system.
*/
static int
link_elf_lookup_set(linker_file_t lf, const char *name,
void ***startp, void ***stopp, int *countp)
{
c_linker_sym_t sym;
linker_symval_t symval;
char *setsym;
void **start, **stop;
int len, error = 0, 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;
}
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;
}
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;
/* 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);
}