summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_linker.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_linker.c')
-rw-r--r--sys/kern/kern_linker.c277
1 files changed, 261 insertions, 16 deletions
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index 4f25dcd..3f15f0e 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -23,9 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: kern_linker.c,v 1.6 1998/01/01 08:56:24 bde Exp $
+ * $Id: kern_linker.c,v 1.7 1998/08/12 08:44:21 dfr Exp $
*/
+#include "opt_ddb.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -35,9 +37,15 @@
#include <sys/proc.h>
#include <sys/lock.h>
#include <machine/cpu.h>
+#include <machine/bootinfo.h>
#include <sys/module.h>
#include <sys/linker.h>
#include <sys/unistd.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/sysctl.h>
MALLOC_DEFINE(M_LINKER, "kld", "kernel linker");
linker_file_t linker_current_file;
@@ -55,7 +63,7 @@ linker_init(void* arg)
TAILQ_INIT(&files);
}
-SYSINIT(linker, SI_SUB_KMEM, SI_ORDER_SECOND, linker_init, 0);
+SYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0);
int
linker_add_class(const char* desc, void* priv,
@@ -82,17 +90,26 @@ linker_file_sysinit(linker_file_t lf)
struct sysinit** sipp;
struct sysinit** xipp;
struct sysinit* save;
-
- linker_current_file = lf;
+ moduledata_t *moddata;
KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
lf->filename));
sysinits = (struct linker_set*)
linker_file_lookup_symbol(lf, "sysinit_set", 0);
+
+ 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;
+ moddata->_file = lf;
+ }
+ }
+
/*
* Perform a bubble sort of the system initialization objects by
* their subsystem (primary key) and order (secondary key).
@@ -182,7 +199,6 @@ linker_load_file(const char* filename, linker_file_t* result)
goto out;
}
}
-
error = ENOEXEC; /* format not recognised */
out:
@@ -218,10 +234,17 @@ linker_find_file_by_id(int fileid)
}
linker_file_t
-linker_make_file(const char* filename, void* priv, struct linker_file_ops* ops)
+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, '/');
+ if (filename && filename[1])
+ filename++;
+ else
+ filename = pathname;
KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
@@ -257,7 +280,7 @@ linker_file_unload(linker_file_t file)
int error = 0;
int i;
- KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", lf->refs));
+ KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
if (file->refs == 1) {
KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
@@ -289,7 +312,7 @@ linker_file_unload(linker_file_t file)
TAILQ_REMOVE(&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);
@@ -337,7 +360,7 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
size_t common_size = 0;
int i;
- KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d",
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n",
file, name, deps));
if (file->ops->lookup_symbol(file, name, &sym) == 0) {
@@ -348,15 +371,19 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
* only allocate space if not found there.
*/
common_size = symval.size;
- else
+ else {
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value));
return symval.value;
+ }
}
if (deps)
for (i = 0; i < file->ndeps; i++) {
address = linker_file_lookup_symbol(file->deps[i], name, 0);
- if (address)
+ if (address) {
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address));
return address;
+ }
}
if (common_size > 0) {
@@ -369,8 +396,10 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
for (cp = STAILQ_FIRST(&file->common); cp;
cp = STAILQ_NEXT(cp, link))
- if (!strcmp(cp->name, name))
+ if (!strcmp(cp->name, name)) {
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address));
return cp->address;
+ }
/*
* Round the symbol size up to align.
@@ -380,8 +409,10 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
+ common_size
+ strlen(name) + 1,
M_LINKER, M_WAITOK);
- if (!cp)
+ if (!cp) {
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n"));
return 0;
+ }
cp->address = (caddr_t) (cp + 1);
cp->name = cp->address + common_size;
@@ -389,12 +420,82 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
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));
return cp->address;
}
+ KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n"));
return 0;
}
+#ifdef DDB
+/*
+ * DDB Helpers. DDB has to look across multiple files with their own
+ * symbol tables and string tables.
+ *
+ * Note that we do not obey list locking protocols here. We really don't
+ * need DDB to hang because somebody's got the lock held. We'll take the
+ * chance that the files list is inconsistant instead.
+ */
+
+int
+linker_ddb_lookup(char *symstr, linker_sym_t *sym)
+{
+ linker_file_t lf;
+
+ for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
+ if (lf->ops->lookup_symbol(lf, symstr, sym) == 0)
+ return 0;
+ }
+ return ENOENT;
+}
+
+int
+linker_ddb_search_symbol(caddr_t value, linker_sym_t *sym, long *diffp)
+{
+ linker_file_t lf;
+ u_long off = (u_long)value;
+ u_long diff, bestdiff;
+ linker_sym_t best;
+ linker_sym_t es;
+
+ best = 0;
+ bestdiff = off;
+ for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
+ if (lf->ops->search_symbol(lf, value, &es, &diff) != 0)
+ continue;
+ if (es != 0 && diff < bestdiff) {
+ best = es;
+ bestdiff = diff;
+ }
+ if (bestdiff == 0)
+ break;
+ }
+ if (best) {
+ *sym = best;
+ *diffp = bestdiff;
+ return 0;
+ } else {
+ *sym = 0;
+ *diffp = off;
+ return ENOENT;
+ }
+}
+
+int
+linker_ddb_symbol_values(linker_sym_t sym, linker_symval_t *symval)
+{
+ linker_file_t lf;
+
+ for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
+ if (lf->ops->symbol_values(lf, sym, symval) == 0)
+ return 0;
+ }
+ return ENOENT;
+}
+
+#endif
+
/*
* Syscalls.
*/
@@ -423,7 +524,7 @@ kldload(struct proc* p, struct kldload_args* uap)
lf->userrefs++;
p->p_retval[0] = lf->id;
-
+
out:
if (filename)
free(filename, M_TEMP);
@@ -477,7 +578,7 @@ kldfind(struct proc* p, struct kldfind_args* uap)
p->p_retval[0] = lf->id;
else
error = ENOENT;
-
+
out:
if (filename)
free(filename, M_TEMP);
@@ -497,7 +598,7 @@ kldnext(struct proc* p, struct kldnext_args* uap)
p->p_retval[0] = 0;
return 0;
}
-
+
lf = linker_find_file_by_id(SCARG(uap, fileid));
if (lf) {
if (TAILQ_NEXT(lf, link))
@@ -574,3 +675,147 @@ kldfirstmod(struct proc* p, struct kldfirstmod_args* uap)
return error;
}
+
+/*
+ * Preloaded module support
+ */
+
+static void
+linker_preload(void* arg)
+{
+ caddr_t modptr;
+ char *modname;
+ linker_file_t lf;
+ linker_class_t lc;
+ int error;
+ struct linker_set *sysinits;
+ struct sysinit **sipp;
+ moduledata_t *moddata;
+
+
+ modptr = NULL;
+ while ((modptr = preload_search_next_name(modptr)) != NULL) {
+ modname = (char *)preload_search_info(modptr, MODINFO_NAME);
+ if (modname == NULL) {
+ printf("Preloaded module at 0x%p does not have a name!\n", modptr);
+ continue;
+ }
+ printf("Preloaded module %s at 0x%p.\n", 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 = lc->ops->load_file(modname, &lf);
+ if (error) {
+ lf = NULL;
+ break;
+ }
+ }
+ if (lf) {
+ lf->userrefs++;
+
+ 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;
+ moddata->_file = lf;
+ }
+ }
+ sysinit_add((struct sysinit **)sysinits->ls_items);
+ }
+
+ break;
+ }
+ }
+}
+
+SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
+
+/*
+ * Search for a not-loaded module by name.
+ *
+ * Modules may be found in the following locations:
+ *
+ * - preloaded (result is just the module name)
+ * - on disk (result is full path to module)
+ *
+ * If the module name is qualified in any way (contains path, etc.)
+ * the we simply return a copy of it.
+ *
+ * The search path can be manipulated via sysctl. Note that we use the ';'
+ * character as a separator to be consistent with the bootloader.
+ */
+
+static char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/";
+
+SYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path,
+ sizeof(linker_path), "module load search path");
+
+static char *
+linker_strdup(const char *str)
+{
+ char *result;
+
+ if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL)
+ strcpy(result, str);
+ return(result);
+}
+
+char *
+linker_search_path(const char *name)
+{
+ struct nameidata nd;
+ struct proc *p = curproc; /* XXX */
+ char *cp, *ep, *result;
+ int error;
+ enum vtype type;
+
+ /* qualified at all? */
+ if (index(name, '/'))
+ return(linker_strdup(name));
+
+ /* traverse the linker path */
+ cp = linker_path;
+ 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);
+ if (result == NULL) /* actually ENOMEM */
+ return(NULL);
+
+ 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) {
+ 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);
+
+ if (*ep == 0)
+ break;
+ cp = ep + 1;
+ }
+ return(NULL);
+}
OpenPOWER on IntegriCloud