#endif
#ifdef KLD_DEBUG
-int kld_debug = 0;
+int kld_debug = 1;
#endif
/* Metadata from the static kernel */
static linker_file_list_t linker_files;
static int next_file_id = 1;
+/* XXX wrong name; we're looking at version provision tags here, not modules */
+typedef TAILQ_HEAD(, modlist) modlisthead_t;
+struct modlist {
+ TAILQ_ENTRY(modlist) link; /* chain together all modules */
+ linker_file_t container;
+ const char *name;
+ int version;
+};
+typedef struct modlist *modlist_t;
+static modlisthead_t found_modules;
+
+
+static int linker_load_module(const char *kldname, const char *modname,
+ struct linker_file *parent, struct mod_depend *verinfo,
+ struct linker_file **lfpp);
+
+static char *
+linker_strdup(const char *str)
+{
+ char *result;
+
+ result = kmalloc(strlen(str) + 1, M_LINKER, M_WAITOK);
+ strcpy(result, str);
+ return(result);
+}
+
static void
linker_init(void* arg)
{
return 0;
}
-static int
+static void
linker_file_sysinit(linker_file_t lf)
{
struct sysinit** start, ** stop;
struct sysinit** sipp;
struct sysinit** xipp;
struct sysinit* save;
- const moduledata_t *moddata;
- int error;
KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
lf->filename));
if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0)
- return 0; /* XXX is this correct ? No sysinit ? */
+ return;
- /* HACK ALERT! */
- for (sipp = start; sipp < stop; sipp++) {
- if ((*sipp)->func == module_register_init) {
- moddata = (*sipp)->udata;
- error = module_register(moddata, lf);
- if (error) {
- kprintf("linker_file_sysinit \"%s\" failed to register! %d\n",
- lf->filename, error);
- return error;
- }
- }
- }
-
/*
* Perform a bubble sort of the system initialization objects by
* their subsystem (primary key) and order (secondary key).
/* Call function */
(*((*sipp)->func))((*sipp)->udata);
}
- return 0; /* no errors */
}
static void
sysctl_unregister_oid(*oidp);
}
+static int
+linker_file_register_modules(linker_file_t lf)
+{
+ struct mod_metadata **start, **stop, **mdp;
+ const moduledata_t *moddata;
+ int first_error, error;
+
+ KLD_DPF(FILE, ("linker_file_register_modules: registering modules in %s\n",
+ lf->filename));
+
+ if (linker_file_lookup_set(lf, "modmetadata_set", &start, &stop, NULL) != 0) {
+ /*
+ * This fallback should be unnecessary, but if we get booted
+ * from boot2 instead of loader and we are missing our
+ * metadata then we have to try the best we can.
+ */
+ if (lf == linker_kernel_file) {
+ start = SET_BEGIN(modmetadata_set);
+ stop = SET_LIMIT(modmetadata_set);
+ } else
+ return (0);
+ }
+ first_error = 0;
+ for (mdp = start; mdp < stop; mdp++) {
+ if ((*mdp)->md_type != MDT_MODULE)
+ continue;
+ moddata = (*mdp)->md_data;
+ KLD_DPF(FILE, ("Registering module %s in %s\n", moddata->name, lf->filename));
+ error = module_register(moddata, lf);
+ if (error) {
+ kprintf("Module %s failed to register: %d\n", moddata->name, error);
+ if (first_error == 0)
+ first_error = error;
+ }
+ }
+ return (first_error);
+}
+
+static void
+linker_init_kernel_modules(void)
+{
+
+ linker_file_register_modules(linker_kernel_file);
+}
+
+SYSINIT(linker_kernel, SI_BOOT2_KLD, SI_ORDER_ANY, linker_init_kernel_modules, 0);
+
int
-linker_load_file(const char *filename, linker_file_t *result, int load_flags)
+linker_load_file(const char *filename, linker_file_t *result)
{
linker_class_t lc;
linker_file_t lf;
int foundfile, error = 0;
- char *koname = NULL;
/* Refuse to load modules if securelevel raised */
if (securelevel > 0 || kernel_mem_readonly)
lf->refs++;
goto out;
}
- if (find_mod_metadata(filename)) {
- if (linker_kernel_file)
- ++linker_kernel_file->refs;
- *result = linker_kernel_file;
- goto out;
- }
- koname = kmalloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
- ksprintf(koname, "%s.ko", filename);
lf = NULL;
foundfile = 0;
TAILQ_FOREACH(lc, &classes, link) {
KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n",
filename, lc->desc));
- /* First with .ko */
- error = lc->ops->load_file(koname, &lf, load_flags);
- if (lf == NULL && error == ENOENT) {
- /* Then try without */
- error = lc->ops->load_file(filename, &lf, load_flags);
- }
+ error = lc->ops->load_file(filename, &lf);
/*
* If we got something other than ENOENT, then it exists but we cannot
* load it for some other reason.
*/
if (error != ENOENT)
foundfile = 1;
-
- /*
- * Finish up. If this is part of a preload chain the sysinits
- * have to be installed for later execution, otherwise we run
- * them immediately.
- */
if (lf) {
- if (load_flags & LINKER_LOAD_FILE_PRELOAD) {
- struct sysinit **si_start, **si_stop;
- struct sysinit **sipp;
- const moduledata_t *moddata;
-
- error = 0;
- if (linker_file_lookup_set(lf, "sysinit_set",
- &si_start, &si_stop, NULL) == 0) {
- for (sipp = si_start; sipp < si_stop; sipp++) {
- if ((*sipp)->func == module_register_init) {
- moddata = (*sipp)->udata;
- error = module_register(moddata, lf);
- if (error) {
- kprintf("Preloaded dependency \"%s\" "
- "failed to register: %d\n",
- koname, error);
- }
- }
- }
- sysinit_add(si_start, si_stop);
- }
- } else {
- error = linker_file_sysinit(lf);
+ error = linker_file_register_modules(lf);
+ if (error == EEXIST) {
+ linker_file_unload(lf /* , LINKER_UNLOAD_FORCE */);
+ return (error);
}
-
linker_file_register_sysctls(lf);
+ linker_file_sysinit(lf);
+ lf->flags |= LINKER_FILE_LINKED;
*result = lf;
- goto out;
+ return (0);
}
}
/*
*/
if (foundfile) {
/*
+ * If the file type has not been recognized by the last try
+ * printout a message before to fail.
+ */
+ if (error == ENOSYS)
+ kprintf("linker_load_file: Unsupported file type\n");
+
+ /*
* Format not recognized or otherwise unloadable.
* When loading a module that is statically built into
* the kernel EEXIST percolates back up as the return
}
out:
- if (koname)
- kfree(koname, M_LINKER);
return error;
}
linker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops)
{
linker_file_t lf = 0;
- int namelen;
const char *filename;
filename = rindex(pathname, '/');
KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
lockmgr(&lock, LK_EXCLUSIVE);
- namelen = strlen(filename) + 1;
- lf = kmalloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
+ lf = kmalloc(sizeof(struct linker_file), M_LINKER, M_WAITOK);
bzero(lf, sizeof(*lf));
lf->refs = 1;
lf->userrefs = 0;
lf->flags = 0;
- lf->filename = (char*) (lf + 1);
- strcpy(lf->filename, filename);
+ lf->filename = linker_strdup(filename);
lf->id = next_file_id++;
lf->ndeps = 0;
lf->deps = NULL;
int
linker_file_unload(linker_file_t file)
{
- module_t mod, next;
+ module_t mod, next;;
+ modlist_t ml, nextml;
struct common_symbol* cp;
int error = 0;
int i;
return EPERM;
KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
+
lockmgr(&lock, LK_EXCLUSIVE);
- if (file->refs == 1) {
- KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
- /*
- * Temporarily bump file->refs to prevent recursive unloading
- */
- ++file->refs;
- /*
- * Inform any modules associated with this file.
- */
- mod = TAILQ_FIRST(&file->modules);
- while (mod) {
- /*
- * Give the module a chance to veto the unload. Note that the
- * act of unloading the module may cause other modules in the
- * same file list to be unloaded recursively.
- */
- if ((error = module_unload(mod)) != 0) {
- KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n",
- mod));
- lockmgr(&lock, LK_RELEASE);
- file->refs--;
- goto out;
- }
+ /* Easy case of just dropping a reference. */
+ if (file->refs > 1) {
+ file->refs--;
+ lockmgr(&lock, LK_RELEASE);
+ return (0);
+ }
- /*
- * Recursive relationships may prevent the release from
- * immediately removing the module, or may remove other
- * modules in the list.
- */
- next = module_getfnext(mod);
- module_release(mod);
- mod = next;
- }
+ KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
+
+ /*
+ * Inform any modules associated with this file.
+ */
+ mod = TAILQ_FIRST(&file->modules);
+ for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) {
+ next = module_getfnext(mod);
/*
- * Since we intend to destroy the file structure, we expect all
- * modules to have been removed by now.
+ * Give the module a chance to veto the unload. Note that the
+ * act of unloading the module may cause other modules in the
+ * same file list to be unloaded recursively.
*/
- for (mod = TAILQ_FIRST(&file->modules);
- mod;
- mod = module_getfnext(mod)
- ) {
- kprintf("linker_file_unload: module %p still has refs!\n", mod);
+ if ((error = module_unload(mod)) != 0) {
+ KLD_DPF(FILE, ("linker_file_unload: module %p vetoes unload\n",
+ mod));
+ lockmgr(&lock, LK_RELEASE);
+ file->refs--;
+ goto out;
}
- --file->refs;
+ module_release(mod);
}
- file->refs--;
- if (file->refs > 0) {
- lockmgr(&lock, LK_RELEASE);
- goto out;
+ TAILQ_FOREACH_MUTABLE(ml, &found_modules, link, nextml) {
+ if (ml->container == file) {
+ TAILQ_REMOVE(&found_modules, ml, link);
+ kfree(ml, M_LINKER);
+ }
}
/* Don't try to run SYSUNINITs if we are unloaded due to a link error */
if (file->flags & LINKER_FILE_LINKED) {
+ file->flags &= ~LINKER_FILE_LINKED;
+ lockmgr(&lock, LK_RELEASE);
linker_file_sysuninit(file);
linker_file_unregister_sysctls(file);
+ lockmgr(&lock, LK_EXCLUSIVE);
}
TAILQ_REMOVE(&linker_files, file, link);
- lockmgr(&lock, LK_RELEASE);
- for (i = 0; i < file->ndeps; i++)
- linker_file_unload(file->deps[i]);
- kfree(file->deps, M_LINKER);
+ if (file->deps) {
+ lockmgr(&lock, LK_RELEASE);
+ for (i = 0; i < file->ndeps; i++)
+ linker_file_unload(file->deps[i]);
+ lockmgr(&lock, LK_EXCLUSIVE);
+ kfree(file->deps, M_LINKER);
+ file->deps = NULL;
+ }
- for (cp = STAILQ_FIRST(&file->common); cp;
- cp = STAILQ_FIRST(&file->common)) {
- STAILQ_REMOVE(&file->common, cp, common_symbol, link);
+ while ((cp = STAILQ_FIRST(&file->common)) != NULL) {
+ STAILQ_REMOVE_HEAD(&file->common, link);
kfree(cp, M_LINKER);
}
file->ops->unload(file);
+
+ if (file->filename) {
+ kfree(file->filename, M_LINKER);
+ file->filename = NULL;
+ }
+
kfree(file, M_LINKER);
+ lockmgr(&lock, LK_RELEASE);
+
out:
return error;
}
size_t common_size = 0;
int i;
- KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n",
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%p, name=%s, deps=%d\n",
file, name, deps));
if (file->ops->lookup_symbol(file, name, &sym) == 0) {
*/
common_size = symval.size;
} else {
- KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value));
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%p\n", symval.value));
*raddr = symval.value;
return 0;
}
if (deps) {
for (i = 0; i < file->ndeps; i++) {
if (linker_file_lookup_symbol(file->deps[i], name, 0, raddr) == 0) {
- KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", *raddr));
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%p\n", *raddr));
return 0;
}
}
if (i < file->ndeps)
continue;
if (linker_file_lookup_symbol(lf, name, 0, raddr) == 0) {
- KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", *raddr));
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%p\n", *raddr));
return 0;
}
}
STAILQ_FOREACH(cp, &file->common, link)
if (!strcmp(cp->name, name)) {
- KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address));
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%p\n", cp->address));
*raddr = cp->address;
return 0;
}
bzero(cp->address, common_size);
STAILQ_INSERT_TAIL(&file->common, cp, link);
- KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address));
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%p\n", cp->address));
*raddr = cp->address;
return 0;
}
sys_kldload(struct kldload_args *uap)
{
struct thread *td = curthread;
- char* filename = NULL, *modulename;
+ char *file;
+ char *kldname, *modname;
linker_file_t lf;
int error = 0;
if ((error = priv_check(td, PRIV_KLD_LOAD)) != 0)
return error;
- filename = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- if ((error = copyinstr(uap->file, filename, MAXPATHLEN, NULL)) != 0)
+ file = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+ if ((error = copyinstr(uap->file, file, MAXPATHLEN, NULL)) != 0)
goto out;
- /* Can't load more than one module with the same name */
- modulename = rindex(filename, '/');
- if (modulename == NULL)
- modulename = filename;
- else
- modulename++;
- if (linker_find_file_by_name(modulename)) {
- error = EEXIST;
- goto out;
+ /*
+ * If file does not contain a qualified name or any dot in it
+ * (kldname.ko, or kldname.ver.ko) treat it as an interface
+ * name.
+ */
+ if (index(file, '/') || index(file, '.')) {
+ kldname = file;
+ modname = NULL;
+ } else {
+ kldname = NULL;
+ modname = file;
}
- if ((error = linker_load_file(filename, &lf, 0)) != 0)
+ if ((error = linker_load_module(kldname, modname, NULL, NULL, &lf)) != 0)
goto out;
lf->userrefs++;
uap->sysmsg_result = lf->id;
out:
- if (filename)
- kfree(filename, M_TEMP);
+ if (file)
+ kfree(file, M_TEMP);
return error;
}
linker_file_t lf;
int error = 0;
- if (uap->fileid == 0) {
- if (TAILQ_FIRST(&linker_files))
- uap->sysmsg_result = TAILQ_FIRST(&linker_files)->id;
- else
- uap->sysmsg_result = 0;
- return 0;
+ if (uap->fileid == 0)
+ lf = TAILQ_FIRST(&linker_files);
+ else {
+ lf = linker_find_file_by_id(uap->fileid);
+ if (lf == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ lf = TAILQ_NEXT(lf, link);
}
- lf = linker_find_file_by_id(uap->fileid);
- if (lf) {
- if (TAILQ_NEXT(lf, link))
- uap->sysmsg_result = TAILQ_NEXT(lf, link)->id;
- else
- uap->sysmsg_result = 0;
- } else
- error = ENOENT;
+ /* Skip partially loaded files. */
+ while (lf != NULL && !(lf->flags & LINKER_FILE_LINKED)) {
+ lf = TAILQ_NEXT(lf, link);
+ }
+
+ if (lf)
+ uap->sysmsg_result = lf->id;
+ else
+ uap->sysmsg_result = 0;
+out:
return error;
}
}
/*
- * Look for module metadata in the static kernel
+ * Preloaded module support
*/
-struct mod_metadata *
-find_mod_metadata(const char *modname)
+
+static modlist_t
+modlist_lookup(const char *name, int ver)
{
- int len;
- struct mod_metadata **mdp;
- struct mod_metadata *mdt;
+ modlist_t mod;
- /*
- * Strip path prefixes and any dot extension. MDT_MODULE names
- * are just the module name without a path or ".ko".
- */
- for (len = strlen(modname) - 1; len >= 0; --len) {
- if (modname[len] == '/')
- break;
+ TAILQ_FOREACH(mod, &found_modules, link) {
+ if (strcmp(mod->name, name) == 0 && (ver == 0 || mod->version == ver))
+ return (mod);
}
- modname += len + 1;
- for (len = 0; modname[len] && modname[len] != '.'; ++len)
- ;
+ return (NULL);
+}
- /*
- * Look for the module declaration
- */
- SET_FOREACH(mdp, modmetadata_set) {
- mdt = *mdp;
- if (mdt->md_type != MDT_MODULE)
+static modlist_t
+modlist_lookup2(const char *name, struct mod_depend *verinfo)
+{
+ modlist_t mod, bestmod;
+ int ver;
+
+ if (verinfo == NULL)
+ return (modlist_lookup(name, 0));
+ bestmod = NULL;
+ TAILQ_FOREACH(mod, &found_modules, link) {
+ if (strcmp(mod->name, name) != 0)
+ continue;
+ ver = mod->version;
+ if (ver == verinfo->md_ver_preferred)
+ return (mod);
+ if (ver >= verinfo->md_ver_minimum &&
+ ver <= verinfo->md_ver_maximum &&
+ (bestmod == NULL || ver > bestmod->version))
+ bestmod = mod;
+ }
+ return (bestmod);
+}
+
+static modlist_t
+modlist_newmodule(const char *modname, int version, linker_file_t container)
+{
+ modlist_t mod;
+
+ mod = kmalloc(sizeof(struct modlist), M_LINKER, M_NOWAIT | M_ZERO);
+ if (mod == NULL)
+ panic("no memory for module list");
+ mod->container = container;
+ mod->name = modname;
+ mod->version = version;
+ TAILQ_INSERT_TAIL(&found_modules, mod, link);
+ return (mod);
+}
+
+static void
+linker_addmodules(linker_file_t lf, struct mod_metadata **start,
+ struct mod_metadata **stop, int preload)
+{
+ struct mod_metadata *mp, **mdp;
+ const char *modname;
+ int ver;
+
+ for (mdp = start; mdp < stop; mdp++) {
+ mp = *mdp;
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = mp->md_cval;
+ ver = ((struct mod_version *)mp->md_data)->mv_version;
+ if (modlist_lookup(modname, ver) != NULL) {
+ kprintf("module %s already present!\n", modname);
+ /* XXX what can we do? this is a build error. :-( */
continue;
- if (strlen(mdt->md_cval) == len &&
- strncmp(mdt->md_cval, modname, len) == 0) {
- return(mdt);
}
+ modlist_newmodule(modname, ver, lf);
}
- return(NULL);
}
-/*
- * Preloaded module support
- */
static void
linker_preload(void* arg)
{
caddr_t modptr;
- char *modname;
+ const char *modname, *nmodname;
char *modtype;
- linker_file_t lf;
+ linker_file_t lf, nlf;
linker_class_t lc;
int error;
- struct sysinit **sipp;
- const moduledata_t *moddata;
+ linker_file_list_t loaded_files;
+ linker_file_list_t depended_files;
+ struct mod_metadata *mp, *nmp;
+ struct mod_metadata **start, **stop, **mdp, **nmdp;
+ struct mod_depend *verinfo;
+ int nver;
+ int resolves;
+ modlist_t mod;
struct sysinit **si_start, **si_stop;
+ TAILQ_INIT(&loaded_files);
+ TAILQ_INIT(&depended_files);
+ TAILQ_INIT(&found_modules);
+
modptr = NULL;
while ((modptr = preload_search_next_name(modptr)) != NULL) {
modname = (char *)preload_search_info(modptr, MODINFO_NAME);
continue;
}
- /*
- * This is a hack at the moment, but what's in FreeBSD-5 is even
- * worse so I'd rather the hack.
- */
- kprintf("Preloaded %s \"%s\" at %p", modtype, modname, modptr);
- if (find_mod_metadata(modname)) {
- kprintf(" (ignored, already in static kernel)\n");
- continue;
- }
- kprintf(".\n");
-
- lf = linker_find_file_by_name(modname);
- if (lf) {
- lf->refs++;
- lf->userrefs++;
- continue;
- }
+ if (bootverbose)
+ kprintf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr);
lf = NULL;
TAILQ_FOREACH(lc, &classes, link) {
- error = lc->ops->load_file(modname, &lf, LINKER_LOAD_FILE_PRELOAD);
- if (error) {
- lf = NULL;
+ error = lc->ops->preload_file(modname, &lf);
+ if (!error)
break;
- }
+ lf = NULL;
}
- if (lf) {
- lf->userrefs++;
+ if (lf)
+ TAILQ_INSERT_TAIL(&loaded_files, lf, loaded);
+ }
- if (linker_file_lookup_set(lf, "sysinit_set", &si_start, &si_stop, NULL) == 0) {
- /* HACK ALERT!
- * This is to set the sysinit moduledata so that the module
- * can attach itself to the correct containing file.
- * The sysinit could be run at *any* time.
+ /*
+ * First get a list of stuff in the kernel.
+ */
+ if (linker_file_lookup_set(linker_kernel_file, MDT_SETNAME, &start,
+ &stop, NULL) == 0)
+ linker_addmodules(linker_kernel_file, start, stop, 1);
+
+ /*
+ * This is a once-off kinky bubble sort to resolve relocation
+ * dependency requirements.
+ */
+restart:
+ TAILQ_FOREACH(lf, &loaded_files, loaded) {
+ error = linker_file_lookup_set(lf, MDT_SETNAME, &start, &stop, NULL);
+ /*
+ * First, look to see if we would successfully link with this
+ * stuff.
+ */
+ resolves = 1; /* unless we know otherwise */
+ if (!error) {
+ for (mdp = start; mdp < stop; mdp++) {
+ mp = *mdp;
+ if (mp->md_type != MDT_DEPEND)
+ continue;
+ modname = mp->md_cval;
+ verinfo = mp->md_data;
+ for (nmdp = start; nmdp < stop; nmdp++) {
+ nmp = *nmdp;
+ if (nmp->md_type != MDT_VERSION)
+ continue;
+ nmodname = nmp->md_cval;
+ if (strcmp(modname, nmodname) == 0)
+ break;
+ }
+ if (nmdp < stop)/* it's a self reference */
+ continue;
+
+ /*
+ * ok, the module isn't here yet, we
+ * are not finished
*/
- for (sipp = si_start; sipp < si_stop; sipp++) {
- if ((*sipp)->func == module_register_init) {
- moddata = (*sipp)->udata;
- error = module_register(moddata, lf);
- if (error)
- kprintf("Preloaded %s \"%s\" failed to register: %d\n",
- modtype, modname, error);
+ if (modlist_lookup2(modname, verinfo) == NULL)
+ resolves = 0;
+ }
+ }
+ /*
+ * OK, if we found our modules, we can link. So, "provide"
+ * the modules inside and add it to the end of the link order
+ * list.
+ */
+ if (resolves) {
+ if (!error) {
+ for (mdp = start; mdp < stop; mdp++) {
+ mp = *mdp;
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = mp->md_cval;
+ nver = ((struct mod_version *)mp->md_data)->mv_version;
+ if (modlist_lookup(modname, nver) != NULL) {
+ kprintf("module %s already present!\n", modname);
+ TAILQ_REMOVE(&loaded_files, lf, loaded);
+ linker_file_unload(lf /* , LINKER_UNLOAD_FORCE */ );
+ /* we changed tailq next ptr */
+ goto restart;
}
+ modlist_newmodule(modname, nver, lf);
}
- sysinit_add(si_start, si_stop);
}
- linker_file_register_sysctls(lf);
+ TAILQ_REMOVE(&loaded_files, lf, loaded);
+ TAILQ_INSERT_TAIL(&depended_files, lf, loaded);
+ /*
+ * Since we provided modules, we need to restart the
+ * sort so that the previous files that depend on us
+ * have a chance. Also, we've busted the tailq next
+ * pointer with the REMOVE.
+ */
+ goto restart;
+ }
+ }
+
+ /*
+ * At this point, we check to see what could not be resolved..
+ */
+ while ((lf = TAILQ_FIRST(&loaded_files)) != NULL) {
+ TAILQ_REMOVE(&loaded_files, lf, loaded);
+ kprintf("KLD file %s is missing dependencies\n", lf->filename);
+ linker_file_unload(lf /* , LINKER_UNLOAD_FORCE */ );
+ }
+
+ /*
+ * We made it. Finish off the linking in the order we determined.
+ */
+ TAILQ_FOREACH_MUTABLE(lf, &depended_files, loaded, nlf) {
+ if (linker_kernel_file) {
+ linker_kernel_file->refs++;
+ linker_file_add_dependancy(lf, linker_kernel_file);
+ }
+ lf->userrefs++;
+
+ error = linker_file_lookup_set(lf, MDT_SETNAME, &start, &stop, NULL);
+ if (!error) {
+ for (mdp = start; mdp < stop; mdp++) {
+ mp = *mdp;
+ if (mp->md_type != MDT_DEPEND)
+ continue;
+ modname = mp->md_cval;
+ verinfo = mp->md_data;
+ mod = modlist_lookup2(modname, verinfo);
+ /* Don't count self-dependencies */
+ if (lf == mod->container)
+ continue;
+ mod->container->refs++;
+ linker_file_add_dependancy(lf, mod->container);
+ }
+ }
+ /*
+ * Now do relocation etc using the symbol search paths
+ * established by the dependencies
+ */
+ error = lf->ops->preload_finish(lf);
+ if (error) {
+ TAILQ_REMOVE(&depended_files, lf, loaded);
+ kprintf("KLD file %s - could not finalize loading\n",
+ lf->filename);
+ linker_file_unload(lf /* , LINKER_UNLOAD_FORCE */);
+ continue;
}
+ linker_file_register_modules(lf);
+ if (linker_file_lookup_set(lf, "sysinit_set", &si_start, &si_stop, NULL) == 0)
+ sysinit_add(si_start, si_stop);
+ linker_file_register_sysctls(lf);
+ lf->flags |= LINKER_FILE_LINKED;
}
+ /* woohoo! we made it! */
}
SYSINIT(preload, SI_BOOT2_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
sizeof(linker_path), "module load search path");
TUNABLE_STR("module_path", linker_path, sizeof(linker_path));
-static char *
-linker_strdup(const char *str)
-{
- char *result;
-
- result = kmalloc(strlen(str) + 1, M_LINKER, M_WAITOK);
- strcpy(result, str);
- return(result);
-}
-
char *
linker_search_path(const char *name)
{
struct nlookupdata nd;
char *cp, *ep, *result;
size_t name_len, prefix_len;
+ size_t result_len;
int sep;
int error;
enum vtype type;
+ const char *exts[] = { "", ".ko", NULL };
+ const char **ext;
/* qualified at all? */
if (index(name, '/'))
sep = 0;
/*
- * +2 : possible separator, plus terminator.
+ * +2+3 : possible separator, plus terminator + possible extension.
*/
- result = kmalloc(prefix_len + name_len + 2, M_LINKER, M_WAITOK);
+ result = kmalloc(prefix_len + name_len + 2+3, M_LINKER, M_WAITOK);
strncpy(result, cp, prefix_len);
if (sep)
result[prefix_len++] = '/';
strcpy(result + prefix_len, name);
- /*
- * Attempt to open the file, and return the path if we succeed and it's
- * a regular file.
- */
- error = nlookup_init(&nd, result, UIO_SYSSPACE, NLC_FOLLOW|NLC_LOCKVP);
- if (error == 0)
- error = vn_open(&nd, NULL, FREAD, 0);
- if (error == 0) {
- type = nd.nl_open_vp->v_type;
- if (type == VREG) {
- nlookup_done(&nd);
- return (result);
+ result_len = strlen(result);
+ for (ext = exts; *ext != NULL; ext++) {
+ strcpy(result + result_len, *ext);
+
+ /*
+ * Attempt to open the file, and return the path if we succeed and it's
+ * a regular file.
+ */
+ error = nlookup_init(&nd, result, UIO_SYSSPACE, NLC_FOLLOW|NLC_LOCKVP);
+ if (error == 0)
+ error = vn_open(&nd, NULL, FREAD, 0);
+ if (error == 0) {
+ type = nd.nl_open_vp->v_type;
+ if (type == VREG) {
+ nlookup_done(&nd);
+ return (result);
+ }
}
+ nlookup_done(&nd);
}
- nlookup_done(&nd);
+
kfree(result, M_LINKER);
if (*ep == 0)
}
return(NULL);
}
+
+/*
+ * Find a file which contains given module and load it, if "parent" is not
+ * NULL, register a reference to it.
+ */
+static int
+linker_load_module(const char *kldname, const char *modname,
+ struct linker_file *parent, struct mod_depend *verinfo,
+ struct linker_file **lfpp)
+{
+ linker_file_t lfdep;
+ const char *filename;
+ char *pathname;
+ int error;
+
+ if (modname == NULL) {
+ /*
+ * We have to load KLD
+ */
+ KASSERT(verinfo == NULL, ("linker_load_module: verinfo is not NULL"));
+ pathname = linker_search_path(kldname);
+ } else {
+ if (modlist_lookup2(modname, verinfo) != NULL)
+ return (EEXIST);
+ if (kldname != NULL)
+ pathname = linker_strdup(kldname);
+ else if (rootvnode == NULL)
+ pathname = NULL;
+ else
+ pathname = linker_search_path(modname);
+#if 0
+ /*
+ * Need to find a KLD with required module
+ */
+ pathname = linker_search_module(modname,
+ strlen(modname), verinfo);
+#endif
+ }
+ if (pathname == NULL)
+ return (ENOENT);
+
+ /*
+ * Can't load more than one file with the same basename XXX:
+ * Actually it should be possible to have multiple KLDs with
+ * the same basename but different path because they can
+ * provide different versions of the same modules.
+ */
+ filename = rindex(pathname, '/');
+ if (filename == NULL)
+ filename = filename;
+ else
+ filename++;
+ if (linker_find_file_by_name(filename))
+ error = EEXIST;
+ else
+ do {
+ error = linker_load_file(pathname, &lfdep);
+ if (error)
+ break;
+ if (modname && verinfo && modlist_lookup2(modname, verinfo) == NULL) {
+ linker_file_unload(lfdep /* , LINKER_UNLOAD_FORCE */ );
+ error = ENOENT;
+ break;
+ }
+ if (parent) {
+ linker_file_add_dependancy(parent, lfdep);
+ }
+ if (lfpp)
+ *lfpp = lfdep;
+ } while (0);
+ kfree(pathname, M_LINKER);
+ return (error);
+}
+
+/*
+ * This routine is responsible for finding dependencies of userland initiated
+ * kldload(2)'s of files.
+ */
+int
+linker_load_dependencies(linker_file_t lf)
+{
+ linker_file_t lfdep;
+ struct mod_metadata **start, **stop, **mdp, **nmdp;
+ struct mod_metadata *mp, *nmp;
+ struct mod_depend *verinfo;
+ modlist_t mod;
+ const char *modname, *nmodname;
+ int ver, error = 0, count;
+
+ /*
+ * All files are dependant on /kernel.
+ */
+ if (linker_kernel_file) {
+ linker_kernel_file->refs++;
+ linker_file_add_dependancy(lf, linker_kernel_file);
+ }
+ if (linker_file_lookup_set(lf, MDT_SETNAME, &start, &stop, &count) != 0)
+ return (0);
+ for (mdp = start; mdp < stop; mdp++) {
+ mp = *mdp;
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = mp->md_cval;
+ ver = ((struct mod_version *)mp->md_data)->mv_version;
+ mod = modlist_lookup(modname, ver);
+ if (mod != NULL) {
+ kprintf("interface %s.%d already present in the KLD '%s'!\n",
+ modname, ver, mod->container->filename);
+ return (EEXIST);
+ }
+ }
+
+ for (mdp = start; mdp < stop; mdp++) {
+ mp = *mdp;
+ if (mp->md_type != MDT_DEPEND)
+ continue;
+ modname = mp->md_cval;
+ verinfo = mp->md_data;
+ nmodname = NULL;
+ for (nmdp = start; nmdp < stop; nmdp++) {
+ nmp = *nmdp;
+ if (nmp->md_type != MDT_VERSION)
+ continue;
+ nmodname = nmp->md_cval;
+ if (strcmp(modname, nmodname) == 0)
+ break;
+ }
+ if (nmdp < stop) /* early exit, it's a self reference */
+ continue;
+ mod = modlist_lookup2(modname, verinfo);
+ if (mod) { /* woohoo, it's loaded already */
+ lfdep = mod->container;
+ lfdep->refs++;
+ linker_file_add_dependancy(lf, lfdep);
+ continue;
+ }
+ error = linker_load_module(NULL, modname, lf, verinfo, NULL);
+ if (error) {
+ kprintf("KLD %s: depends on %s - not available or version mismatch\n",
+ lf->filename, modname);
+ break;
+ }
+ }
+
+ if (error)
+ return (error);
+ linker_addmodules(lf, start, stop, 0);
+ return (error);
+}
#include <vm/pmap.h>
#include <vm/vm_map.h>
-static int link_elf_load_module(const char*, linker_file_t*, int);
-static int link_elf_load_file(const char*, linker_file_t*, int);
+static int link_elf_preload_file(const char *, linker_file_t *);
+static int link_elf_preload_finish(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*,
c_linker_sym_t*);
static int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*);
static void link_elf_unload_module(linker_file_t);
static int link_elf_lookup_set(linker_file_t, const char *,
void ***, void ***, int *);
+static int elf_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *);
+static void link_elf_reloc_local(linker_file_t lf);
static struct linker_class_ops link_elf_class_ops = {
- link_elf_load_module,
+ link_elf_load_file,
+ link_elf_preload_file,
};
static struct linker_file_ops link_elf_file_ops = {
- link_elf_lookup_symbol,
- link_elf_symbol_values,
- link_elf_search_symbol,
- link_elf_unload_file,
- link_elf_lookup_set
+ .lookup_symbol = link_elf_lookup_symbol,
+ .symbol_values = link_elf_symbol_values,
+ .search_symbol = link_elf_search_symbol,
+ .unload = link_elf_unload_file,
+ .lookup_set = link_elf_lookup_set
};
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,
- link_elf_lookup_set
+ .lookup_symbol = link_elf_lookup_symbol,
+ .symbol_values = link_elf_symbol_values,
+ .search_symbol = link_elf_search_symbol,
+ .preload_finish = link_elf_preload_finish,
+ .unload = link_elf_unload_module,
+ .lookup_set = link_elf_lookup_set,
};
typedef struct elf_file {
} *elf_file_t;
static int parse_dynamic(linker_file_t lf);
-static int load_dependancies(linker_file_t lf, int load_flags);
static int relocate_file(linker_file_t lf);
static int parse_module_symbols(linker_file_t lf);
}
static int
-link_elf_load_module(const char *filename, linker_file_t *result, int load_flags)
+link_elf_preload_file(const char *filename, linker_file_t *result)
{
caddr_t modptr, baseptr, sizeptr, dynptr;
char *type;
*/
modptr = preload_search_by_name(filename);
if (modptr == NULL)
- return (link_elf_load_file(filename, result, load_flags));
+ return ENOENT;
/* 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)
+ if (type == NULL ||
+ (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " module") != 0 &&
+ strcmp(type, "elf module") != 0))
return (EFTYPE);
if (baseptr == NULL || sizeptr == NULL || dynptr == NULL)
return (EINVAL);
linker_file_unload(lf);
return error;
}
- error = load_dependancies(lf, load_flags);
- if (error) {
- linker_file_unload(lf);
- return error;
- }
+ link_elf_reloc_local(lf);
+ *result = lf;
+ return (0);
+}
+
+static int
+link_elf_preload_finish(linker_file_t lf)
+{
+ int error;
+
error = relocate_file(lf);
- if (error) {
- linker_file_unload(lf);
+ if (error)
return error;
- }
parse_module_symbols(lf);
- lf->flags |= LINKER_FILE_LINKED;
- *result = lf;
+
return (0);
}
static int
-link_elf_load_file(const char* filename, linker_file_t* result, int load_flags)
+link_elf_load_file(const char* filename, linker_file_t* result)
{
struct nlookupdata nd;
struct thread *td = curthread; /* XXX */
goto out;
}
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
- link_elf_error("Unsupported file type");
- error = ENOEXEC;
+ error = ENOSYS;
goto out;
}
if (hdr->e_machine != ELF_TARG_MACH) {
case PT_DYNAMIC:
phdyn = phdr;
break;
+
+ case PT_INTERP:
+ error = ENOSYS;
+ goto out;
}
++phdr;
error = parse_dynamic(lf);
if (error)
goto out;
- error = load_dependancies(lf, load_flags);
+ link_elf_reloc_local(lf);
+ error = linker_load_dependencies(lf);
if (error)
goto out;
error = relocate_file(lf);
ef->ddbstrcnt = strcnt;
ef->ddbstrtab = ef->strbase;
- lf->flags |= LINKER_FILE_LINKED;
-
nosyms:
*result = lf;
return error;
}
+Elf_Addr
+elf_relocaddr(linker_file_t lf, Elf_Addr x)
+{
+ elf_file_t ef;
+
+ ef = lf->priv;
+#if 0
+ if (x >= ef->pcpu_start && x < ef->pcpu_stop)
+ return ((x - ef->pcpu_start) + ef->pcpu_base);
+#ifdef VIMAGE
+ if (x >= ef->vnet_start && x < ef->vnet_stop)
+ return ((x - ef->vnet_start) + ef->vnet_base);
+#endif
+#endif
+ return (x);
+}
+
static void
link_elf_unload_file(linker_file_t file)
{
preload_delete_name(file->filename);
}
-static int
-load_dependancies(linker_file_t lf, int load_flags)
-{
- elf_file_t ef = lf->priv;
- linker_file_t lfdep;
- char* name;
- const Elf_Dyn *dp;
- int error = 0;
-
- /*
- * All files are dependant on /kernel.
- */
- if (linker_kernel_file) {
- linker_kernel_file->refs++;
- linker_file_add_dependancy(lf, linker_kernel_file);
- }
-
- for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
- if (dp->d_tag == DT_NEEDED) {
- name = ef->strtab + dp->d_un.d_val;
-
- error = linker_load_file(name, &lfdep, load_flags);
- if (error)
- goto out;
- linker_file_add_dependancy(lf, lfdep);
- }
- }
-
-out:
- return error;
-}
-
static const char *
symbol_name(elf_file_t ef, Elf_Size r_info)
{
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(lf, rel, ELF_RELOC_REL, symname)) {
+ if (elf_reloc(lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, elf_lookup)) {
+ symname = symbol_name(ef, rel->r_info);
kprintf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
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(lf, rela, ELF_RELOC_RELA, symname)) {
+ if (elf_reloc(lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, elf_lookup)) {
+ symname = symbol_name(ef, rela->r_info);
kprintf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
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(lf, rel, ELF_RELOC_REL, symname)) {
+ if (elf_reloc(lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, elf_lookup)) {
+ symname = symbol_name(ef, rel->r_info);
kprintf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize);
while (rela < relalim) {
symname = symbol_name(ef, rela->r_info);
- if (elf_reloc(lf, rela, ELF_RELOC_RELA, symname)) {
+ if (elf_reloc(lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, elf_lookup)) {
kprintf("link_elf: symbol %s undefined\n", symname);
return ENOENT;
}
unsigned long hash;
int i;
+ /* If we don't have a hash, bail. */
+ if (ef->buckets == NULL || ef->nbuckets == 0) {
+ kprintf("link_elf_lookup_symbol: missing symbol hash table\n");
+ return ENOENT;
+ }
+
/* First, search hashed global symbols */
hash = elf_hash(name);
symnum = ef->buckets[hash % ef->nbuckets];
}
static int
-link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval)
+link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t *symval)
{
- elf_file_t ef = lf->priv;
- 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;
- }
+ elf_file_t ef = lf->priv;
+ 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)
+ c_linker_sym_t *sym, long *diffp)
{
- elf_file_t ef = lf->priv;
- u_long off = (uintptr_t) (void *) value;
- u_long diff = off;
- u_long st_value;
- 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;
- }
- }
+ elf_file_t ef = lf->priv;
+ u_long off = (uintptr_t)(void *)value;
+ u_long diff = off;
+ u_long st_value;
+ 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;
+ }
+ if (best == 0)
+ *diffp = off;
+ else
+ *diffp = diff;
+ *sym = (c_linker_sym_t) best;
- return 0;
+ return 0;
}
/*
*/
static int
link_elf_lookup_set(linker_file_t lf, const char *name,
- void ***startp, void ***stopp, int *countp)
+ void ***startp, void ***stopp, int *countp)
{
- c_linker_sym_t sym;
- linker_symval_t symval;
- char *setsym;
- void **start, **stop;
- int len, error = 0, count;
-
- len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
- setsym = kmalloc(len, M_LINKER, M_WAITOK);
-
- /* get address of first entry */
- ksnprintf(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 */
- ksnprintf(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;
+ 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 = kmalloc(len, M_LINKER, M_WAITOK);
+
+ /* get address of first entry */
+ ksnprintf(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 */
+ ksnprintf(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 the number of entries */
+ count = stop - start;
- /* and copy out */
- if (startp)
- *startp = start;
- if (stopp)
- *stopp = stop;
- if (countp)
- *countp = count;
+ /* and copy out */
+ if (startp)
+ *startp = start;
+ if (stopp)
+ *stopp = stop;
+ if (countp)
+ *countp = count;
- out:
- kfree(setsym, M_LINKER);
- return error;
+out:
+ kfree(setsym, M_LINKER);
+ return error;
+}
+
+/*
+ * 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.
+ */
+static int
+elf_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *result)
+{
+ elf_file_t ef = lf->priv;
+ 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 (ENOENT);
+
+ 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 (ENOENT);
+ 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 (ENOENT);
+
+ return (linker_file_lookup_symbol(lf, symbol, deps, (caddr_t *)result));
}
+static void
+link_elf_reloc_local(linker_file_t lf)
+{
+ elf_file_t ef = lf->priv;
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ /* Perform relocations without addend if there are any: */
+ if ((rel = ef->rel) != NULL) {
+ rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize);
+ while (rel < rellim) {
+ elf_reloc_local(lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL,
+ elf_lookup);
+ rel++;
+ }
+ }
+ /* Perform relocations with addend if there are any: */
+ if ((rela = ef->rela) != NULL) {
+ relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize);
+ while (rela < relalim) {
+ elf_reloc_local(lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA,
+ elf_lookup);
+ rela++;
+ }
+ }
+}