1998-08-24 08:25:26 +00:00
|
|
|
/*-
|
2000-04-24 17:08:04 +00:00
|
|
|
* Copyright (c) 1998-2000 Doug Rabson
|
1998-08-24 08:25:26 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2003-06-11 00:56:59 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2008-05-23 00:49:39 +00:00
|
|
|
#include "opt_ddb.h"
|
2004-07-10 21:37:30 +00:00
|
|
|
#include "opt_gdb.h"
|
2000-04-24 17:08:04 +00:00
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
2004-05-19 14:36:38 +00:00
|
|
|
#ifdef GPROF
|
|
|
|
#include <sys/gmon.h>
|
|
|
|
#endif
|
2001-05-01 08:13:21 +00:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/lock.h>
|
1998-08-24 08:25:26 +00:00
|
|
|
#include <sys/malloc.h>
|
2001-06-03 02:19:07 +00:00
|
|
|
#include <sys/mutex.h>
|
2006-06-20 21:31:38 +00:00
|
|
|
#include <sys/mount.h>
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
#include <sys/pcpu.h>
|
1998-08-24 08:25:26 +00:00
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/linker.h>
|
2001-05-01 08:13:21 +00:00
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
#include <machine/elf.h>
|
|
|
|
|
2006-10-22 11:52:19 +00:00
|
|
|
#include <security/mac/mac_framework.h>
|
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
#include <vm/vm.h>
|
|
|
|
#include <vm/vm_param.h>
|
1999-10-11 15:19:12 +00:00
|
|
|
#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>
|
1999-10-11 15:19:12 +00:00
|
|
|
#endif
|
1998-09-11 08:46:15 +00:00
|
|
|
#include <vm/pmap.h>
|
|
|
|
#include <vm/vm_map.h>
|
2001-05-01 08:13:21 +00:00
|
|
|
|
2002-08-22 20:39:30 +00:00
|
|
|
#include <sys/link_elf.h>
|
1998-09-11 08:46:15 +00:00
|
|
|
|
2008-05-23 00:49:39 +00:00
|
|
|
#ifdef DDB_CTF
|
|
|
|
#include <net/zlib.h>
|
|
|
|
#endif
|
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
#include "linker_if.h"
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2005-10-17 23:21:55 +00:00
|
|
|
#define MAXSEGS 4
|
|
|
|
|
2008-07-11 01:10:40 +00:00
|
|
|
typedef struct elf_file {
|
|
|
|
struct linker_file lf; /* Common fields */
|
|
|
|
int preloaded; /* Was file pre-loaded */
|
|
|
|
caddr_t address; /* Relocation address */
|
|
|
|
#ifdef SPARSE_MAPPING
|
|
|
|
vm_object_t object; /* VM object to hold file pages */
|
|
|
|
#endif
|
|
|
|
Elf_Dyn* dynamic; /* Symbol table etc. */
|
|
|
|
Elf_Hashelt nbuckets; /* DT_HASH info */
|
|
|
|
Elf_Hashelt nchains;
|
|
|
|
const Elf_Hashelt* buckets;
|
|
|
|
const Elf_Hashelt* chains;
|
|
|
|
caddr_t hash;
|
|
|
|
caddr_t strtab; /* DT_STRTAB */
|
|
|
|
int strsz; /* DT_STRSZ */
|
|
|
|
const Elf_Sym* symtab; /* DT_SYMTAB */
|
|
|
|
Elf_Addr* got; /* DT_PLTGOT */
|
|
|
|
const Elf_Rel* pltrel; /* DT_JMPREL */
|
|
|
|
int pltrelsize; /* DT_PLTRELSZ */
|
|
|
|
const Elf_Rela* pltrela; /* DT_JMPREL */
|
|
|
|
int pltrelasize; /* DT_PLTRELSZ */
|
|
|
|
const Elf_Rel* rel; /* DT_REL */
|
|
|
|
int relsize; /* DT_RELSZ */
|
|
|
|
const Elf_Rela* rela; /* DT_RELA */
|
|
|
|
int relasize; /* DT_RELASZ */
|
|
|
|
caddr_t modptr;
|
|
|
|
const Elf_Sym* ddbsymtab; /* The symbol table we are using */
|
|
|
|
long ddbsymcnt; /* Number of symbols */
|
|
|
|
caddr_t ddbstrtab; /* String table */
|
|
|
|
long ddbstrcnt; /* number of bytes in string table */
|
|
|
|
caddr_t symbase; /* malloc'ed symbold base */
|
|
|
|
caddr_t strbase; /* malloc'ed string base */
|
|
|
|
caddr_t ctftab; /* CTF table */
|
|
|
|
long ctfcnt; /* number of bytes in CTF table */
|
|
|
|
caddr_t ctfoff; /* CTF offset table */
|
|
|
|
caddr_t typoff; /* Type offset table */
|
|
|
|
long typlen; /* Number of type entries. */
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
Elf_Addr pcpu_start; /* Pre-relocation pcpu set start. */
|
|
|
|
Elf_Addr pcpu_stop; /* Pre-relocation pcpu set stop. */
|
|
|
|
Elf_Addr pcpu_base; /* Relocated pcpu set address. */
|
2008-07-11 01:10:40 +00:00
|
|
|
#ifdef GDB
|
|
|
|
struct link_map gdb; /* hooks for gdb */
|
|
|
|
#endif
|
|
|
|
} *elf_file_t;
|
|
|
|
|
2008-05-23 00:49:39 +00:00
|
|
|
#include <kern/kern_ctf.c>
|
|
|
|
|
2002-10-19 18:59:33 +00:00
|
|
|
static int link_elf_link_common_finish(linker_file_t);
|
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*);
|
2000-04-24 17:08:04 +00:00
|
|
|
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);
|
2000-04-29 13:19:31 +00:00
|
|
|
static void link_elf_unload_preload(linker_file_t);
|
2001-06-13 10:58:39 +00:00
|
|
|
static int link_elf_lookup_set(linker_file_t, const char *,
|
|
|
|
void ***, void ***, int *);
|
2001-10-30 15:21:45 +00:00
|
|
|
static int link_elf_each_function_name(linker_file_t,
|
|
|
|
int (*)(const char *, void *),
|
|
|
|
void *);
|
2008-05-23 00:49:39 +00:00
|
|
|
static int link_elf_each_function_nameval(linker_file_t,
|
|
|
|
linker_function_nameval_callback_t,
|
|
|
|
void *);
|
2003-01-21 02:42:44 +00:00
|
|
|
static void link_elf_reloc_local(linker_file_t);
|
2009-05-26 21:39:09 +00:00
|
|
|
static long link_elf_symtab_get(linker_file_t, const Elf_Sym **);
|
|
|
|
static long link_elf_strtab_get(linker_file_t, caddr_t *);
|
2005-12-18 04:52:37 +00:00
|
|
|
static Elf_Addr elf_lookup(linker_file_t lf, Elf_Size symidx, int deps);
|
2000-04-24 17:08:04 +00:00
|
|
|
|
|
|
|
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),
|
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),
|
2001-06-13 10:58:39 +00:00
|
|
|
KOBJMETHOD(linker_lookup_set, link_elf_lookup_set),
|
2001-10-30 15:21:45 +00:00
|
|
|
KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
|
2008-05-23 00:49:39 +00:00
|
|
|
KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
|
|
|
|
KOBJMETHOD(linker_ctf_get, link_elf_ctf_get),
|
2009-05-26 21:39:09 +00:00
|
|
|
KOBJMETHOD(linker_symtab_get, link_elf_symtab_get),
|
|
|
|
KOBJMETHOD(linker_strtab_get, link_elf_strtab_get),
|
2000-04-24 17:08:04 +00:00
|
|
|
{ 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);
|
2000-04-29 13:19:31 +00:00
|
|
|
static int link_elf_preload_parse_symbols(elf_file_t ef);
|
2000-04-24 17:08:04 +00:00
|
|
|
|
2004-07-10 21:37:30 +00:00
|
|
|
#ifdef GDB
|
2000-10-06 05:20:02 +00:00
|
|
|
static void r_debug_state(struct r_debug *dummy_one,
|
|
|
|
struct link_map *dummy_two);
|
2000-04-24 17:08:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A list of loaded modules for GDB to use for loading symbols.
|
|
|
|
*/
|
|
|
|
struct r_debug r_debug;
|
|
|
|
|
2000-10-06 05:20:02 +00:00
|
|
|
#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL);
|
2000-04-24 17:08:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Function for the debugger to set a breakpoint on to gain control.
|
|
|
|
*/
|
2002-09-28 17:15:38 +00:00
|
|
|
static void
|
2000-10-06 05:20:02 +00:00
|
|
|
r_debug_state(struct r_debug *dummy_one __unused,
|
|
|
|
struct link_map *dummy_two __unused)
|
2000-04-24 17:08:04 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2002-10-19 18:43:37 +00:00
|
|
|
static void
|
|
|
|
link_elf_add_gdb(struct link_map *l)
|
|
|
|
{
|
|
|
|
struct link_map *prev;
|
|
|
|
|
2002-10-19 18:59:33 +00:00
|
|
|
l->l_next = NULL;
|
|
|
|
|
|
|
|
if (r_debug.r_map == NULL) {
|
|
|
|
/* Add first. */
|
|
|
|
l->l_prev = NULL;
|
|
|
|
r_debug.r_map = l;
|
|
|
|
} else {
|
|
|
|
/* Append to list. */
|
|
|
|
for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next)
|
|
|
|
;
|
|
|
|
l->l_prev = prev;
|
|
|
|
prev->l_next = l;
|
|
|
|
}
|
2002-10-19 18:43:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
link_elf_delete_gdb(struct link_map *l)
|
|
|
|
{
|
|
|
|
if (l->l_prev == NULL) {
|
2002-10-19 18:59:33 +00:00
|
|
|
/* Remove first. */
|
2002-10-19 18:43:37 +00:00
|
|
|
if ((r_debug.r_map = l->l_next) != NULL)
|
|
|
|
l->l_next->l_prev = NULL;
|
2002-10-19 18:59:33 +00:00
|
|
|
} else {
|
|
|
|
/* Remove any but first. */
|
|
|
|
if ((l->l_prev->l_next = l->l_next) != NULL)
|
|
|
|
l->l_next->l_prev = l->l_prev;
|
2002-10-19 18:43:37 +00:00
|
|
|
}
|
|
|
|
}
|
2004-07-10 21:37:30 +00:00
|
|
|
#endif /* GDB */
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2002-04-21 21:08:30 +00:00
|
|
|
#ifdef __ia64__
|
|
|
|
Elf_Addr link_elf_get_gp(linker_file_t);
|
|
|
|
#endif
|
|
|
|
|
2008-07-11 01:10:40 +00:00
|
|
|
/*
|
|
|
|
* The kernel symbol table starts here.
|
|
|
|
*/
|
|
|
|
extern struct _dynamic _DYNAMIC;
|
|
|
|
|
2002-10-19 18:43:37 +00:00
|
|
|
static void
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(const char *filename, const char *s)
|
2002-10-19 18:43:37 +00:00
|
|
|
{
|
2008-07-08 23:51:38 +00:00
|
|
|
if (filename == NULL)
|
|
|
|
printf("kldload: %s\n", s);
|
|
|
|
else
|
|
|
|
printf("kldload: %s: %s\n", filename, s);
|
2002-10-19 18:43:37 +00:00
|
|
|
}
|
|
|
|
|
2002-10-19 18:59:33 +00:00
|
|
|
/*
|
|
|
|
* Actions performed after linking/loading both the preloaded kernel and any
|
|
|
|
* modules; whether preloaded or dynamicly loaded.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
link_elf_link_common_finish(linker_file_t lf)
|
|
|
|
{
|
2004-07-10 21:37:30 +00:00
|
|
|
#ifdef GDB
|
2002-10-19 18:59:33 +00:00
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
char *newfilename;
|
|
|
|
#endif
|
2002-10-19 19:16:03 +00:00
|
|
|
int error;
|
|
|
|
|
|
|
|
/* Notify MD code that a module is being loaded. */
|
|
|
|
error = elf_cpu_load_file(lf);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
2002-10-19 18:59:33 +00:00
|
|
|
|
2004-07-10 21:37:30 +00:00
|
|
|
#ifdef GDB
|
2002-10-19 18:59:33 +00:00
|
|
|
GDB_STATE(RT_ADD);
|
|
|
|
ef->gdb.l_addr = lf->address;
|
2003-02-19 05:47:46 +00:00
|
|
|
newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK);
|
2002-10-19 18:59:33 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
static void
|
|
|
|
link_elf_init(void* arg)
|
|
|
|
{
|
1998-10-09 23:55:31 +00:00
|
|
|
Elf_Dyn *dp;
|
|
|
|
caddr_t modptr, baseptr, sizeptr;
|
|
|
|
elf_file_t ef;
|
|
|
|
char *modname;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
linker_add_class(&link_elf_class);
|
1998-08-24 08:25:26 +00:00
|
|
|
|
1998-10-09 23:55:31 +00:00
|
|
|
dp = (Elf_Dyn*) &_DYNAMIC;
|
2001-09-03 04:39:38 +00:00
|
|
|
modname = NULL;
|
2003-04-06 05:20:00 +00:00
|
|
|
modptr = preload_search_by_type("elf" __XSTRING(__ELF_WORD_SIZE) " kernel");
|
|
|
|
if (modptr == NULL)
|
|
|
|
modptr = preload_search_by_type("elf kernel");
|
2001-09-03 04:39:38 +00:00
|
|
|
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");
|
2002-10-19 18:59:33 +00:00
|
|
|
|
2001-09-03 04:39:38 +00:00
|
|
|
ef = (elf_file_t) linker_kernel_file;
|
|
|
|
ef->preloaded = 1;
|
|
|
|
ef->address = 0;
|
2000-04-24 17:08:04 +00:00
|
|
|
#ifdef SPARSE_MAPPING
|
2001-09-03 04:39:38 +00:00
|
|
|
ef->object = 0;
|
2000-04-24 17:08:04 +00:00
|
|
|
#endif
|
2001-09-03 04:39:38 +00:00
|
|
|
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);
|
2000-04-24 17:08:04 +00:00
|
|
|
|
2004-07-10 21:37:30 +00:00
|
|
|
#ifdef GDB
|
2002-10-19 18:59:33 +00:00
|
|
|
r_debug.r_map = NULL;
|
2001-09-03 04:39:38 +00:00
|
|
|
r_debug.r_brk = r_debug_state;
|
|
|
|
r_debug.r_state = RT_CONSISTENT;
|
2000-04-24 17:08:04 +00:00
|
|
|
#endif
|
2002-10-19 18:59:33 +00:00
|
|
|
|
|
|
|
(void)link_elf_link_common_finish(linker_kernel_file);
|
2007-02-26 16:48:14 +00:00
|
|
|
linker_kernel_file->flags |= LINKER_FILE_LINKED;
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
2006-07-14 22:39:18 +00:00
|
|
|
SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_THIRD, link_elf_init, 0);
|
1998-08-24 08:25:26 +00:00
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
static int
|
2000-04-29 13:19:31 +00:00
|
|
|
link_elf_preload_parse_symbols(elf_file_t ef)
|
1998-10-12 09:13:50 +00:00
|
|
|
{
|
|
|
|
caddr_t pointer;
|
|
|
|
caddr_t ssym, esym, base;
|
|
|
|
caddr_t strtab;
|
|
|
|
int strcnt;
|
|
|
|
Elf_Sym* symtab;
|
|
|
|
int symcnt;
|
|
|
|
|
1998-10-13 09:27:00 +00:00
|
|
|
if (ef->modptr == NULL)
|
|
|
|
return 0;
|
1998-10-12 09:13:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
static int
|
2000-04-24 17:08:04 +00:00
|
|
|
parse_dynamic(elf_file_t ef)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
2000-04-24 17:08:04 +00:00
|
|
|
Elf_Dyn *dp;
|
1998-09-11 08:46:15 +00:00
|
|
|
int plttype = DT_REL;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
|
|
|
|
switch (dp->d_tag) {
|
|
|
|
case DT_HASH:
|
|
|
|
{
|
|
|
|
/* From src/libexec/rtld-elf/rtld.c */
|
2001-07-31 03:46:39 +00:00
|
|
|
const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
|
1998-08-24 08:25:26 +00:00
|
|
|
(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);
|
1998-08-24 08:25:26 +00:00
|
|
|
break;
|
1998-10-12 09:13:50 +00:00
|
|
|
case DT_STRSZ:
|
|
|
|
ef->strsz = dp->d_un.d_val;
|
|
|
|
break;
|
1998-08-24 08:25:26 +00:00
|
|
|
case DT_SYMTAB:
|
1998-09-11 08:46:15 +00:00
|
|
|
ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr);
|
1998-08-24 08:25:26 +00:00
|
|
|
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;
|
2004-07-10 21:37:30 +00:00
|
|
|
#ifdef GDB
|
2000-04-24 17:08:04 +00:00
|
|
|
case DT_DEBUG:
|
|
|
|
dp->d_un.d_ptr = (Elf_Addr) &r_debug;
|
|
|
|
break;
|
|
|
|
#endif
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
ef->ddbsymtab = ef->symtab;
|
|
|
|
ef->ddbsymcnt = ef->nchains;
|
|
|
|
ef->ddbstrtab = ef->strtab;
|
|
|
|
ef->ddbstrcnt = ef->strsz;
|
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
static int
|
|
|
|
parse_dpcpu(elf_file_t ef)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
ef->pcpu_start = 0;
|
|
|
|
ef->pcpu_stop = 0;
|
|
|
|
error = link_elf_lookup_set(&ef->lf, "pcpu", (void ***)&ef->pcpu_start,
|
|
|
|
(void ***)&ef->pcpu_stop, &count);
|
|
|
|
/* Error just means there is no pcpu set to relocate. */
|
|
|
|
if (error)
|
|
|
|
return (0);
|
|
|
|
count *= sizeof(void *);
|
|
|
|
/*
|
|
|
|
* Allocate space in the primary pcpu area. Copy in our initialization
|
|
|
|
* from the data section and then initialize all per-cpu storage from
|
|
|
|
* that.
|
|
|
|
*/
|
|
|
|
ef->pcpu_base = (Elf_Addr)(uintptr_t)dpcpu_alloc(count);
|
|
|
|
if (ef->pcpu_base == (Elf_Addr)NULL)
|
|
|
|
return (ENOSPC);
|
|
|
|
memcpy((void *)ef->pcpu_base, (void *)ef->pcpu_start, count);
|
|
|
|
dpcpu_copy((void *)ef->pcpu_base, count);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-10-09 23:55:31 +00:00
|
|
|
static int
|
2000-04-29 13:19:31 +00:00
|
|
|
link_elf_link_preload(linker_class_t cls,
|
|
|
|
const char* filename, linker_file_t *result)
|
1998-10-09 23:55:31 +00:00
|
|
|
{
|
|
|
|
caddr_t modptr, baseptr, sizeptr, dynptr;
|
|
|
|
char *type;
|
|
|
|
elf_file_t ef;
|
|
|
|
linker_file_t lf;
|
|
|
|
int error;
|
|
|
|
vm_offset_t dp;
|
|
|
|
|
2000-04-29 13:19:31 +00:00
|
|
|
/* Look to see if we have the file preloaded */
|
1998-10-09 23:55:31 +00:00
|
|
|
modptr = preload_search_by_name(filename);
|
|
|
|
if (modptr == NULL)
|
2000-04-29 13:19:31 +00:00
|
|
|
return ENOENT;
|
1998-10-09 23:55:31 +00:00
|
|
|
|
|
|
|
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);
|
2003-04-06 05:20:00 +00:00
|
|
|
if (type == NULL ||
|
|
|
|
(strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " module") != 0 &&
|
|
|
|
strcmp(type, "elf module") != 0))
|
1998-10-09 23:55:31 +00:00
|
|
|
return (EFTYPE);
|
|
|
|
if (baseptr == NULL || sizeptr == NULL || dynptr == NULL)
|
|
|
|
return (EINVAL);
|
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
lf = linker_make_file(filename, &link_elf_class);
|
|
|
|
if (lf == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
ef = (elf_file_t) lf;
|
|
|
|
ef->preloaded = 1;
|
1998-10-13 09:27:00 +00:00
|
|
|
ef->modptr = modptr;
|
1998-10-09 23:55:31 +00:00
|
|
|
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;
|
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
error = parse_dynamic(ef);
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
if (error == 0)
|
|
|
|
error = parse_dpcpu(ef);
|
1998-10-09 23:55:31 +00:00
|
|
|
if (error) {
|
2004-07-13 19:36:59 +00:00
|
|
|
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
|
1998-10-09 23:55:31 +00:00
|
|
|
return error;
|
|
|
|
}
|
2003-01-21 02:42:44 +00:00
|
|
|
link_elf_reloc_local(lf);
|
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;
|
|
|
|
|
|
|
|
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;
|
1998-10-09 23:55:31 +00:00
|
|
|
}
|
2000-04-29 13:19:31 +00:00
|
|
|
#endif
|
2000-04-24 17:08:04 +00:00
|
|
|
error = relocate_file(ef);
|
2000-04-29 13:19:31 +00:00
|
|
|
if (error)
|
1998-10-09 23:55:31 +00:00
|
|
|
return error;
|
2000-04-29 13:19:31 +00:00
|
|
|
(void)link_elf_preload_parse_symbols(ef);
|
2000-04-24 17:08:04 +00:00
|
|
|
|
2002-10-19 18:59:33 +00:00
|
|
|
return (link_elf_link_common_finish(lf));
|
1998-10-09 23:55:31 +00:00
|
|
|
}
|
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
static int
|
2002-10-19 18:59:33 +00:00
|
|
|
link_elf_load_file(linker_class_t cls, const char* filename,
|
|
|
|
linker_file_t* result)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
|
|
|
struct nameidata nd;
|
2001-09-12 08:38:13 +00:00
|
|
|
struct thread* td = curthread; /* XXX */
|
1998-11-06 15:16:07 +00:00
|
|
|
Elf_Ehdr *hdr;
|
|
|
|
caddr_t firstpage;
|
1998-09-11 08:46:15 +00:00
|
|
|
int nbytes, i;
|
|
|
|
Elf_Phdr *phdr;
|
|
|
|
Elf_Phdr *phlimit;
|
2005-10-17 23:21:55 +00:00
|
|
|
Elf_Phdr *segs[MAXSEGS];
|
1998-09-11 08:46:15 +00:00
|
|
|
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;
|
1998-08-24 08:25:26 +00:00
|
|
|
int error = 0;
|
2000-07-04 03:34:11 +00:00
|
|
|
int resid, flags;
|
1998-08-24 08:25:26 +00:00
|
|
|
elf_file_t ef;
|
|
|
|
linker_file_t lf;
|
1998-10-13 09:27:00 +00:00
|
|
|
Elf_Shdr *shdr;
|
|
|
|
int symtabindex;
|
|
|
|
int symstrindex;
|
|
|
|
int symcnt;
|
|
|
|
int strcnt;
|
2006-06-20 21:31:38 +00:00
|
|
|
int vfslocked;
|
1998-10-13 09:27:00 +00:00
|
|
|
|
|
|
|
shdr = NULL;
|
|
|
|
lf = NULL;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2006-06-20 21:31:38 +00:00
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_SYSSPACE, filename, td);
|
2000-07-04 03:34:11 +00:00
|
|
|
flags = FREAD;
|
2007-05-31 11:51:53 +00:00
|
|
|
error = vn_open(&nd, &flags, 0, NULL);
|
1998-08-24 08:25:26 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2006-06-20 21:31:38 +00:00
|
|
|
vfslocked = NDHASGIANT(&nd);
|
1999-12-15 23:02:35 +00:00
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
2008-03-15 17:40:18 +00:00
|
|
|
if (nd.ni_vp->v_type != VREG) {
|
|
|
|
error = ENOEXEC;
|
|
|
|
firstpage = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
2002-11-19 22:12:42 +00:00
|
|
|
#ifdef MAC
|
2007-10-24 19:04:04 +00:00
|
|
|
error = mac_kld_check_load(curthread->td_ucred, nd.ni_vp);
|
2002-11-19 22:12:42 +00:00
|
|
|
if (error) {
|
|
|
|
firstpage = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
#endif
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
/*
|
1998-09-11 08:46:15 +00:00
|
|
|
* Read the elf header from the file.
|
1998-08-24 08:25:26 +00:00
|
|
|
*/
|
2003-02-19 05:47:46 +00:00
|
|
|
firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
|
1998-11-06 15:16:07 +00:00
|
|
|
if (firstpage == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
hdr = (Elf_Ehdr *)firstpage;
|
|
|
|
error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
|
|
|
|
&resid, td);
|
1998-11-06 15:16:07 +00:00
|
|
|
nbytes = PAGE_SIZE - resid;
|
1998-08-24 08:25:26 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
1998-11-06 15:16:07 +00:00
|
|
|
if (!IS_ELF(*hdr)) {
|
1998-09-11 08:46:15 +00:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
1998-11-06 15:16:07 +00:00
|
|
|
if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
|
|
|
|
|| hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "Unsupported file layout");
|
1998-09-11 08:46:15 +00:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto out;
|
|
|
|
}
|
1998-11-06 15:16:07 +00:00
|
|
|
if (hdr->e_ident[EI_VERSION] != EV_CURRENT
|
|
|
|
|| hdr->e_version != EV_CURRENT) {
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "Unsupported file version");
|
1998-09-11 08:46:15 +00:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto out;
|
|
|
|
}
|
1998-11-06 15:16:07 +00:00
|
|
|
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
|
2009-02-10 15:50:19 +00:00
|
|
|
error = ENOSYS;
|
1998-09-11 08:46:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
1998-11-06 15:16:07 +00:00
|
|
|
if (hdr->e_machine != ELF_TARG_MACH) {
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "Unsupported machine");
|
1998-09-11 08:46:15 +00:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto out;
|
|
|
|
}
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
/*
|
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.
|
1998-08-24 08:25:26 +00:00
|
|
|
*/
|
1998-11-06 15:16:07 +00:00
|
|
|
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)))
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "Unreadable program headers");
|
1998-09-11 08:46:15 +00:00
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
/*
|
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.
|
1998-08-24 08:25:26 +00:00
|
|
|
*/
|
1998-11-06 15:16:07 +00:00
|
|
|
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:
|
2005-10-17 23:21:55 +00:00
|
|
|
if (nsegs == MAXSEGS) {
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "Too many sections");
|
1998-09-11 08:46:15 +00:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto out;
|
|
|
|
}
|
2003-05-12 15:08:10 +00:00
|
|
|
/*
|
|
|
|
* XXX: We just trust they come in right order ??
|
|
|
|
*/
|
1998-09-11 08:46:15 +00:00
|
|
|
segs[nsegs] = phdr;
|
|
|
|
++nsegs;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PT_PHDR:
|
|
|
|
phphdr = phdr;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PT_DYNAMIC:
|
|
|
|
phdyn = phdr;
|
|
|
|
break;
|
2000-09-06 02:21:43 +00:00
|
|
|
|
|
|
|
case PT_INTERP:
|
2009-02-10 15:50:19 +00:00
|
|
|
error = ENOSYS;
|
2000-09-06 02:21:43 +00:00
|
|
|
goto out;
|
1998-09-11 08:46:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
++phdr;
|
|
|
|
}
|
|
|
|
if (phdyn == NULL) {
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "Object is not dynamically-linked");
|
1998-09-11 08:46:15 +00:00
|
|
|
error = ENOEXEC;
|
1998-08-24 08:25:26 +00:00
|
|
|
goto out;
|
1998-09-11 08:46:15 +00:00
|
|
|
}
|
2005-10-17 23:21:55 +00:00
|
|
|
if (nsegs == 0) {
|
2008-07-08 23:51:38 +00:00
|
|
|
link_elf_error(filename, "No sections");
|
2003-05-12 15:08:10 +00:00
|
|
|
error = ENOEXEC;
|
|
|
|
goto out;
|
|
|
|
}
|
1998-08-24 08:25:26 +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-08-24 08:25:26 +00:00
|
|
|
*/
|
1998-09-11 08:46:15 +00:00
|
|
|
base_offset = trunc_page(segs[0]->p_offset);
|
|
|
|
base_vaddr = trunc_page(segs[0]->p_vaddr);
|
2005-10-17 23:21:55 +00:00
|
|
|
base_vlimit = round_page(segs[nsegs - 1]->p_vaddr +
|
|
|
|
segs[nsegs - 1]->p_memsz);
|
1998-09-11 08:46:15 +00:00
|
|
|
mapsize = base_vlimit - base_vaddr;
|
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
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) {
|
|
|
|
error = ENOMEM;
|
1998-08-24 08:25:26 +00:00
|
|
|
goto out;
|
|
|
|
}
|
1998-09-11 08:46:15 +00:00
|
|
|
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);
|
2000-04-24 17:08:04 +00:00
|
|
|
ef->object = 0;
|
1998-09-11 08:46:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
#else
|
2003-02-19 05:47:46 +00:00
|
|
|
ef->address = malloc(mapsize, M_LINKER, M_WAITOK);
|
2000-04-24 17:08:04 +00:00
|
|
|
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.
|
|
|
|
*/
|
2005-10-17 23:21:55 +00:00
|
|
|
for (i = 0; i < nsegs; i++) {
|
1998-09-11 08:46:15 +00:00
|
|
|
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,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
|
|
|
|
&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);
|
2005-08-28 00:19:54 +00:00
|
|
|
|
|
|
|
#ifdef SPARSE_MAPPING
|
|
|
|
/*
|
|
|
|
* Wire down the pages
|
|
|
|
*/
|
2005-08-28 04:50:11 +00:00
|
|
|
error = vm_map_wire(kernel_map,
|
2005-08-28 00:19:54 +00:00
|
|
|
(vm_offset_t) segbase,
|
|
|
|
(vm_offset_t) segbase + segs[i]->p_memsz,
|
|
|
|
VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES);
|
2005-08-28 04:50:11 +00:00
|
|
|
if (error != KERN_SUCCESS) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-08-28 00:19:54 +00:00
|
|
|
#endif
|
1998-09-11 08:46:15 +00:00
|
|
|
}
|
|
|
|
|
2001-10-30 15:08:51 +00:00
|
|
|
#ifdef GPROF
|
|
|
|
/* Update profiling information with the new text segment. */
|
2006-06-21 20:42:08 +00:00
|
|
|
mtx_lock(&Giant);
|
2001-10-30 15:08:51 +00:00
|
|
|
kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr +
|
|
|
|
segs[0]->p_memsz));
|
2006-06-21 20:42:08 +00:00
|
|
|
mtx_unlock(&Giant);
|
2001-10-30 15:08:51 +00:00
|
|
|
#endif
|
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr);
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
lf->address = ef->address;
|
1998-09-11 08:46:15 +00:00
|
|
|
lf->size = mapsize;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
error = parse_dynamic(ef);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error)
|
1998-10-09 23:55:31 +00:00
|
|
|
goto out;
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
error = parse_dpcpu(ef);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
2003-01-21 02:42:44 +00:00
|
|
|
link_elf_reloc_local(lf);
|
|
|
|
|
2008-08-03 13:33:45 +00:00
|
|
|
VOP_UNLOCK(nd.ni_vp, 0);
|
2001-11-16 21:08:40 +00:00
|
|
|
error = linker_load_dependencies(lf);
|
2008-08-03 13:33:45 +00:00
|
|
|
vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error)
|
1998-10-09 23:55:31 +00:00
|
|
|
goto out;
|
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
|
2000-04-24 17:08:04 +00:00
|
|
|
error = relocate_file(ef);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Try and load the symbol table if it's present. (you can strip it!) */
|
1998-11-06 15:16:07 +00:00
|
|
|
nbytes = hdr->e_shnum * hdr->e_shentsize;
|
|
|
|
if (nbytes == 0 || hdr->e_shoff == 0)
|
1998-10-13 09:27:00 +00:00
|
|
|
goto nosyms;
|
2003-02-19 05:47:46 +00:00
|
|
|
shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (shdr == NULL) {
|
|
|
|
error = ENOMEM;
|
1998-08-24 08:25:26 +00:00
|
|
|
goto out;
|
|
|
|
}
|
1998-10-13 09:27:00 +00:00
|
|
|
error = vn_rdwr(UIO_READ, nd.ni_vp,
|
1998-11-06 15:16:07 +00:00
|
|
|
(caddr_t)shdr, nbytes, hdr->e_shoff,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
|
|
|
|
&resid, td);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
symtabindex = -1;
|
|
|
|
symstrindex = -1;
|
1998-11-06 15:16:07 +00:00
|
|
|
for (i = 0; i < hdr->e_shnum; i++) {
|
1998-10-13 09:27:00 +00:00
|
|
|
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;
|
2003-02-19 05:47:46 +00:00
|
|
|
ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK);
|
1998-10-13 09:27:00 +00:00
|
|
|
strcnt = shdr[symstrindex].sh_size;
|
2003-02-19 05:47:46 +00:00
|
|
|
ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK);
|
1998-10-13 09:27:00 +00:00
|
|
|
|
|
|
|
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,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
|
|
|
|
&resid, td);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
error = vn_rdwr(UIO_READ, nd.ni_vp,
|
|
|
|
ef->strbase, strcnt, shdr[symstrindex].sh_offset,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
|
|
|
|
&resid, td);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ef->ddbsymcnt = symcnt / sizeof(Elf_Sym);
|
|
|
|
ef->ddbsymtab = (const Elf_Sym *)ef->symbase;
|
|
|
|
ef->ddbstrcnt = strcnt;
|
|
|
|
ef->ddbstrtab = ef->strbase;
|
|
|
|
|
2002-10-19 18:59:33 +00:00
|
|
|
error = link_elf_link_common_finish(lf);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
2000-04-24 17:08:04 +00:00
|
|
|
|
1998-10-13 09:27:00 +00:00
|
|
|
nosyms:
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
*result = lf;
|
|
|
|
|
|
|
|
out:
|
1998-10-13 09:27:00 +00:00
|
|
|
if (error && lf)
|
2004-07-13 19:36:59 +00:00
|
|
|
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
|
1998-10-13 09:27:00 +00:00
|
|
|
if (shdr)
|
|
|
|
free(shdr, M_LINKER);
|
1998-11-06 15:16:07 +00:00
|
|
|
if (firstpage)
|
|
|
|
free(firstpage, M_LINKER);
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(nd.ni_vp, 0);
|
2002-02-27 18:32:23 +00:00
|
|
|
vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
|
2006-06-20 21:31:38 +00:00
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
Elf_Addr
|
|
|
|
elf_relocaddr(linker_file_t lf, Elf_Addr x)
|
|
|
|
{
|
|
|
|
elf_file_t ef;
|
|
|
|
|
|
|
|
ef = (elf_file_t)lf;
|
|
|
|
if (x >= ef->pcpu_start && x < ef->pcpu_stop)
|
|
|
|
return ((x - ef->pcpu_start) + ef->pcpu_base);
|
|
|
|
return (x);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-08-24 08:25:26 +00:00
|
|
|
static void
|
1998-10-09 23:55:31 +00:00
|
|
|
link_elf_unload_file(linker_file_t file)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
2000-04-24 17:08:04 +00:00
|
|
|
elf_file_t ef = (elf_file_t) file;
|
|
|
|
|
Implement a facility for dynamic per-cpu variables.
- Modules and kernel code alike may use DPCPU_DEFINE(),
DPCPU_GET(), DPCPU_SET(), etc. akin to the statically defined
PCPU_*. Requires only one extra instruction more than PCPU_* and is
virtually the same as __thread for builtin and much faster for shared
objects. DPCPU variables can be initialized when defined.
- Modules are supported by relocating the module's per-cpu linker set
over space reserved in the kernel. Modules may fail to load if there
is insufficient space available.
- Track space available for modules with a one-off extent allocator.
Free may block for memory to allocate space for an extent.
Reviewed by: jhb, rwatson, kan, sam, grehan, marius, marcel, stas
2009-06-23 22:42:39 +00:00
|
|
|
if (ef->pcpu_base) {
|
|
|
|
dpcpu_free((void *)ef->pcpu_base, ef->pcpu_stop - ef->pcpu_start);
|
|
|
|
}
|
2004-07-10 21:37:30 +00:00
|
|
|
#ifdef GDB
|
2000-04-24 17:08:04 +00:00
|
|
|
if (ef->gdb.l_ld) {
|
|
|
|
GDB_STATE(RT_DELETE);
|
2001-09-10 07:53:04 +00:00
|
|
|
free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER);
|
2000-04-24 17:08:04 +00:00
|
|
|
link_elf_delete_gdb(&ef->gdb);
|
|
|
|
GDB_STATE(RT_CONSISTENT);
|
|
|
|
}
|
|
|
|
#endif
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2002-10-19 19:16:03 +00:00
|
|
|
/* Notify MD code that a module is being unloaded. */
|
|
|
|
elf_cpu_unload_file(file);
|
|
|
|
|
2000-04-24 17:08:04 +00:00
|
|
|
if (ef->preloaded) {
|
2000-04-29 13:19:31 +00:00
|
|
|
link_elf_unload_preload(file);
|
2000-04-24 17:08:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2002-10-19 18:59:33 +00:00
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
#ifdef SPARSE_MAPPING
|
2000-04-24 17:08:04 +00:00
|
|
|
if (ef->object) {
|
|
|
|
vm_map_remove(kernel_map, (vm_offset_t) ef->address,
|
|
|
|
(vm_offset_t) ef->address
|
|
|
|
+ (ef->object->size << PAGE_SHIFT));
|
|
|
|
}
|
1998-09-11 08:46:15 +00:00
|
|
|
#else
|
2000-04-24 17:08:04 +00:00
|
|
|
if (ef->address)
|
|
|
|
free(ef->address, M_LINKER);
|
1998-09-11 08:46:15 +00:00
|
|
|
#endif
|
2000-04-24 17:08:04 +00:00
|
|
|
if (ef->symbase)
|
|
|
|
free(ef->symbase, M_LINKER);
|
|
|
|
if (ef->strbase)
|
|
|
|
free(ef->strbase, M_LINKER);
|
2008-05-23 00:49:39 +00:00
|
|
|
if (ef->ctftab)
|
|
|
|
free(ef->ctftab, M_LINKER);
|
|
|
|
if (ef->ctfoff)
|
|
|
|
free(ef->ctfoff, M_LINKER);
|
|
|
|
if (ef->typoff)
|
|
|
|
free(ef->typoff, M_LINKER);
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
1998-10-09 23:55:31 +00:00
|
|
|
static void
|
2000-04-29 13:19:31 +00:00
|
|
|
link_elf_unload_preload(linker_file_t file)
|
1998-10-09 23:55:31 +00:00
|
|
|
{
|
|
|
|
if (file->filename)
|
|
|
|
preload_delete_name(file->filename);
|
|
|
|
}
|
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
static const char *
|
2005-12-18 04:52:37 +00:00
|
|
|
symbol_name(elf_file_t ef, Elf_Size r_info)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
1998-09-11 08:46:15 +00:00
|
|
|
const Elf_Sym *ref;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
1998-10-16 03:55:01 +00:00
|
|
|
if (ELF_R_SYM(r_info)) {
|
|
|
|
ref = ef->symtab + ELF_R_SYM(r_info);
|
1998-10-15 17:16:24 +00:00
|
|
|
return ef->strtab + ref->st_name;
|
1998-09-11 08:46:15 +00:00
|
|
|
} else
|
|
|
|
return NULL;
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2000-04-24 17:08:04 +00:00
|
|
|
relocate_file(elf_file_t ef)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
1998-09-11 08:46:15 +00:00
|
|
|
const Elf_Rel *rellim;
|
|
|
|
const Elf_Rel *rel;
|
|
|
|
const Elf_Rela *relalim;
|
|
|
|
const Elf_Rela *rela;
|
1998-10-16 03:55:01 +00:00
|
|
|
const char *symname;
|
1998-09-11 08:46:15 +00:00
|
|
|
|
|
|
|
/* Perform relocations without addend if there are any: */
|
1998-10-16 03:55:01 +00:00
|
|
|
rel = ef->rel;
|
|
|
|
if (rel) {
|
1999-01-29 09:04:27 +00:00
|
|
|
rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize);
|
1998-10-16 03:55:01 +00:00
|
|
|
while (rel < rellim) {
|
2004-05-16 20:00:28 +00:00
|
|
|
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL,
|
|
|
|
elf_lookup)) {
|
2002-04-25 01:22:16 +00:00
|
|
|
symname = symbol_name(ef, rel->r_info);
|
2000-04-29 11:21:44 +00:00
|
|
|
printf("link_elf: symbol %s undefined\n", symname);
|
1998-10-16 03:55:01 +00:00
|
|
|
return ENOENT;
|
1998-12-31 09:17:20 +00:00
|
|
|
}
|
1998-10-16 03:55:01 +00:00
|
|
|
rel++;
|
|
|
|
}
|
1998-09-11 08:46:15 +00:00
|
|
|
}
|
1998-08-24 08:25:26 +00:00
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
/* Perform relocations with addend if there are any: */
|
1998-10-16 03:55:01 +00:00
|
|
|
rela = ef->rela;
|
|
|
|
if (rela) {
|
1999-01-29 09:04:27 +00:00
|
|
|
relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize);
|
1998-10-16 03:55:01 +00:00
|
|
|
while (rela < relalim) {
|
2004-05-16 20:00:28 +00:00
|
|
|
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA,
|
|
|
|
elf_lookup)) {
|
2002-04-25 01:22:16 +00:00
|
|
|
symname = symbol_name(ef, rela->r_info);
|
2000-04-29 11:21:44 +00:00
|
|
|
printf("link_elf: symbol %s undefined\n", symname);
|
1998-10-16 03:55:01 +00:00
|
|
|
return ENOENT;
|
1998-12-31 09:17:20 +00:00
|
|
|
}
|
1998-10-16 03:55:01 +00:00
|
|
|
rela++;
|
|
|
|
}
|
1998-09-11 08:46:15 +00:00
|
|
|
}
|
1998-08-24 08:25:26 +00:00
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
/* Perform PLT relocations without addend if there are any: */
|
1998-10-16 03:55:01 +00:00
|
|
|
rel = ef->pltrel;
|
|
|
|
if (rel) {
|
1999-01-29 09:04:27 +00:00
|
|
|
rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize);
|
1998-10-16 03:55:01 +00:00
|
|
|
while (rel < rellim) {
|
2004-05-16 20:00:28 +00:00
|
|
|
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL,
|
|
|
|
elf_lookup)) {
|
2002-04-25 01:22:16 +00:00
|
|
|
symname = symbol_name(ef, rel->r_info);
|
2000-04-29 11:21:44 +00:00
|
|
|
printf("link_elf: symbol %s undefined\n", symname);
|
1998-10-16 03:55:01 +00:00
|
|
|
return ENOENT;
|
1998-12-31 09:17:20 +00:00
|
|
|
}
|
1998-10-16 03:55:01 +00:00
|
|
|
rel++;
|
|
|
|
}
|
1998-09-11 08:46:15 +00:00
|
|
|
}
|
1998-08-24 08:25:26 +00:00
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
/* Perform relocations with addend if there are any: */
|
1998-10-16 03:55:01 +00:00
|
|
|
rela = ef->pltrela;
|
|
|
|
if (rela) {
|
1999-01-29 09:04:27 +00:00
|
|
|
relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize);
|
1998-10-16 03:55:01 +00:00
|
|
|
while (rela < relalim) {
|
2004-05-16 20:00:28 +00:00
|
|
|
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA,
|
|
|
|
elf_lookup)) {
|
2002-04-25 01:22:16 +00:00
|
|
|
symname = symbol_name(ef, rela->r_info);
|
2000-04-29 11:21:44 +00:00
|
|
|
printf("link_elf: symbol %s undefined\n", symname);
|
1998-10-16 03:55:01 +00:00
|
|
|
return ENOENT;
|
1998-12-31 09:17:20 +00:00
|
|
|
}
|
1998-10-16 03:55:01 +00:00
|
|
|
rela++;
|
|
|
|
}
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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-08-24 08:25:26 +00:00
|
|
|
{
|
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;
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
2002-09-28 17:15:38 +00:00
|
|
|
static int
|
1999-01-27 21:50:00 +00:00
|
|
|
link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
2000-04-24 17:08:04 +00:00
|
|
|
elf_file_t ef = (elf_file_t) lf;
|
1998-09-11 08:46:15 +00:00
|
|
|
unsigned long symnum;
|
1998-10-12 09:13:50 +00:00
|
|
|
const Elf_Sym* symp;
|
|
|
|
const char *strp;
|
1998-09-11 08:46:15 +00:00
|
|
|
unsigned long hash;
|
|
|
|
int i;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2005-10-31 19:17:32 +00:00
|
|
|
/* If we don't have a hash, bail. */
|
|
|
|
if (ef->buckets == NULL || ef->nbuckets == 0) {
|
|
|
|
printf("link_elf_lookup_symbol: missing symbol hash table\n");
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
/* 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-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
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)) {
|
1999-01-27 21:50:00 +00:00
|
|
|
*sym = (c_linker_sym_t) symp;
|
1998-09-11 08:46:15 +00:00
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
symnum = ef->chains[symnum];
|
|
|
|
}
|
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
/* 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)) {
|
1999-01-27 21:50:00 +00:00
|
|
|
*sym = (c_linker_sym_t) symp;
|
1998-10-12 09:13:50 +00:00
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-11 08:46:15 +00:00
|
|
|
return ENOENT;
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
1998-10-09 23:55:31 +00:00
|
|
|
static int
|
1999-01-27 23:45:44 +00:00
|
|
|
link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
2000-04-24 17:08:04 +00:00
|
|
|
elf_file_t ef = (elf_file_t) lf;
|
1999-01-28 00:57:57 +00:00
|
|
|
const Elf_Sym* es = (const Elf_Sym*) sym;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
2002-08-24 05:01:33 +00:00
|
|
|
if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) {
|
1998-10-12 09:13:50 +00:00
|
|
|
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;
|
2002-08-24 05:01:33 +00:00
|
|
|
if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
|
1998-10-12 09:13:50 +00:00
|
|
|
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;
|
1998-08-24 08:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
link_elf_search_symbol(linker_file_t lf, caddr_t value,
|
1999-01-27 21:50:00 +00:00
|
|
|
c_linker_sym_t* sym, long* diffp)
|
1998-08-24 08:25:26 +00:00
|
|
|
{
|
2000-04-24 17:08:04 +00:00
|
|
|
elf_file_t ef = (elf_file_t) lf;
|
1999-12-24 15:33:36 +00:00
|
|
|
u_long off = (uintptr_t) (void *) value;
|
1998-08-24 08:25:26 +00:00
|
|
|
u_long diff = off;
|
1999-12-24 15:33:36 +00:00
|
|
|
u_long st_value;
|
1998-09-11 08:46:15 +00:00
|
|
|
const Elf_Sym* es;
|
|
|
|
const Elf_Sym* best = 0;
|
1998-08-24 08:25:26 +00:00
|
|
|
int i;
|
|
|
|
|
1998-10-12 09:13:50 +00:00
|
|
|
for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
|
1998-08-24 08:25:26 +00:00
|
|
|
if (es->st_name == 0)
|
|
|
|
continue;
|
1999-12-24 15:33:36 +00:00
|
|
|
st_value = es->st_value + (uintptr_t) (void *) ef->address;
|
1999-11-28 11:59:18 +00:00
|
|
|
if (off >= st_value) {
|
|
|
|
if (off - st_value < diff) {
|
|
|
|
diff = off - st_value;
|
1998-08-24 08:25:26 +00:00
|
|
|
best = es;
|
|
|
|
if (diff == 0)
|
|
|
|
break;
|
1999-11-28 11:59:18 +00:00
|
|
|
} else if (off - st_value == diff) {
|
1998-08-24 08:25:26 +00:00
|
|
|
best = es;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (best == 0)
|
|
|
|
*diffp = off;
|
|
|
|
else
|
|
|
|
*diffp = diff;
|
1999-01-27 21:50:00 +00:00
|
|
|
*sym = (c_linker_sym_t) best;
|
1998-08-24 08:25:26 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2001-06-13 10:58:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
2003-02-19 05:47:46 +00:00
|
|
|
setsym = malloc(len, M_LINKER, M_WAITOK);
|
2001-06-13 10:58:39 +00:00
|
|
|
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;
|
|
|
|
}
|
2001-10-30 15:21:45 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2002-04-21 21:08:30 +00:00
|
|
|
|
2008-05-23 00:49:39 +00:00
|
|
|
static int
|
|
|
|
link_elf_each_function_nameval(linker_file_t file,
|
|
|
|
linker_function_nameval_callback_t callback, void *opaque)
|
|
|
|
{
|
|
|
|
linker_symval_t symval;
|
|
|
|
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 = link_elf_symbol_values(file, (c_linker_sym_t) symp, &symval);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
error = callback(file, i, &symval, opaque);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2002-04-21 21:08:30 +00:00
|
|
|
#ifdef __ia64__
|
|
|
|
/*
|
|
|
|
* Each KLD has its own GP. The GP value for each load module is given by
|
|
|
|
* DT_PLTGOT on ia64. We need GP to construct function descriptors, but
|
|
|
|
* don't have direct access to the ELF file structure. The link_elf_get_gp()
|
|
|
|
* function returns the GP given a pointer to a generic linker file struct.
|
|
|
|
*/
|
|
|
|
Elf_Addr
|
|
|
|
link_elf_get_gp(linker_file_t lf)
|
|
|
|
{
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
return (Elf_Addr)ef->got;
|
|
|
|
}
|
|
|
|
#endif
|
2002-04-25 01:22:16 +00:00
|
|
|
|
2002-09-27 23:12:53 +00:00
|
|
|
const Elf_Sym *
|
2005-12-18 04:52:37 +00:00
|
|
|
elf_get_sym(linker_file_t lf, Elf_Size symidx)
|
2002-09-27 23:12:53 +00:00
|
|
|
{
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
|
|
|
|
if (symidx >= ef->nchains)
|
|
|
|
return (NULL);
|
|
|
|
return (ef->symtab + symidx);
|
|
|
|
}
|
|
|
|
|
2002-10-15 05:40:07 +00:00
|
|
|
const char *
|
2005-12-18 04:52:37 +00:00
|
|
|
elf_get_symname(linker_file_t lf, Elf_Size symidx)
|
2002-10-15 05:40:07 +00:00
|
|
|
{
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
const Elf_Sym *sym;
|
|
|
|
|
|
|
|
if (symidx >= ef->nchains)
|
|
|
|
return (NULL);
|
|
|
|
sym = ef->symtab + symidx;
|
|
|
|
return (ef->strtab + sym->st_name);
|
|
|
|
}
|
|
|
|
|
2002-04-25 01:22:16 +00:00
|
|
|
/*
|
|
|
|
* Symbol lookup function that can be used when the symbol index is known (ie
|
|
|
|
* in relocations). It uses the symbol index instead of doing a fully fledged
|
|
|
|
* hash table based lookup when such is valid. For example for local symbols.
|
|
|
|
* This is not only more efficient, it's also more correct. It's not always
|
|
|
|
* the case that the symbol can be found through the hash table.
|
|
|
|
*/
|
2004-05-16 20:00:28 +00:00
|
|
|
static Elf_Addr
|
2005-12-18 04:52:37 +00:00
|
|
|
elf_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
2002-04-25 01:22:16 +00:00
|
|
|
{
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
const Elf_Sym *sym;
|
|
|
|
const char *symbol;
|
|
|
|
|
|
|
|
/* Don't even try to lookup the symbol if the index is bogus. */
|
|
|
|
if (symidx >= ef->nchains)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
sym = ef->symtab + symidx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't do a full lookup when the symbol is local. It may even
|
|
|
|
* fail because it may not be found through the hash table.
|
|
|
|
*/
|
|
|
|
if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
|
|
|
|
/* Force lookup failure when we have an insanity. */
|
|
|
|
if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0)
|
|
|
|
return (0);
|
|
|
|
return ((Elf_Addr)ef->address + sym->st_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX we can avoid doing a hash table based lookup for global
|
|
|
|
* symbols as well. This however is not always valid, so we'll
|
|
|
|
* just do it the hard way for now. Performance tweaks can
|
|
|
|
* always be added.
|
|
|
|
*/
|
|
|
|
|
|
|
|
symbol = ef->strtab + sym->st_name;
|
|
|
|
|
|
|
|
/* Force a lookup failure if the symbol name is bogus. */
|
|
|
|
if (*symbol == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
|
|
|
|
}
|
2003-01-21 02:42:44 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
link_elf_reloc_local(linker_file_t lf)
|
|
|
|
{
|
|
|
|
const Elf_Rel *rellim;
|
|
|
|
const Elf_Rel *rel;
|
|
|
|
const Elf_Rela *relalim;
|
|
|
|
const Elf_Rela *rela;
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
|
|
|
|
/* Perform relocations without addend if there are any: */
|
|
|
|
if ((rel = ef->rel) != NULL) {
|
|
|
|
rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize);
|
|
|
|
while (rel < rellim) {
|
2004-05-16 20:00:28 +00:00
|
|
|
elf_reloc_local(lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL,
|
|
|
|
elf_lookup);
|
2003-01-21 02:42:44 +00:00
|
|
|
rel++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform relocations with addend if there are any: */
|
|
|
|
if ((rela = ef->rela) != NULL) {
|
|
|
|
relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize);
|
|
|
|
while (rela < relalim) {
|
2004-05-16 20:00:28 +00:00
|
|
|
elf_reloc_local(lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA,
|
|
|
|
elf_lookup);
|
2003-01-21 02:42:44 +00:00
|
|
|
rela++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-26 21:39:09 +00:00
|
|
|
|
|
|
|
static long
|
|
|
|
link_elf_symtab_get(linker_file_t lf, const Elf_Sym **symtab)
|
|
|
|
{
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
|
|
|
|
*symtab = ef->ddbsymtab;
|
|
|
|
|
|
|
|
if (*symtab == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
return (ef->ddbsymcnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
link_elf_strtab_get(linker_file_t lf, caddr_t *strtab)
|
|
|
|
{
|
|
|
|
elf_file_t ef = (elf_file_t)lf;
|
|
|
|
|
|
|
|
*strtab = ef->ddbstrtab;
|
|
|
|
|
|
|
|
if (*strtab == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
return (ef->ddbstrcnt);
|
|
|
|
}
|