Fully implement KLD and preloading.

- seperate unload for preloaded linker objects.
- Don't build a kernel object if running as an a.out kernel.
- extract the real kernel name rather than hardwiring "kernel" for kldstat.
  (sysctl kern.bootfile getst the full name via bootinfo)
- use real addresses on the kernel "module" rather than fictitious ones.
- preloaded module support
- search module path for file modules.
- symbols are checked to see if they are in the right containing file
  before using their indexes into string tables.  This is to help ddb
  since it only supplies a pointer to an opaque symbol and there is no
  telling which file/object/module/whatever it came from.
This commit is contained in:
peter 1998-10-09 23:55:31 +00:00
parent df303e259f
commit 080952858d
2 changed files with 284 additions and 106 deletions

View File

@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: link_elf.c,v 1.1 1998/08/24 08:25:26 dfr Exp $
* $Id: link_elf.c,v 1.2 1998/09/11 08:46:15 dfr Exp $
*/
#include <sys/param.h>
@ -47,17 +47,16 @@
#include <vm/pmap.h>
#include <vm/vm_map.h>
extern int elf_reloc(linker_file_t lf, const Elf_Rela *rela,
const char *sym);
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 void link_elf_symbol_values(linker_file_t, linker_sym_t, linker_symval_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(linker_file_t);
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
@ -67,16 +66,22 @@ static void link_elf_unload(linker_file_t);
linker_file_t linker_kernel_file;
static struct linker_class_ops link_elf_class_ops = {
link_elf_load_file,
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,
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
@ -113,7 +118,12 @@ extern struct _dynamic _DYNAMIC;
static void
link_elf_init(void* arg)
{
Elf_Dyn* dp = (Elf_Dyn*) &_DYNAMIC;
#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);
@ -121,9 +131,9 @@ link_elf_init(void* arg)
linker_add_class("elf64", NULL, &link_elf_class_ops);
#endif
#ifdef __ELF__
dp = (Elf_Dyn*) &_DYNAMIC;
if (dp) {
elf_file_t ef;
ef = malloc(sizeof(struct elf_file), M_LINKER, M_NOWAIT);
if (ef == NULL)
panic("link_elf_init: Can't create linker structures for kernel");
@ -133,25 +143,38 @@ link_elf_init(void* arg)
ef->object = 0;
#endif
ef->dynamic = dp;
linker_kernel_file =
linker_make_file(kernelname, ef, &link_elf_file_ops);
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);
/*
* XXX there must be a better way of getting these constants.
*/
/* 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) {
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;
}
linker_current_file = linker_kernel_file;
}
#endif
}
SYSINIT(link_elf, SI_SUB_KMEM, SI_ORDER_THIRD, link_elf_init, 0);
SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
static int
parse_dynamic(linker_file_t lf)
@ -236,6 +259,69 @@ 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;
}
/* Try to load dependencies */
error = load_dependancies(lf);
if (error) {
linker_file_unload(lf);
return error;
}
error = relocate_file(lf);
if (error) {
linker_file_unload(lf);
return error;
}
*result = lf;
return (0);
}
static int
link_elf_load_file(const char* filename, linker_file_t* result)
{
@ -272,9 +358,14 @@ link_elf_load_file(const char* filename, linker_file_t* result)
int resid;
elf_file_t ef;
linker_file_t lf;
char *pathname;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p);
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;
@ -314,11 +405,6 @@ link_elf_load_file(const char* filename, linker_file_t* result)
error = ENOEXEC;
goto out;
}
if (!ELF_MACHINE_OK(u.hdr.e_machine)) {
link_elf_error("Incompatibile elf machine type");
error = ENOEXEC;
goto out;
}
/*
* We rely on the program header being in the first page. This is
@ -457,9 +543,19 @@ link_elf_load_file(const char* filename, linker_file_t* result)
lf->address = ef->address;
lf->size = mapsize;
if ((error = parse_dynamic(lf)) != 0
|| (error = load_dependancies(lf)) != 0
|| (error = relocate_file(lf)) != 0) {
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;
}
@ -474,7 +570,7 @@ link_elf_load_file(const char* filename, linker_file_t* result)
}
static void
link_elf_unload(linker_file_t file)
link_elf_unload_file(linker_file_t file)
{
elf_file_t ef = file->priv;
@ -493,6 +589,17 @@ link_elf_unload(linker_file_t file)
}
}
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)
{
@ -514,29 +621,6 @@ load_dependancies(linker_file_t lf)
if (dp->d_tag == DT_NEEDED) {
name = ef->strtab + dp->d_un.d_val;
/*
* Prepend pathname if dep is not an absolute filename.
*/
if (name[0] != '/') {
char* p;
if (!filename) {
filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
if (!filename) {
error = ENOMEM;
goto out;
}
}
p = lf->filename + strlen(lf->filename) - 1;
while (p >= lf->filename && *p != '/')
p--;
if (p >= lf->filename) {
strncpy(filename, lf->filename, p - lf->filename);
filename[p - lf->filename] = '\0';
strcat(filename, "/");
strcat(filename, name);
name = filename;
}
}
error = linker_load_file(name, &lfdep);
if (error)
goto out;
@ -679,15 +763,21 @@ link_elf_lookup_symbol(linker_file_t lf, const char* name, linker_sym_t* sym)
return ENOENT;
}
static void
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;
int symcount = ef->nchains;
if (es < ef->symtab)
return ENOENT;
if ((es - ef->symtab) > symcount)
return ENOENT;
symval->name = ef->strtab + es->st_name;
symval->value = (caddr_t) ef->address + es->st_value;
symval->size = es->st_size;
return 0;
}
static int
@ -702,7 +792,7 @@ link_elf_search_symbol(linker_file_t lf, caddr_t value,
const Elf_Sym* best = 0;
int i;
for (i = 0, es = ef->symtab; i < ef->nchains; i++, es++) {
for (i = 0, es = ef->symtab; i < symcount; i++, es++) {
if (es->st_name == 0)
continue;
if (off >= es->st_value) {
@ -724,4 +814,3 @@ link_elf_search_symbol(linker_file_t lf, caddr_t value,
return 0;
}

View File

@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: link_elf.c,v 1.1 1998/08/24 08:25:26 dfr Exp $
* $Id: link_elf.c,v 1.2 1998/09/11 08:46:15 dfr Exp $
*/
#include <sys/param.h>
@ -47,17 +47,16 @@
#include <vm/pmap.h>
#include <vm/vm_map.h>
extern int elf_reloc(linker_file_t lf, const Elf_Rela *rela,
const char *sym);
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 void link_elf_symbol_values(linker_file_t, linker_sym_t, linker_symval_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(linker_file_t);
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
@ -67,16 +66,22 @@ static void link_elf_unload(linker_file_t);
linker_file_t linker_kernel_file;
static struct linker_class_ops link_elf_class_ops = {
link_elf_load_file,
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,
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
@ -113,7 +118,12 @@ extern struct _dynamic _DYNAMIC;
static void
link_elf_init(void* arg)
{
Elf_Dyn* dp = (Elf_Dyn*) &_DYNAMIC;
#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);
@ -121,9 +131,9 @@ link_elf_init(void* arg)
linker_add_class("elf64", NULL, &link_elf_class_ops);
#endif
#ifdef __ELF__
dp = (Elf_Dyn*) &_DYNAMIC;
if (dp) {
elf_file_t ef;
ef = malloc(sizeof(struct elf_file), M_LINKER, M_NOWAIT);
if (ef == NULL)
panic("link_elf_init: Can't create linker structures for kernel");
@ -133,25 +143,38 @@ link_elf_init(void* arg)
ef->object = 0;
#endif
ef->dynamic = dp;
linker_kernel_file =
linker_make_file(kernelname, ef, &link_elf_file_ops);
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);
/*
* XXX there must be a better way of getting these constants.
*/
/* 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) {
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;
}
linker_current_file = linker_kernel_file;
}
#endif
}
SYSINIT(link_elf, SI_SUB_KMEM, SI_ORDER_THIRD, link_elf_init, 0);
SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
static int
parse_dynamic(linker_file_t lf)
@ -236,6 +259,69 @@ 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;
}
/* Try to load dependencies */
error = load_dependancies(lf);
if (error) {
linker_file_unload(lf);
return error;
}
error = relocate_file(lf);
if (error) {
linker_file_unload(lf);
return error;
}
*result = lf;
return (0);
}
static int
link_elf_load_file(const char* filename, linker_file_t* result)
{
@ -272,9 +358,14 @@ link_elf_load_file(const char* filename, linker_file_t* result)
int resid;
elf_file_t ef;
linker_file_t lf;
char *pathname;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p);
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;
@ -314,11 +405,6 @@ link_elf_load_file(const char* filename, linker_file_t* result)
error = ENOEXEC;
goto out;
}
if (!ELF_MACHINE_OK(u.hdr.e_machine)) {
link_elf_error("Incompatibile elf machine type");
error = ENOEXEC;
goto out;
}
/*
* We rely on the program header being in the first page. This is
@ -457,9 +543,19 @@ link_elf_load_file(const char* filename, linker_file_t* result)
lf->address = ef->address;
lf->size = mapsize;
if ((error = parse_dynamic(lf)) != 0
|| (error = load_dependancies(lf)) != 0
|| (error = relocate_file(lf)) != 0) {
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;
}
@ -474,7 +570,7 @@ link_elf_load_file(const char* filename, linker_file_t* result)
}
static void
link_elf_unload(linker_file_t file)
link_elf_unload_file(linker_file_t file)
{
elf_file_t ef = file->priv;
@ -493,6 +589,17 @@ link_elf_unload(linker_file_t file)
}
}
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)
{
@ -514,29 +621,6 @@ load_dependancies(linker_file_t lf)
if (dp->d_tag == DT_NEEDED) {
name = ef->strtab + dp->d_un.d_val;
/*
* Prepend pathname if dep is not an absolute filename.
*/
if (name[0] != '/') {
char* p;
if (!filename) {
filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
if (!filename) {
error = ENOMEM;
goto out;
}
}
p = lf->filename + strlen(lf->filename) - 1;
while (p >= lf->filename && *p != '/')
p--;
if (p >= lf->filename) {
strncpy(filename, lf->filename, p - lf->filename);
filename[p - lf->filename] = '\0';
strcat(filename, "/");
strcat(filename, name);
name = filename;
}
}
error = linker_load_file(name, &lfdep);
if (error)
goto out;
@ -679,15 +763,21 @@ link_elf_lookup_symbol(linker_file_t lf, const char* name, linker_sym_t* sym)
return ENOENT;
}
static void
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;
int symcount = ef->nchains;
if (es < ef->symtab)
return ENOENT;
if ((es - ef->symtab) > symcount)
return ENOENT;
symval->name = ef->strtab + es->st_name;
symval->value = (caddr_t) ef->address + es->st_value;
symval->size = es->st_size;
return 0;
}
static int
@ -702,7 +792,7 @@ link_elf_search_symbol(linker_file_t lf, caddr_t value,
const Elf_Sym* best = 0;
int i;
for (i = 0, es = ef->symtab; i < ef->nchains; i++, es++) {
for (i = 0, es = ef->symtab; i < symcount; i++, es++) {
if (es->st_name == 0)
continue;
if (off >= es->st_value) {
@ -724,4 +814,3 @@ link_elf_search_symbol(linker_file_t lf, caddr_t value,
return 0;
}