summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_linker.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2000-04-29 13:19:31 +0000
committerpeter <peter@FreeBSD.org>2000-04-29 13:19:31 +0000
commit8de950a22b47f2a5a632e462ab0d4818247b2bdf (patch)
tree38b4753b3c3b977d419f65254cb442749845de6e /sys/kern/kern_linker.c
parentc3e5d07abf2d42fb40aaf24a4fe6f2216e01b35c (diff)
downloadFreeBSD-src-8de950a22b47f2a5a632e462ab0d4818247b2bdf.zip
FreeBSD-src-8de950a22b47f2a5a632e462ab0d4818247b2bdf.tar.gz
First round implementation of a fine grain enhanced module to module
version dependency system. This isn't quite finished, but it is at a useful stage to do a functional checkpoint. Highlights: - version and dependency metadata is gathered via linker sets, so things are handled the same for static kernels and code built to live in a kld. - The dependencies are at module level (versus at file level). - Dependencies determine kld symbol search order - this means that you cannot link against symbols in another file unless you depend on it. This is so that you cannot accidently unload the target out from underneath the ones referencing it. - It is flexible enough that we can put tags in #include files and macros so that we can get decent hooks for enforcing recompiles on incompatable ABI changes. eg: if we change struct proc, we could force a recompile for all kld's that reference the proc struct. - Tangled dependency references at boot time are sorted. Files are relocated once all their dependencies are already relocated. Caveats: - Loader support is incomplete, but has been worked on seperately. - Actual enforcement of the version number tags is not active yet - just the module dependencies are live. The actual structure of versioning hasn't been agreed on yet. (eg: major.minor, or whatever) - There is some backwards compatability for old modules without metadata but I'm not sure how good it is. This is based on work originally done by Boris Popov (bp@freebsd.org), but I'm not sure he'd recognize much of it now. Don't blame him. :-) Also, ideas have been borrowed from Mike Smith.
Diffstat (limited to 'sys/kern/kern_linker.c')
-rw-r--r--sys/kern/kern_linker.c610
1 files changed, 480 insertions, 130 deletions
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index a3193e5..7fc8a2f 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -52,8 +52,11 @@
int kld_debug = 0;
#endif
-MALLOC_DEFINE(M_LINKER, "kld", "kernel linker");
-linker_file_t linker_current_file;
+static char *linker_search_path(const char *name);
+static const char *linker_basename(const char* path);
+
+MALLOC_DEFINE(M_LINKER, "linker", "kernel linker");
+
linker_file_t linker_kernel_file;
static struct lock lock; /* lock for the file list */
@@ -61,6 +64,16 @@ static linker_class_list_t classes;
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;
+};
+typedef struct modlist *modlist_t;
+static modlisthead_t found_modules;
+
static char *
linker_strdup(const char *str)
{
@@ -85,7 +98,7 @@ int
linker_add_class(linker_class_t lc)
{
kobj_class_compile((kobj_class_t) lc);
- TAILQ_INSERT_HEAD(&classes, lc, link);
+ TAILQ_INSERT_TAIL(&classes, lc, link);
return 0;
}
@@ -96,8 +109,6 @@ linker_file_sysinit(linker_file_t lf)
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));
@@ -108,18 +119,6 @@ linker_file_sysinit(linker_file_t lf)
KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits));
if (!sysinits)
return;
-
- /* HACK ALERT! */
- for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
- if ((*sipp)->func == module_register_init) {
- moddata = (*sipp)->udata;
- error = module_register(moddata, lf);
- if (error)
- printf("linker_file_sysinit \"%s\" failed to register! %d\n",
- lf->filename, error);
- }
- }
-
/*
* Perform a bubble sort of the system initialization objects by
* their subsystem (primary key) and order (secondary key).
@@ -190,7 +189,6 @@ linker_file_sysuninit(linker_file_t lf)
}
}
-
/*
* Traverse the (now) ordered list of system initialization tasks.
* Perform each task, and continue on to the next task.
@@ -240,13 +238,69 @@ linker_file_unregister_sysctls(linker_file_t lf)
sysctl_unregister_set(sysctls);
}
+static int
+linker_file_register_modules(linker_file_t lf)
+{
+ int error, mcount;
+ struct linker_set *modules;
+ struct mod_metadata **mdpp;
+ const moduledata_t *moddata;
+ struct sysinit **sipp;
+
+ KLD_DPF(FILE, ("linker_file_register_modules: registering modules in %s\n",
+ lf->filename));
+
+ modules = (struct linker_set*)
+ linker_file_lookup_symbol(lf, "modmetadata_set", 0);
+ mcount = 0;
+ if (modules) {
+ for (mdpp = (struct mod_metadata**)modules->ls_items; *mdpp; mdpp++) {
+ if ((*mdpp)->md_type != MDT_MODULE)
+ continue;
+ mcount++;
+ moddata = (*mdpp)->md_data;
+ KLD_DPF(FILE, ("Registering module %s in %s\n",
+ moddata->name, lf->filename));
+ error = module_register(moddata, lf);
+ if (error)
+ printf("Module %s failed to register: %d\n", moddata->name, error);
+ }
+ }
+ if (mcount)
+ return mcount; /* Do not mix old and new style */
+
+ /* Hack - handle old kld's without metadata */
+ modules = (struct linker_set*)
+ linker_file_lookup_symbol(lf, "sysinit_set", 0);
+ if (modules) {
+ for (sipp = (struct sysinit **)modules->ls_items; *sipp; sipp++) {
+ if ((*sipp)->func != module_register_init)
+ continue;
+ mcount++;
+ moddata = (*sipp)->udata;
+ printf("Old-style KLD file %s found\n", moddata->name);
+ error = module_register(moddata, lf);
+ if (error)
+ printf("Old-style KLD file %s failed to register: %d\n", moddata->name, error);
+ }
+ }
+ return mcount;
+}
+
+static void
+linker_init_kernel_modules(void)
+{
+ linker_file_register_modules(linker_kernel_file);
+}
+
+SYSINIT(linker_kernel, SI_SUB_KLD, SI_ORDER_ANY, linker_init_kernel_modules, 0);
+
int
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;
lf = linker_find_file_by_name(filename);
if (lf) {
@@ -256,21 +310,12 @@ linker_load_file(const char* filename, linker_file_t* result)
goto out;
}
- koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
- if (koname == NULL) {
- error = ENOMEM;
- goto out;
- }
- sprintf(koname, "%s.ko", filename);
lf = NULL;
foundfile = 0;
for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n",
filename, lc->desc));
-
- error = LINKER_LOAD_FILE(lc, koname, &lf); /* First with .ko */
- if (lf == NULL && error == ENOENT)
- error = LINKER_LOAD_FILE(lc, filename, &lf); /* Then try without */
+ error = LINKER_LOAD_FILE(lc, filename, &lf);
/*
* If we got something other than ENOENT, then it exists but we cannot
* load it for some other reason.
@@ -278,8 +323,10 @@ linker_load_file(const char* filename, linker_file_t* result)
if (error != ENOENT)
foundfile = 1;
if (lf) {
+ linker_file_register_modules(lf);
linker_file_register_sysctls(lf);
linker_file_sysinit(lf);
+ lf->flags |= LINKER_FILE_LINKED;
*result = lf;
error = 0;
@@ -296,8 +343,6 @@ linker_load_file(const char* filename, linker_file_t* result)
error = ENOENT; /* Nothing found */
out:
- if (koname)
- free(koname, M_LINKER);
return error;
}
@@ -347,11 +392,7 @@ linker_make_file(const char* pathname, linker_class_t lc)
linker_file_t lf = 0;
const char *filename;
- filename = rindex(pathname, '/');
- if (filename && filename[1])
- filename++;
- else
- filename = pathname;
+ filename = linker_basename(pathname);
KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
lockmgr(&lock, LK_EXCLUSIVE, 0, curproc);
@@ -380,6 +421,7 @@ int
linker_file_unload(linker_file_t file)
{
module_t mod, next;
+ modlist_t ml, nextml;
struct common_symbol* cp;
int error = 0;
int i;
@@ -414,6 +456,13 @@ linker_file_unload(linker_file_t file)
goto out;
}
+ for (ml = TAILQ_FIRST(&found_modules); ml; ml = nextml) {
+ nextml = TAILQ_NEXT(ml, link);
+ if (ml->container == file) {
+ TAILQ_REMOVE(&found_modules, ml, link);
+ }
+ }
+
/* Don't try to run SYSUNINITs if we are unloaded due to a link error */
if (file->flags & LINKER_FILE_LINKED) {
linker_file_sysuninit(file);
@@ -423,9 +472,12 @@ linker_file_unload(linker_file_t file)
TAILQ_REMOVE(&linker_files, file, link);
lockmgr(&lock, LK_RELEASE, 0, curproc);
- for (i = 0; i < file->ndeps; i++)
- linker_file_unload(file->deps[i]);
- free(file->deps, M_LINKER);
+ if (file->deps) {
+ for (i = 0; i < file->ndeps; i++)
+ linker_file_unload(file->deps[i]);
+ free(file->deps, M_LINKER);
+ file->deps = NULL;
+ }
for (cp = STAILQ_FIRST(&file->common); cp;
cp = STAILQ_FIRST(&file->common)) {
@@ -434,7 +486,10 @@ linker_file_unload(linker_file_t file)
}
LINKER_UNLOAD(file);
- free(file->filename, M_LINKER);
+ if (file->filename) {
+ free(file->filename, M_LINKER);
+ file->filename = NULL;
+ }
kobj_delete((kobj_t) file, M_LINKER);
out:
@@ -468,7 +523,6 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
{
c_linker_sym_t sym;
linker_symval_t symval;
- linker_file_t lf;
caddr_t address;
size_t common_size = 0;
int i;
@@ -498,24 +552,6 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
return address;
}
}
-
- /* If we have not found it in the dependencies, search globally */
- for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
- /* But skip the current file if it's on the list */
- if (lf == file)
- continue;
- /* And skip the files we searched above */
- for (i = 0; i < file->ndeps; i++)
- if (lf == file->deps[i])
- break;
- if (i < file->ndeps)
- continue;
- address = linker_file_lookup_symbol(lf, name, 0);
- if (address) {
- KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address));
- return address;
- }
- }
}
if (common_size > 0) {
@@ -636,7 +672,8 @@ linker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
int
kldload(struct proc* p, struct kldload_args* uap)
{
- char* filename = NULL, *modulename;
+ char* pathname, *realpath;
+ const char *filename;
linker_file_t lf;
int error = 0;
@@ -648,30 +685,34 @@ kldload(struct proc* p, struct kldload_args* uap)
if ((error = suser(p)) != 0)
return error;
- filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0)
+ realpath = NULL;
+ pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+ if ((error = copyinstr(SCARG(uap, file), pathname, 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)) {
+ realpath = linker_search_path(pathname);
+ if (realpath == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ /* Can't load more than one file with the same name */
+ filename = linker_basename(realpath);
+ if (linker_find_file_by_name(filename)) {
error = EEXIST;
goto out;
}
- if ((error = linker_load_file(filename, &lf)) != 0)
+ if ((error = linker_load_file(realpath, &lf)) != 0)
goto out;
lf->userrefs++;
p->p_retval[0] = lf->id;
out:
- if (filename)
- free(filename, M_TEMP);
+ if (pathname)
+ free(pathname, M_TEMP);
+ if (realpath)
+ free(realpath, M_LINKER);
return error;
}
@@ -691,7 +732,7 @@ kldunload(struct proc* p, struct kldunload_args* uap)
if (lf) {
KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
if (lf->userrefs == 0) {
- printf("linkerunload: attempt to unload file that was loaded by the kernel\n");
+ printf("kldunload: attempt to unload file that was loaded by the kernel\n");
error = EBUSY;
goto out;
}
@@ -709,29 +750,28 @@ out:
int
kldfind(struct proc* p, struct kldfind_args* uap)
{
- char* filename = NULL, *modulename;
+ char* pathname;
+ const char *filename;
linker_file_t lf;
int error = 0;
p->p_retval[0] = -1;
- filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0)
+ pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+ if ((error = copyinstr(SCARG(uap, file), pathname, MAXPATHLEN, NULL)) != 0)
goto out;
- modulename = rindex(filename, '/');
- if (modulename == NULL)
- modulename = filename;
+ filename = linker_basename(pathname);
- lf = linker_find_file_by_name(modulename);
+ lf = linker_find_file_by_name(filename);
if (lf)
p->p_retval[0] = lf->id;
else
error = ENOENT;
out:
- if (filename)
- free(filename, M_TEMP);
+ if (pathname)
+ free(pathname, M_TEMP);
return error;
}
@@ -883,6 +923,27 @@ out:
* Preloaded module support
*/
+static modlist_t
+modlist_lookup(const char *name)
+{
+ modlist_t mod;
+
+ for (mod = TAILQ_FIRST(&found_modules); mod; mod = TAILQ_NEXT(mod, link)) {
+ if (!strcmp(mod->name, name))
+ return mod;
+ }
+ return NULL;
+}
+
+/*
+ * This routine is cheap and nasty but will work for data pointers.
+ */
+static void *
+linker_reloc_ptr(linker_file_t lf, void *offset)
+{
+ return lf->address + (uintptr_t)offset;
+}
+
static void
linker_preload(void* arg)
{
@@ -891,10 +952,20 @@ linker_preload(void* arg)
char *modtype;
linker_file_t lf;
linker_class_t lc;
- int error;
+ int error, mcount;
struct linker_set *sysinits;
- struct sysinit **sipp;
- const moduledata_t *moddata;
+ linker_file_list_t loaded_files;
+ linker_file_list_t depended_files;
+ struct linker_set *deps;
+ struct mod_metadata *mp;
+ int i;
+ int resolves;
+ modlist_t mod;
+
+ TAILQ_INIT(&loaded_files);
+ TAILQ_INIT(&depended_files);
+ TAILQ_INIT(&found_modules);
+ error = 0;
modptr = NULL;
while ((modptr = preload_search_next_name(modptr)) != NULL) {
@@ -909,44 +980,158 @@ linker_preload(void* arg)
continue;
}
printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr);
- lf = linker_find_file_by_name(modname);
- if (lf) {
- lf->userrefs++;
- continue;
- }
lf = NULL;
for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
- error = LINKER_LOAD_FILE(lc, modname, &lf);
+ error = LINKER_LINK_PRELOAD(lc, modname, &lf);
if (error) {
lf = NULL;
break;
}
}
- if (lf) {
- lf->userrefs++;
+ if (lf)
+ TAILQ_INSERT_TAIL(&loaded_files, lf, loaded);
+ }
+
+ /*
+ * First get a list of stuff in the kernel.
+ */
+ deps = (struct linker_set*)
+ linker_file_lookup_symbol(linker_kernel_file, MDT_SETNAME, 0);
+ if (deps) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = deps->ls_items[i];
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = mp->md_cval;
+ if (modlist_lookup(modname) != NULL) {
+ printf("module %s already present!\n", modname);
+ /* XXX what can we do? this is a build error. :-( */
+ continue;
+ }
+ mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT);
+ if (mod == NULL)
+ panic("no memory for module list");
+ bzero(mod, sizeof(*mod));
+ mod->container = linker_kernel_file;
+ mod->name = modname;
+ TAILQ_INSERT_TAIL(&found_modules, mod, link);
+ }
+ }
- sysinits = (struct linker_set*)
- linker_file_lookup_symbol(lf, "sysinit_set", 0);
- if (sysinits) {
- /* 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.
- */
- for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
- if ((*sipp)->func == module_register_init) {
- moddata = (*sipp)->udata;
- error = module_register(moddata, lf);
- if (error)
- printf("Preloaded %s \"%s\" failed to register: %d\n",
- modtype, modname, error);
+ /*
+ * this is a once-off kinky bubble sort
+ * resolve relocation dependency requirements
+ */
+restart:
+ for (lf = TAILQ_FIRST(&loaded_files); lf; lf = TAILQ_NEXT(lf, loaded)) {
+ deps = (struct linker_set*)
+ linker_file_lookup_symbol(lf, MDT_SETNAME, 0);
+ /*
+ * First, look to see if we would successfully link with this stuff.
+ */
+ resolves = 1; /* unless we know otherwise */
+ if (deps) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = linker_reloc_ptr(lf, deps->ls_items[i]);
+ if (mp->md_type != MDT_DEPEND)
+ continue;
+ modname = linker_reloc_ptr(lf, mp->md_cval);
+ if (modlist_lookup(modname) == NULL) {
+ /* ok, the module isn't here yet, we are not finished */
+ 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 (deps) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = linker_reloc_ptr(lf, deps->ls_items[i]);
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = linker_reloc_ptr(lf, mp->md_cval);
+ if (modlist_lookup(modname) != NULL) {
+ printf("module %s already present!\n", modname);
+ linker_file_unload(lf);
+ TAILQ_REMOVE(&loaded_files, lf, loaded);
+ goto restart; /* we changed the tailq next ptr */
}
+ mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT);
+ if (mod == NULL)
+ panic("no memory for module list");
+ bzero(mod, sizeof(*mod));
+ mod->container = lf;
+ mod->name = modname;
+ TAILQ_INSERT_TAIL(&found_modules, mod, link);
}
- sysinit_add((struct sysinit **)sysinits->ls_items);
}
- 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..
+ */
+ for (lf = TAILQ_FIRST(&loaded_files); lf; lf = TAILQ_NEXT(lf, loaded)) {
+ printf("KLD file %s is missing dependencies\n", lf->filename);
+ linker_file_unload(lf);
+ TAILQ_REMOVE(&loaded_files, lf, loaded);
+ }
+
+ /*
+ * We made it. Finish off the linking in the order we determined.
+ */
+ for (lf = TAILQ_FIRST(&depended_files); lf; lf = TAILQ_NEXT(lf, loaded)) {
+ if (linker_kernel_file) {
+ linker_kernel_file->refs++;
+ error = linker_file_add_dependancy(lf, linker_kernel_file);
+ if (error)
+ panic("cannot add dependency");
+ }
+ lf->userrefs++; /* so we can (try to) kldunload it */
+ deps = (struct linker_set*)
+ linker_file_lookup_symbol(lf, MDT_SETNAME, 0);
+ if (deps) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = linker_reloc_ptr(lf, deps->ls_items[i]);
+ if (mp->md_type != MDT_DEPEND)
+ continue;
+ modname = linker_reloc_ptr(lf, mp->md_cval);
+ mod = modlist_lookup(modname);
+ mod->container->refs++;
+ error = linker_file_add_dependancy(lf, mod->container);
+ if (error)
+ panic("cannot add dependency");
+ }
+ }
+
+ /* Now do relocation etc using the symbol search paths established by the dependencies */
+ error = LINKER_LINK_PRELOAD_FINISH(lf);
+ if (error) {
+ printf("KLD file %s - could not finalize loading\n", lf->filename);
+ linker_file_unload(lf);
+ continue;
+ }
+
+ mcount = linker_file_register_modules(lf);
+ sysinits = (struct linker_set*)
+ linker_file_lookup_symbol(lf, "sysinit_set", 0);
+ if (sysinits)
+ sysinit_add((struct sysinit **)sysinits->ls_items);
+ linker_file_register_sysctls(lf);
+ lf->flags |= LINKER_FILE_LINKED;
+ }
+ /* woohoo! we made it! */
}
SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
@@ -971,46 +1156,63 @@ static char linker_path[MAXPATHLEN] = "/;/boot/;/modules/";
SYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path,
sizeof(linker_path), "module load search path");
-char *
+static char *linker_ext_list[] = {
+ ".ko",
+ "",
+ NULL
+};
+
+static char *
linker_search_path(const char *name)
{
struct nameidata nd;
struct proc *p = curproc; /* XXX */
- char *cp, *ep, *result;
- int error;
+ char *cp, *ep, *result, **cpp;
+ int error, extlen, len;
enum vtype type;
/* qualified at all? */
if (index(name, '/'))
return(linker_strdup(name));
+ extlen = 0;
+ for (cpp = linker_ext_list; *cpp; cpp++) {
+ len = strlen(*cpp);
+ if (len > extlen)
+ extlen = len;
+ }
+ extlen++; /* trailing '\0' */
+
/* traverse the linker path */
cp = linker_path;
+ len = strlen(name);
for (;;) {
/* find the end of this component */
for (ep = cp; (*ep != 0) && (*ep != ';'); ep++)
;
- result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK);
+ result = malloc((len + (ep - cp) + extlen), M_LINKER, M_WAITOK);
if (result == NULL) /* actually ENOMEM */
return(NULL);
+ for (cpp = linker_ext_list; *cpp; cpp++) {
+ strncpy(result, cp, ep - cp);
+ strcpy(result + (ep - cp), name);
+ strcat(result, *cpp);
- strncpy(result, cp, ep - cp);
- strcpy(result + (ep - cp), name);
-
- /*
- * Attempt to open the file, and return the path if we succeed and it's
- * a regular file.
- */
- NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p);
- error = vn_open(&nd, FREAD, 0);
- if (error == 0) {
- NDFREE(&nd, NDF_ONLY_PNBUF);
- type = nd.ni_vp->v_type;
- VOP_UNLOCK(nd.ni_vp, 0, p);
- vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
- if (type == VREG)
- return(result);
+ /*
+ * Attempt to open the file, and return the path if we succeed
+ * and it's a regular file.
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p);
+ error = vn_open(&nd, FREAD, 0);
+ if (error == 0) {
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ type = nd.ni_vp->v_type;
+ VOP_UNLOCK(nd.ni_vp, 0, p);
+ vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
+ if (type == VREG)
+ return(result);
+ }
}
free(result, M_LINKER);
@@ -1020,3 +1222,151 @@ linker_search_path(const char *name)
}
return(NULL);
}
+
+static const char *
+linker_basename(const char* path)
+{
+ const char *filename;
+
+ filename = rindex(path, '/');
+ if (filename == NULL)
+ return path;
+ if (filename[1])
+ filename++;
+ return filename;
+}
+
+/*
+ * 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 *modname, struct linker_file *parent)
+{
+ linker_file_t lfdep;
+ const char *filename;
+ char *pathname;
+ int error;
+
+ /*
+ * There will be a system to look up or guess a file name from
+ * a module name.
+ * For now we just try to load a file with the same name.
+ */
+ pathname = linker_search_path(modname);
+ if (pathname == NULL)
+ return ENOENT;
+
+ /* Can't load more than one file with the same basename */
+ filename = linker_basename(pathname);
+ if (linker_find_file_by_name(filename)) {
+ error = EEXIST;
+ goto out;
+ }
+
+ do {
+ error = linker_load_file(pathname, &lfdep);
+ if (error)
+ break;
+ if (parent) {
+ error = linker_file_add_dependancy(parent, lfdep);
+ if (error)
+ break;
+ }
+ } while(0);
+out:
+ if (pathname)
+ free(pathname, M_LINKER);
+ return error;
+}
+
+/*
+ * This routine is responsible for finding dependencies of userland
+ * initiated kldload(2)'s of files.
+ */
+int
+linker_load_dependancies(linker_file_t lf)
+{
+ linker_file_t lfdep;
+ struct linker_set *deps;
+ struct mod_metadata *mp, *nmp;
+ modlist_t mod;
+ char *modname, *nmodname;
+ int i, j, error = 0;
+
+ /*
+ * All files are dependant on /kernel.
+ */
+ if (linker_kernel_file) {
+ linker_kernel_file->refs++;
+ error = linker_file_add_dependancy(lf, linker_kernel_file);
+ if (error)
+ return error;
+ }
+
+ deps = (struct linker_set*)
+ linker_file_lookup_symbol(lf, MDT_SETNAME, 0);
+ if (deps != NULL) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = linker_reloc_ptr(lf, deps->ls_items[i]);
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = linker_reloc_ptr(lf, mp->md_cval);
+ if (modlist_lookup(modname) != NULL) {
+ printf("module %s already present!\n", modname);
+ return EEXIST;
+ }
+ }
+ }
+ if (deps != NULL) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = linker_reloc_ptr(lf, deps->ls_items[i]);
+ if (mp->md_type != MDT_DEPEND)
+ continue;
+ modname = linker_reloc_ptr(lf, mp->md_cval);
+ nmodname = NULL;
+ for (j = 0; j < deps->ls_length; j++) {
+ nmp = linker_reloc_ptr(lf, deps->ls_items[j]);
+ if (nmp->md_type != MDT_VERSION)
+ continue;
+ nmodname = linker_reloc_ptr(lf, nmp->md_cval);
+ if (strcmp(modname, nmodname) == 0)
+ break;
+ }
+ if (j < deps->ls_length) /* early exit, it's a self reference */
+ continue;
+ mod = modlist_lookup(modname);
+ if (mod) { /* woohoo, it's loaded already */
+ lfdep = mod->container;
+ lfdep->refs++;
+ error = linker_file_add_dependancy(lf, lfdep);
+ if (error)
+ break;
+ continue;
+ }
+ error = linker_load_module(modname, lf);
+ if (error) {
+ printf("KLD %s: depends on %s - not available\n",
+ lf->filename, modname);
+ break;
+ }
+ }
+
+ }
+ if (error == 0 && deps) {
+ for (i = 0; i < deps->ls_length; i++) {
+ mp = linker_reloc_ptr(lf, deps->ls_items[i]);
+ if (mp->md_type != MDT_VERSION)
+ continue;
+ modname = linker_reloc_ptr(lf, mp->md_cval);
+ mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT);
+ if (mod == NULL)
+ panic("no memory for module list");
+ bzero(mod, sizeof(*mod));
+ mod->container = lf;
+ mod->name = modname;
+ TAILQ_INSERT_TAIL(&found_modules, mod, link);
+ }
+ }
+ return error;
+}
OpenPOWER on IntegriCloud