summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_linker.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1998-10-10 00:07:53 +0000
committerpeter <peter@FreeBSD.org>1998-10-10 00:07:53 +0000
commit3e75936c6c42230ab8fefae122340215c88acb84 (patch)
tree17765a20eca4cd71020020aa01da6b645ec53973 /sys/kern/kern_linker.c
parent36ac00d99f3526af895fe7a472a104b1e59c7c61 (diff)
downloadFreeBSD-src-3e75936c6c42230ab8fefae122340215c88acb84.zip
FreeBSD-src-3e75936c6c42230ab8fefae122340215c88acb84.tar.gz
Use Mike Smith's linker module search path code.
Implement preloading in a fairly MI way, assuming the information is prepared. DDB interface helpers.. Provide some support for db_kld.c so that we don't have to export too much detail. Debugging and cosmetic nits left in from development.. The other half of the containing file hack so modules can associate themselves with their "file".
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