/*
 * Install a module in the kernel.
 *
 * See the file COPYING for your rights (GNU GPL)
 *
 * Originally by Anonymous (as far as I know...)
 * Linux version by Bas Laarhoven <bas@vimec.nl>
 * Modified by Jon Tombs.
 *
 * Support for transient and resident symbols
 * added by Bjorn Ekwall <bj0rn@blox.se> in June 1994 (C)
 *
 * Load map option conceived by Derek Atkins <warlord@MIT.EDU>
 *
 * Support for versioned kernels and symbols: Bjorn Ekwall in December 1994
 *
 * Merged in ksyms and rmmod in December 1994: Bjorn Ekwall
 *
 * Support for ELF modules: Bjorn Ekwall in December 1994 after having
 *                          mangled sources from, and been enlightened
 *                          and supported by Eric Youngdale <eric@aib.com>
 *                          (the kludges are all mine, don't blame Eric...)
 *
 * Support for array initializers: Bjorn Ekwall in January 1995
 * Support for string initializers: Bjorn Ekwall in January 1995
 * Fixed major bug in a.out bss variable handling: March '95, Bas.
 * ELF fixes from H.J.Lu <hjl@nynexst.com>
 * Many ELF and other fixes from:
 * 	James Bottomley <J.E.J.Bottomley@damtp.cambridge.ac.uk>
 * Full support for MODPATH setting: Henrik Storner <storner@osiris.ping.dk>
 * Removed limitation of unversioned module vs. versioned kernel: Bjorn
 * Handle all combinations of ELF vs a.out kernels/modules: Bjorn
 * Added syslog error reporting with option "-s": Jacques Gelinas
 * Added MOD_AUTOCLEAN and option "-k" (for kerneld"): Bjorn and Jacques
 * mc68000: Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
 * More fixes (ELF SHN_UNDEF et al) by  Andreas Schwab in February 1996
 * Added Sparc support: Eddie C. Dost <ecd@skynet.be>
 * Various improvements added by Hans Lermen <lermen@elserv.ffm.fgan.de>
 */

#ifndef MOD_AUTOCLEAN /* defined in <linux/module.h> by the "kerneld"-patch */
#define MOD_AUTOCLEAN 0x40000000 /* big enough, but no sign problems... */
#endif

static char *default_path[] = {
	".", "/linux/modules",
	"/lib/modules/%s/fs",
	"/lib/modules/%s/net",
	"/lib/modules/%s/scsi",
	"/lib/modules/%s/block",
	"/lib/modules/%s/cdrom",
	"/lib/modules/%s/ipv4",
	"/lib/modules/%s/misc",
	"/lib/modules/default/fs",
	"/lib/modules/default/net",
	"/lib/modules/default/scsi",
	"/lib/modules/default/block",
	"/lib/modules/default/cdrom",
	"/lib/modules/default/ipv4",
	"/lib/modules/default/misc",
	"/lib/modules/fs",
	"/lib/modules/net",
	"/lib/modules/scsi",
	"/lib/modules/block",
	"/lib/modules/cdrom",
	"/lib/modules/ipv4",
	"/lib/modules/misc",
	0
};

#include "insmod.h"
#include "../Version.h"
#define is_global(sp) (aout_flag?(sp->u.n.n_type & N_EXT) : \
				(ELF32_ST_BIND(sp->u.e.st_info) != STB_LOCAL))
#define is_undef(sp) (aout_flag?((sp->u.n.n_type & ~N_EXT) == N_UNDF) : \
			    (sp->u.e.st_shndx == SHN_UNDEF))

/* hack: sizeof(struct elfhdr) > sizeof(struct exec) */
Elf32_Ehdr header;
int aout_flag; /* to know the format of the module */
int elf_kernel = 0; /* to know the format of the kernel */

size_t codesize;
size_t progsize;
size_t bss1size;
size_t bss2size;

char *textseg;

int nsymbols;
struct symbol *symtab;
char *stringtab;
unsigned long addr;
int verbose = 0;
int silent_poll = 0;

static int export_flag = 1; /* See comment at option handler in main() */
static int force_load = 0;
static int loadmap = 0;
static int versioned_kernel = 0;
static struct kernel_sym nullsym;
static struct symbol *symroot;
static struct utsname uts_info;
static int kernelv;
static char kd_dbmfile[40];

/*
	Probe the running kernel for its version and release
*/
int kernel_getversion ()
{
	int ret = uname (&uts_info);
	int v[3];
	v[0] = v[1] = v[2] = 0;
	kernelv = 0;
	if (ret != -1){
		char *pt;
		v[0] = atoi(uts_info.release);
		pt = strchr(uts_info.release,'.');
		if (pt != NULL){
			pt++;
			v[1] = atoi(pt);
			pt = strchr(pt,'.');
			if (pt != NULL){
				v[2] = atoi(pt+1);
				kernelv = v[0]*1000000+v[1]*1000+v[2];
			}
		}
	}
	return ret;
}


void * ckalloc(size_t nbytes)
{
	void *p;

	if ((p = malloc(nbytes)) == NULL) {
	        perror("insmod: malloc failed");
		exit(2);
	}
	return p;
}

void * ckrealloc(void *ptr, size_t nbytes)
{
        void *p;

	if ((p = realloc(ptr, nbytes)) == NULL) {
	        perror("insmod: realloc failed");
		exit(2);
	}
	return p;
}

#if 0
static int create_module(const char *name, unsigned long size)
{
	return syscall( __NR_create_module, name, size);
}

#else
/* do syscall create_module directly to avoid bug in libc
   Hans Lermen <lermen@elserv.ffm.fgan.de> */
static _syscall2(int, create_module, const char *, name, unsigned long, size)
static int our_create_module(const char *name, unsigned long size)
{
	/* Why all this fuss ?
		In linux 2.1, the address returned by create module
		point in kernel space which is now mapped at the top
		of user space. Above 2Gig. This looks like a negative
		number for a long. The normal syscall macro of linux 2.0
		(and all libc compile with linux 2.0 or below) consider
		that the return value is a negative number and consider
		it is an error number (A kernel convention, return value
		are positive or negative, indicating the error number).
		
		By checking the value of errno, we know if we have been
		fooled by the syscall2 macro and we fix it.
	*/
	int ret =  create_module(name,size);
	if (errno > 125 && ret == -1) {
		ret = -errno;
		errno = 0;
	}
	return ret;
}
#define create_module our_create_module  /* this fakes our kludge */
#endif

/*
	Normally, these structure are defined in module.h
	To provide a utility which work and compiled on any
	system (2.0.x or 2.1.x and higher), we have to forget
	about system header and duplicate the structure here.

	Only the names were changed to protect the innocent.
*/
struct _einfo{
	char *start;
	char *end;
};

#ifndef MODULE_2_1_7_SIG
	#define MODULE_2_1_7_SIG	((void*)0x00000217)
#endif


struct mod_routines_2_1_7 {
	void *signature;
	int (*init)(void);		/* initialization routine */
	void (*cleanup)(void);		/* cleanup routine */
	struct _einfo exceptinfo;
};

struct mod_routines_2_0_0 {
	int (*init)(void);		/* initialization routine */
	void (*cleanup)(void);		/* cleanup routine */
};

union mod_routines_union{
	struct mod_routines_2_1_7 mod21;
	struct mod_routines_2_0_0 mod20;
};

static int init_module(const char *name, void *code, unsigned codesize,
		union mod_routines_union *routines,
		struct symbol_table *syms) {
	return syscall( __NR_init_module, name, code, codesize, routines,
		syms);
}

static int delete_module(const char *name)
{
	return syscall( __NR_delete_module, name);
}

static int get_kernel_syms(struct kernel_sym *buffer)
{
	return syscall( __NR_get_kernel_syms, buffer);
}

static void check_version(int force_load)
{
	unsigned long kernel_version;

	/* Check if module and kernel version match */
	kernel_version = looksym("kernel_version");
	if (strcmp( (char*) textseg + kernel_version, uts_info.release)) {
		insmod_error (
			"Error: The module was compiled on kernel version %s.\n"
			"       This kernel is version %s. They don't match!\n"
			"       Check that the module is usable with the current kernel,\n"
			"       recompile the module and try again.",
			(char*) textseg + kernel_version, uts_info.release);
		if (force_load)
			insmod_error("       Trying to load it anyway...");
		else
			exit( 2);
	}

}

/*
 * unversioned kernel, versioned module
 */
static int m_strncmp(const char *tabentry, const char *lookfor, size_t n)
{
	int len = strlen(lookfor);
	int retval;

	if ((retval = strncmp(tabentry, lookfor, len)) != 0)
		return retval;
	/* else */
	if ((strncmp(tabentry + len, "_R", 2) == 0) &&
		(strlen(tabentry + len) == 10))
		return 0;
	else
		return strcmp(tabentry, lookfor);
}

/*
 * versioned kernel, unversioned module
 */
static int k_strncmp(const char *tabentry, const char *lookfor, size_t n)
{
	int len = strlen(tabentry);
	int retval;

	if ((retval = strncmp(tabentry, lookfor, len)) != 0)
		return retval;
	/* else */
	if ((strncmp(lookfor + len, "_R", 2) == 0) &&
		(strlen(lookfor + len) == 10))
		return 0;
	else
		return strcmp(tabentry, lookfor);
}

struct strpatch {
	int where;	/* offset from start of the string pointer */
	int what;	/* Offset of the string location */
	char *string;	/* offset in argv[i] */
	int len;	/* length of the string */
};
static struct strpatch *stringpatches;
static int n_stringpatches;

static void push_string(char *string, int patch_offset)
{
	int len;
	char *p;

	if ((p = strchr(string, ',')) != (char *)0)
		len = p - string;
	else
		len = strlen(string);

	/*
	 * If we want to use any string (even one with only numbers):
	 * enclose the string with quotes, i.e.  ' or "
	 * (most probably escaped, if from a shell)
	 *
	 * Inspired by Giorgio Caset <gca@wag.ch>
	 */
	if (((string[0] == '\'') || (string[0] == '"')) &&
	     (string[len - 1] == string[0])) {
		++string; /* strip quote */
		len -= 2; /* and quote at the end as well */
	}

	if (n_stringpatches == 0)
		stringpatches = (struct strpatch *)ckalloc(sizeof(struct strpatch));
	else
		stringpatches = (struct strpatch *)ckrealloc(stringpatches,
			(n_stringpatches + 1) * sizeof(struct strpatch));

	(*(stringpatches + n_stringpatches)).where = patch_offset;
	(*(stringpatches + n_stringpatches)).string = string;
	(*(stringpatches + n_stringpatches)).len = len;
	++n_stringpatches;
}

int main(int argc, char **argv)
{
	FILE *fp;
	struct exec *aouthdr = (struct exec *)&header;
	struct kernel_sym *curr_module = &nullsym;
	struct kernel_sym *ksymtab = NULL;
	struct kernel_sym *ksym = NULL;
	struct kernel_sym *resident_ksym_start = NULL;
	union mod_routines_union routines;
	struct symbol *sp;
	struct strpatch *patch;
	unsigned long init_func, cleanup_func;
	int (*tabcomp) (const char *, const char *, size_t) = strncmp;
	int fatal_error;
	int i;
	int nksyms;
	int versioned_module;
	int found_resident = -1;
	int autoclean = 0; /* for insertions from kerneld: option "-k" */
	int bss_offset;

	struct symbol_table *newtab;
	int module_refs = 0; /* number of references modules */
	int n_symboldefs = 0; /* number of defined symbols in the module */
	int string_table_size = 0; /* size of the new symbol table string table */
	struct internal_symbol *symp;
	struct module_ref *refp;
	char *stringp;

	char *filename;
	char spare_path[200]; /* just testing... */
	char *modname = NULL;
	char *otextseg; /* JEJB: store the initial textseg, so we get offset */
	char *p;

	/* find basename */
	
	if ((p = strrchr(argv[0], '/')) != (char *)0)
		++p;
	else
		p = argv[0];

	if (strcmp(p, "rmmod") == 0)
		return (rmmod(argc, argv))?1:0;

	if (strcmp(p, "ksyms") == 0)
		return ksyms(argc, argv);

	/* else  this is insmod! */

	while (argc > 1 && (argv[1][0] == '-')) {
		p = &(argv[1][1]);
		while (*p) {
			switch (*p) {
			case 'f': /* force loading */
				force_load = 1;
				break;

			case 'k': /* module loaded by kerneld, auto-cleanable */
				autoclean = MOD_AUTOCLEAN;
				break;

			case 'm': /* generate loadmap */
				loadmap = 1;
				break;

			case 'o':
				modname = argv[2];
				--argc;
				++argv;
				break;

			case 'p': /* silent poll mode */
				silent_poll = 1;
				break;

			case 's':
				insmod_setsyslog("insmod");
				break;

			case 'v': /* verbose output */
				verbose = 1;
				break;

			case 'V':
				printf("Version " MODULES_VERSION "\n");
				break;

			case 'x': /* do _not_ export externs */
				export_flag = 0;
				break;

			case 'X': /* _do_ export externs */
				export_flag = 1;
				break;
			}
			++p;
		}
		--argc;
		++argv;
	}

	if (argc < 2) {
		fputs("Usage:\n"
		      "insmod [-fkmopsvVxX] [-o name] module [[sym=value]...]\n"
		      "\n"
		      "  module     Filename of a loadable kernel module (*.o)\n"
		      "  -f         Force loading under wrong kernel version\n"
		      "  -k         Make module (autoclean)-able\n"
		      "  -m         Generate loadmap (so crashes can be traced)\n"
		      "  -o name    Set internal modulname to name\n"
		      "  -p         Poll mode, just check if the module matches the kernel\n"
		      "  -s         Report errors via syslog\n"
		      "  -v         Verbose output\n"
		      "  -V         Show version\n"
		      "  -x         do *not* export externs\n"
		      "  -X         *do* export externs\n"
		      , stderr);
		exit(2);
	}

	kernel_getversion();
	sprintf(kd_dbmfile, "/lib/modules/%s/persist.gdbm", uts_info.release);

	filename = argv[1];
	argv += 2;
	--argc;

	/* get the size of the current kernel symbol table */
	nksyms = get_kernel_syms(NULL);

	if (nksyms < 0) {
		insmod_error("get_kernel_sys failed: Cannot find Kernel symbols!");
		exit(2);
	}

	if (nksyms) {
		ksymtab = (struct kernel_sym *) ckalloc(nksyms * sizeof *ksymtab);
		/* NOTE!!! The order of the symbols is important */
		if (get_kernel_syms(ksymtab) != nksyms) {
			insmod_error ("Kernel symbol problem");
			exit(2);
		}
	}

	/* Is this a kernel with appended CRC-versions? */
	for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) {
		if (found_resident == -1 && strcmp(ksym->name, "#") == 0) {
			resident_ksym_start = ksym;
			found_resident = i;
			break;
		}
	}

	/* look only in residents */
	if (found_resident != -1) {
		++ksym;
		/* The first symbol in a versioned kernel is _Using_Versions */
		if ((strcmp(ksym->name, "_Using_Versions") == 0) ||
		    (strcmp(ksym->name, "Using_Versions") == 0))
	    		versioned_kernel = 1;

		/* look at a "normal" symbol: */
		++ksym;
		if (((i + 1) < nksyms) && (ksym->name[0] != '_'))
			elf_kernel = 1;
	}

	/* construct the module name */
	if (modname == NULL) {
		int len;

		if ((p = strrchr(filename, '/')) != NULL)
			p++;
		else
			p = filename;
		len = strlen(p);
		if (len > 2 && strcmp(p + len - 2, ".o") == 0)
			len -= 2;
		else if (len > 4 && strcmp(p + len - 4, ".mod") == 0)
			len -= 4;

		modname = (char*) ckalloc(len + 1);
		memcpy(modname, p, len);
		modname[len] = '\0';
	}

	if ((strchr(filename, '/') == 0) && (strchr(filename, '.') == 0)) {
		struct stat dummy;
		char **defp;
		char *path;
		char *onepath;
		char *p;

		/* Look in the path given by MODPATH environment,
		 * if not found, look in the default directories.
                 * <storner@osiris.ping.dk> 950319 + Bjorn
		 */
		if ((path = getenv("MODPATH")) == 0)  {
			onepath = ckalloc(2000); /* or whatever */
			onepath[0] = '\0';
			for (defp = default_path; *defp; ++defp) {
				sprintf(spare_path, *defp, uts_info.release);
				strcat(onepath, spare_path);
				strcat(onepath, ":");
			}
		}
		else {
			onepath = ckalloc(strlen(path)+2);
			sprintf(onepath, "%s:", path);
		}

		for (path = onepath; (*path != '\0'); path += strlen(path)+1) {
			for (p = path; (*p != ':') && (*p != '\0'); p++)
				; 
			*p = '\0';
			sprintf(spare_path, "%s/%s", path, filename);
			if ((stat(spare_path, &dummy) >= 0) && (dummy.st_mode & S_IFREG))
				break;
			/* else */
			strcat(spare_path, ".o");
			if ((stat(spare_path, &dummy) >= 0) && (dummy.st_mode & S_IFREG))
				break;
		}
		free(onepath);
		filename = spare_path;
	}

	/* open file and read header */
	if ((fp = fopen(filename, "r")) == NULL) {
		insmod_error ("Cannot open %s", filename);
		exit(2);
	}

	/* sizeof(struct elfhdr) > sizeof(struct exec) */
	fread(&header, sizeof(Elf32_Ehdr), 1, fp);
	if (feof(fp) || ferror(fp)) {
		insmod_error ("Could not read header of %s", filename);
		exit(2);
	}

	symtab = (struct symbol *)0;
	nsymbols = 0;

	if (N_MAGIC((*aouthdr)) == OMAGIC) {
		char *errstr;

		if ((errstr = load_aout(fp)) != (char *)0) {
			insmod_error ("%s: %s", filename, errstr);
			exit(2);
		}
	}
	else if ((header.e_ident[0] == 0x7f) &&
		 (strncmp(&header.e_ident[1], "ELF",3) == 0) &&
		 (header.e_type == ET_REL) &&
#ifdef __i386__
		 ((header.e_machine == 3) || (header.e_machine == 6))
#endif
#ifdef __mc68000__
		 header.e_machine == EM_68K
#endif
#ifdef __sparc__
		 header.e_machine == EM_SPARC
#endif
		) {
			char *errstr;

			if ((errstr = load_elf(fp)) != (char *)0) {
				insmod_error ("%s: %s", filename, errstr);
				exit(2);
			}
	}
	else {
		insmod_error ("%s: not an object file", filename);
		exit(2);
	}

	/* JEJB: now we have textseg fixed after the load, so save value
	 * for later finding offset if realloc forces a move */
	otextseg = textseg;

	if (findsym("Using_Versions", NULL, strncmp))
		versioned_module = 1;
	else
		versioned_module = 0;

	/* check version info */
        if (verbose) {
		insmod_error ("versioned kernel: %s\nversioned module: %s",
			versioned_kernel ? "yes" : "no",
			versioned_module ? "yes" : "no");
		insmod_error ("%s kernel\n%s module",
			elf_kernel ? "ELF" : "a.out",
			aout_flag ? "a.out" : "ELF");
	}
	/*
	 * Logic:
	 *
	 * versioned_kernel versioned_module	action
	 * ================ ================	=============================
	 *	no		no		same kernel and module version
	 *					all symbols must match
	 *	no		yes		same kernel and module version
	 *					ignore symbol version suffix
	 *	yes		no		same kernel and module version
	 *					ignore symbol version suffix
	 *	yes		yes		all symbols must match,
	 *					including the version suffix
	 */
	switch ((versioned_kernel << 1) + versioned_module) {
	case 0: /* unversioned_kernel, unversioned_module */
		/* Uwe Bonnes <bon@elektron.ikp.physik.th-darmstadt.de>: */
		if (!force_load)
			check_version(force_load);
		tabcomp = strncmp;
		break;

	case 1: /* unversioned_kernel, versioned_module */
		check_version(force_load);
		tabcomp = m_strncmp;
		break;

	case 2: /* versioned_kernel,   unversioned_module */
		check_version(force_load);
		tabcomp = k_strncmp;
		break;

	case 3: /* versioned_kernel,   versioned_module */
		if (force_load)
			check_version(force_load);
		tabcomp = strncmp;
		break;

	}

	/* get initialization and cleanup routines */
	init_func = looksym("init_module");
	cleanup_func = looksym("cleanup_module");

	/* bind undefined symbols (ELF-stuff hidden in defsym()) */
	defsym(strncmp, "mod_use_count_", 0 - sizeof (int), N_BSS | N_EXT, TRANSIENT);

	/* First: resolve symbols using kernel transient symbols */
	/* Then: use resident kernel symbols to resolve the last ones... */
	for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) {
		/* Magic in this version of the new get_kernel_syms:
		 * Every module is sent along as a symbol,
		 * where the module name is represented as "#module", and
		 * the address of the module struct is stuffed into the value.
		 * The name "#" means that the symbols that follow are
		 * kernel resident.
		 */
		if (ksym->name[0] == '#') {
			curr_module = ksym;
			continue;
		}

 		if (defsym(tabcomp, ksym->name +
		    ((!elf_kernel && (ksym->name[0] == '_'))?1:0),
			ksym->value, N_ABS | N_EXT,
		/* this is safe since curr_module was initialized properly */
			(curr_module->name[1]) ?  TRANSIENT : RESIDENT)) {
			/* kludge: mark referenced modules */
			if (curr_module->name[1] && /* but not the kernel */
				(curr_module->name[0] == '#')) {
				curr_module->name[0] = '+';
				++module_refs;
			}
		}
	}

	/* allocate space for "common" symbols */
	/* and check for undefined symbols */
	fatal_error = 0;
	bss_offset = codesize + bss1size;
	/*
	 * Scan symbol table to determine global bss size
	 */
	for (sp = symtab ; sp < symtab + nsymbols ; sp++) {
		if ((symname(sp) == (char *)0) || (*symname(sp) == '\0'))
			continue;

		if ( ((aout_flag && (sp->u.n.n_type == (N_UNDF | N_EXT))) ||
			(!aout_flag && is_global(sp) && is_undef(sp)))
			&& (symvalue(sp) != 0)) {
			int len;

			if (aout_flag) {
				sp->u.n.n_type = N_BSS | N_EXT;
				len = symvalue(sp);
 				if (verbose)
 				        insmod_error (
 					"bss2 sym %X, size =%7d: %s",
 			                bss_offset, len, symname( sp));
			}
			else { /* I'm not sure I understand this... */
				sp->u.e.st_info = (STB_GLOBAL << 4)| STT_OBJECT;
				sp->u.e.st_shndx = SHN_ABS;
				len = (sp->u.e.st_size)?(sp->u.e.st_size):4;
			}
 			symvalue(sp) = bss_offset;
 			bss_offset += len;
		} else if (is_undef(sp)) {
			if (versioned_module) {
				char *v = strrchr(symname(sp), '_');
				if (v && (strncmp(v, "_R", 2) == 0) &&
					(strlen(v) == 10))
					*v = '\0';
				insmod_error ("%s: wrong version or undefined",
					symname(sp));
				if (v && !(*v))
					*v = '_';
			}
			else
				insmod_error ("%s undefined", symname(sp));
			fatal_error = 1;
		}
	}
	if (fatal_error) {
		insmod_error (  "Loading failed! The module symbols "
				"(from linux-%s) don't match your linux-%s",
			(char*) textseg + looksym("kernel_version"),
			uts_info.release);
		exit(2);
	}

	/* Change values according to the parameters to "insmod" */
	{
		struct symbol *change;
		int patchoff;
		char *param;
		char *val;
		int value;

		while (argc > 1) {
			param = *argv;
			++argv;
			--argc;

			/*
			 * This makes the following contruct legal:
			 *    insmod module.o -o new_name
			 * This is used by the "options" configuration
			 * lines for modprobe.
			 */
			if (strcmp(param, "-o") == 0) {
				modname = (char*) ckalloc(strlen(argv[0]) + 1);
				strcpy(modname, argv[0]);
				--argc;
				++argv;
				continue;
			}
			if ((val = strchr(param, '=')) == NULL)
				continue;
			*val = '\0'; /* mark end of tag */

			change = findsym(param, NULL, tabcomp);
			if (change == NULL) {
				insmod_error ("Symbol '%s' not found", param);
				exit(2);
			}
			patchoff = symvalue(change);

			do {
				++val;
#ifdef PERSIST
				/*
				 * This enables the useage of previously
				 * saved module persistent data to update
				 * the symbol value.
				 *
				 * Syntax:
				 *
				 *	symbol=?key:default,default...
				 *
				 * If no value can be found for the key,
				 * the default values will be used.
				 */
				if (*val == '?') {
#include <gdbm.h>
					GDBM_FILE dbf;
					datum key, data;
					char *x;

					key.dptr = val + 1;
					if ((x = strchr(val, ':')) != NULL) {
						*x = '\0';
						val = ++x;
					}
					if ((key.dptr[0] == '\'') ||
					    (key.dptr[0] == '"')) {
						if ((x = strchr(key.dptr, key.dptr[0]))) {
							*x = '\0';
					    		key.dptr++;
						}
					}
					key.dsize = strlen(key.dptr) + 1;

					if ((dbf = gdbm_open(kd_dbmfile, 0,
						    GDBM_READER, 0600, NULL))) {
						data = gdbm_fetch(dbf, key);
						gdbm_close(dbf);
						if (data.dptr) {
							/* found a value! */
							memcpy((int *)(textseg + patchoff), data.dptr, data.dsize);
							free (data.dptr);
							/* ignore given info */
							break;
						}
					}
					/*
					 * No persistent data found,
					 * use the given info
					 */
					if (*val == '\0')
						break;
				}
#endif /* PERSIST */
				if (*val < '0' || '9' < *val) {
				      /*
				       * Textseg may change during calls to 
				       * push_string() so we store the offset
				       *
				       * Timo Kokkonen <timo@cs.ualberta.ca>
				       */
					if (*val != ',')
					{
				     		push_string(val, patchoff);
						patchoff += sizeof(char *);
					}
				}
				else { /* numerical */
					if (val[0] == '0') {
						if (val[1] == 'x')
							sscanf(val, "%x", &value);
						else
							sscanf(val, "%o", &value);
					}
					else
						sscanf(val, "%d", &value);
					*((int *)(textseg + patchoff)) = value;
					patchoff += sizeof(int);
				}
			} while ((val = strchr(val, ',')) != (char *)0);
		}
	}

	/* Now, we are going to copy the strings */
	if(n_stringpatches > 0)
	  {
	    int		sum = 0;
	    int		n = n_stringpatches;
	    u_int	string_offset = progsize;

	    /* Calculate the increase needed */
	    for(patch = stringpatches; n-- > 0; ++patch)
	      sum += patch->len + 1;

	    progsize += sum;
	    /* JEJB: don't like this, but if we're copying strings, better
	     * make sure memory actually exists to copy them into */
	    textseg = ckrealloc(textseg, progsize);

	    /* Copy all the strings */
	    n = n_stringpatches;
	    for(patch = stringpatches; n-- > 0; ++patch)
	      {
		strncpy(textseg + string_offset, patch->string, patch->len);
		*(textseg + string_offset + patch->len) = '\0';
		patch->what = string_offset;
		string_offset += patch->len + 1;
	      }
	  }

	/*
	 * If we have survived this far, and are just checking,
	 * that's it...
	 */
	if (silent_poll)
		exit(0);

	/* create the module */
	errno = 0;
	/* make sure we have enough memory malloc'd for copy which kernel
	 * will perform */
	textseg = ckrealloc(textseg, progsize);
	/* We add "sizeof (int)" to skip over the use count */
	addr = create_module(modname, progsize) + sizeof (int);

	switch (errno) {
	case EEXIST:
		insmod_error ("A module named %s already exists", modname);
		exit(2);
	case ENOMEM:
		insmod_error ("Cannot allocate space for module");
		exit(2);
	case 0:
		break;
	default:
		perror("create_module");
		exit(2);
	}

	/* perform relocation */
	if (aout_flag)
		relocate_aout(fp, textseg - otextseg);
	else
		relocate_elf(fp, textseg - otextseg);

	/*
	 * Patch in any new strings from the command line
	 */
	for (patch = stringpatches; n_stringpatches-- > 0; ++patch) {
		/*
		 * Now we have to calculate the "absolute" address of the
		 * string and update the "pointer" in the module image...
		 *
		 * Timo Kokkonen <timo@cs.ualberta.ca>
		 *
		 * Pointer increment fixes by:
		 *   Alberto Vignani <alberto.vignani@torino.alpcom.it>
		 *   Wolfgang Wander <wwc@rem-wwc.desy.de>
		 */
		*((int *)(textseg + patch->where)) = addr + patch->what;
		/* insmod address */			 /* kernel address */
	}
	if (stringpatches)
		free(stringpatches);
	stringpatches = (struct strpatch *)0;
	n_stringpatches = 0;


	/*
	 * Handle module "administrativia"
	 */
	init_func += addr;
	cleanup_func += addr;

	/* "hide" the module specific symbols, not to be exported! */
	hidesym("cleanup_module");
	hidesym("init_module");
	hidesym("kernel_version");
	hidesym("mod_use_count_");

	/* Build the module symbol table */
	/* abuse of *_other:
	 * 0	refer kernel resident
	 * 1	define module symbol, inserted in kernel syms
	 * 2	refer module symbol
	 * 3	won't happen (define resolved by other module?)
	 */

	/*
	 * Get size info for the new symbol table
	 * we already have module_refs
	 */
	for (sp = symtab ; export_flag && (sp < symtab + nsymbols) ; sp++) {
		if (is_global(sp) && (symother(sp) & DEF_BY_MODULE)) {
			string_table_size += strlen(symname(sp)) + 1;
			if (!elf_kernel)
				++string_table_size;
			++n_symboldefs;
		}
	}
	newtab = (struct symbol_table *)ckalloc(sizeof(struct symbol_table) +
		n_symboldefs * sizeof(struct internal_symbol) +
		module_refs * sizeof(struct module_ref) +
		string_table_size);

	newtab->size = sizeof(struct symbol_table) +
		n_symboldefs * sizeof(struct internal_symbol) +
		module_refs * sizeof(struct module_ref) +
		string_table_size;

	newtab->n_symbols = n_symboldefs;
	newtab->n_refs = module_refs;

	symp = &(newtab->symbol[0]);
	stringp = ((char *)symp) + 
		n_symboldefs * sizeof(struct internal_symbol) +
		module_refs * sizeof(struct module_ref);

	/* update the string pointer (to a string index) in the symbol table */
	for (sp = symtab ; export_flag && (sp < symtab + nsymbols) ; sp++) {
		if (is_global(sp) && (symother(sp) & DEF_BY_MODULE)) {
			symp->addr = (void *)(symvalue(sp) + addr);
			symp->name = (char *)(stringp - (char *)newtab);
			if (!elf_kernel) {
				strcpy(stringp, "_");
				++stringp;
			}
			strcpy(stringp, symname(sp));
			stringp += strlen(symname(sp)) + 1;
			++symp;
		}
	}

	refp = (struct module_ref *)symp;
	for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) {
		if (ksym->name[0] == '+') {
			refp->module = (struct module *)ksym->value;
			++refp;
		}
	}

	/* load the module into the kernel */
	/* NOTE: the symbol table == NULL if there are no defines/updates */
	routines.mod21.init = (int (*)(void)) init_func;
	routines.mod21.cleanup = (void (*)(void)) cleanup_func;
	{
		char *start=NULL, *end=NULL;
		if (!aout_flag){
			elf_sect_info ("__ex_table",addr,&start,&end);
			#if 0
				fprintf (stderr,"Locate exception %p %p %08lx\n",start,end,addr);
			#endif
		}
		routines.mod21.signature = MODULE_2_1_7_SIG;
		routines.mod21.exceptinfo.start = start;
		routines.mod21.exceptinfo.end = end;
		if (kernelv < 2001008){
			/* See kernel/module.c for an explanation (jg) */
			routines.mod20.init = (int (*)(void)) init_func;
			routines.mod20.cleanup = (void (*)(void)) cleanup_func;
		}
	}
	if (init_module(modname, textseg,
		/* An autoclean marker for a non-kerneld-aware kernel
		 * will result in a rejection of the module.
		 * This is what we want, since the "-k" option is useful
		 * only with kerneld aware kernels...
		 */
		autoclean | progsize /*codesize*/, &routines,
		((n_symboldefs + module_refs)? newtab : NULL)) < 0) {

		if (errno == EBUSY) {
			insmod_error ("Initialization of %s failed", modname);
		} else {
			perror("init_module");
		}
		delete_module(modname);
		exit(1);
	}

	/*
	 * Print a loadmap so that kernel panics can be identified.
	 * Load map option conceived by Derek Atkins <warlord@MIT.EDU>
	 */
	for (sp = symtab; loadmap && (sp < symtab + nsymbols) ; sp++) {
		char symtype = 'u';

		if (aout_flag) {
			if ((sp->u.n.n_type & ~N_EXT) == N_ABS)
				continue;

			switch (sp->u.n.n_type & ~N_EXT) {
			case N_TEXT | N_EXT: symtype = 'T'; break;
			case N_TEXT: symtype = 't'; break;
			case N_DATA | N_EXT: symtype = 'D'; break;
			case N_DATA: symtype = 'd'; break;
			case N_BSS | N_EXT: symtype = 'B'; break;
			case N_BSS: symtype = 'b'; break;
			}
		}
#if 0
		else
			symtype = ' '; /* until someone does the work... */
#endif

		insmod_printf("%X %c %s\n",
			symvalue(sp) + addr, symtype, symname(sp));
	}

	if (nksyms > 0)
		free(ksymtab); /* it has done its job */

	exit(0);
}

void hidesym(const char *name)
{
	struct symbol *sp;

	if ((sp = findsym(name, NULL, strncmp)) != NULL) {
		if (aout_flag)
			sp->u.n.n_type &= ~N_EXT;
		else
			sp->u.e.st_info = (STB_LOCAL << 4) |
		    			  (ELF32_ST_TYPE(sp->u.e.st_info));
	}
}

int defsym(int (*pfi) (const char *, const char *, size_t),
	const char *name, unsigned long value, int type, int source)
{
	struct symbol *sp;
	/*
	 * field "*_other" abused in the symbol table
	 * in order to discriminate between defines and references
	 * and resolves by kernel resident or transient symbols...
	 */

	if ((sp = findsym(name, NULL, pfi)) == NULL)
		return 0; /* symbol not used */
	/* else */

	/*
	 * Multiply defined?
	 * Well, this is what we _want_!
	 * I.e. a module _should_ be able to replace a kernel resident
	 * symbol, or even a symbol defined by another module!
	 * NOTE: The symbols read from the kernel (the transient ones)
	 * MUST be handled in the order:
	 *   last defined, ..., earlier defined, ...
	 */
	if (aout_flag) {
		if (sp->u.n.n_type != (N_UNDF | N_EXT))
			return 0; /* symbol not used */
		sp->u.n.n_type = type;
	}
	else { /* elf */
		if ((ELF32_ST_BIND(sp->u.e.st_info) == STB_LOCAL) ||
		    (sp->u.e.st_shndx != SHN_UNDEF))
			return 0; /* symbol not used */
		/* hack follows... */
		if (type & N_ABS)
			sp->u.e.st_info = (STB_GLOBAL << 4);
		else
			sp->u.e.st_info = (STB_LOCAL << 4);
		sp->u.e.st_shndx = SHN_ABS; /* or whatever */
	}

	symother(sp) |= source;
	symvalue(sp) = value;

	return 1; /* symbol used */
}


/*
 * Look up an name in the symbol table.  If "add" is not null, add a
 * the entry to the table.  The table is stored as a splay tree.
 */
struct symbol * findsym(const char *key, struct symbol *add,
	int (*pfi) (const char *, const char *, size_t))
{
	struct symbol *left, *right;
	struct symbol **leftp, **rightp;
	struct symbol *sp1, *sp2, *sp3;
	int cmp;
	int path1, path2;

	if (add) {
		if (aout_flag)
			add->u.n.n_un.n_name = (char *)key;
		else
			add->u.e.st_name = (Elf32_Word)key;
	}
	sp1 = symroot;
	if (sp1 == NULL)
		return add? symroot = add : NULL;
	leftp = &left, rightp = &right;
	for (;;) {
		cmp = (*pfi)(symname(sp1), key, SYM_MAX_NAME);
		if (cmp == 0)
			break;
		if (cmp > 0) {
			sp2 = sp1->child[0];
			path1 = 0;
		} else {
			sp2 = sp1->child[1];
			path1 = 1;
		}
		if (sp2 == NULL) {
			if (! add)
				break;
			sp2 = add;
		}
		cmp = (*pfi)(symname(sp2), key, SYM_MAX_NAME);
		if (cmp == 0) {
one_level_only:
			if (path1 == 0) {	/* sp2 is left child of sp1 */
				*rightp = sp1;
				rightp = &sp1->child[0];
			} else {
				*leftp = sp1;
				leftp = &sp1->child[1];
			}
			sp1 = sp2;
			break;
		}
		if (cmp > 0) {
			sp3 = sp2->child[0];
			path2 = 0;
		} else {
			sp3 = sp2->child[1];
			path2 = 1;
		}
		if (sp3 == NULL) {
			if (! add)
				goto one_level_only;
			sp3 = add;
		}
		if (path1 == 0) {
			if (path2 == 0) {
				sp1->child[0] = sp2->child[1];
				sp2->child[1] = sp1;
				*rightp = sp2;
				rightp = &sp2->child[0];
			} else {
				*rightp = sp1;
				rightp = &sp1->child[0];
				*leftp = sp2;
				leftp = &sp2->child[1];
			}
		} else {
			if (path2 == 0) {
				*leftp = sp1;
				leftp = &sp1->child[1];
				*rightp = sp2;
				rightp = &sp2->child[0];
			} else {
				sp1->child[1] = sp2->child[0];
				sp2->child[0] = sp1;
				*leftp = sp2;
				leftp = &sp2->child[1];
			}
		}
		sp1 = sp3;
	}
	/*
	 * Now sp1 points to the result of the search.  If cmp is zero,
	 * we had a match; otherwise not.
	 */
	*leftp = sp1->child[0];
	*rightp = sp1->child[1];
	sp1->child[0] = left;
	sp1->child[1] = right;
	symroot = sp1;
	return cmp == 0? sp1 : NULL;
}


unsigned long looksym(const char *name)
{
	struct symbol *sp;

	sp = findsym(name, NULL, strncmp);
	if (sp == NULL) {
		insmod_error ("%s needed, but can't be found", name);
		exit(2);
	}
	return symvalue(sp);
}

/*
 * This is (was) rmmod, now merged with insmod!
 *
 * Original author: Jon Tombs <jon@gtex02.us.es>,
 * and extended by Bjorn Ekwall <bj0rn@blox.se> in 1994 (C).
 * See the file COPYING for your rights (GNU GPL)
 */

#define WANT_TO_REMOVE 1
#define CAN_REMOVE 2

struct ref {
	int modnum;
	struct ref *next;
};

struct mod {
	int status; /* or of: WANT_TO_REMOVE, CAN_REMOVE */
	char name[MOD_MAX_NAME];
	struct ref *ref;
};

static struct mod *loaded;
static int current = -1;

/* build the references as shown in /proc/ksyms */
static void get_stacks()
{
	FILE *fp;
	struct ref *rp;
	int i;
	char line[200]; /* or whatever... */
	char *p;
	char *r;

	if ((fp = fopen("/proc/modules", "r")) == (FILE *)0) {
		perror("/proc/modules");
		exit(1);
	}

	while (fgets(line, 200, fp)) {
		if (loaded) {
			++current;
			loaded = (struct mod *)ckrealloc(loaded,
				(current + 1) * sizeof(struct mod));
		} else {
			current = 0;
			loaded = (struct mod *)ckalloc(sizeof(struct mod));
		}

		loaded[current].status = 0;
		loaded[current].ref = (struct ref *)0;

		/* save the module name */
		p = strchr(line, ' ');
		*p = '\0';
		strcpy(loaded[current].name, line);

		/* any references? */
		if ((p = strchr(p + 1, '['))) {
			*(strrchr(p + 1, ']')) = '\0';
			do {
				r = p + 1;
				if ((p = strchr(r, ' ')))
					*p = '\0';
				for (i = 0; i < current; ++i) {
					if (strcmp(loaded[i].name, r) == 0)
						break;
				}

				if (i == current) { /* not found! */
					insmod_error (
					"Strange reference in "
					"/proc/modules: '%s' used by '%s'?",
					loaded[current].name, r);
					exit(1);
				}

				rp = (struct ref *)ckalloc(sizeof(struct ref));
				rp->modnum = i;
				rp->next = loaded[current].ref;
				loaded[current].ref = rp;
			} while (p);
		}
	}

	fclose(fp);
}

int rmmod(int argc, char **argv)
{
	struct ref *rp;
	int i, j;
	int errors = 0;
	int count;
	char **list;
	char *p;

	if (argc == 1) {
		fprintf(stderr, "usage: rmmod [-r] [-s] module ...\n");
		return 1;
	}
	/* else */

	if (strcmp(argv[1], "-r") != 0) {
		if (strcmp(argv[1], "-a") == 0) {
			/* delete all unused modules and stacks */
			return (delete_module(NULL))?1:0;
		}
		/* else */
		if (strcmp(argv[1], "-s") == 0){
			insmod_setsyslog("rmmod");
			--argc;
			++argv;
		}
		while (argc > 1) {
			if ((p = strrchr(argv[1], '.')) &&
				((strcmp(p, ".o") == 0) ||
				 (strcmp(p, ".mod") == 0))) 
					*p = '\0';
			if (delete_module(argv[1]) < 0) {
				++errors;
				perror(argv[1]);
			}
			--argc;
			++argv;
		}
		return errors;
	}
	/* else recursive removal */

	count = argc - 2;
	list = &(argv[2]);
	if (count <= 0) {
		insmod_error ("usage: rmmod [-r] module ...");
		return 1;
	}

	get_stacks();

	for (i = 0; i < count; ++i) {
		for (j = 0; j <= current; ++j) {
			if (strcmp(loaded[j].name, list[i]) == 0) {
				loaded[j].status = WANT_TO_REMOVE;
				break;
			}
		}
		if (j > current) {
			insmod_error("module '%s' not loaded", list[i]);
			++errors;
		}
	}

	for (i = 0; i <= current; ++i) {
		if (loaded[i].ref || (loaded[i].status == WANT_TO_REMOVE))
			loaded[i].status |= CAN_REMOVE;

		for (rp = loaded[i].ref; rp; rp = rp->next) {
			switch (loaded[rp->modnum].status) {
			case CAN_REMOVE:
			case WANT_TO_REMOVE | CAN_REMOVE:
				break;

			case WANT_TO_REMOVE:
				if (loaded[rp->modnum].ref == (struct ref *)0)
					break;
				/* else fallthtough */
			default:
				loaded[i].status &= ~CAN_REMOVE;
				break;
			}
		}

		switch (loaded[i].status) {
		case CAN_REMOVE:
		case WANT_TO_REMOVE | CAN_REMOVE:
			if (delete_module(loaded[i].name) < 0) {
				++errors;
				perror(loaded[i].name);
			}
			break;

		case WANT_TO_REMOVE:
			insmod_error("module '%s' is in use!", loaded[i].name);
			++errors;
			break;
		}
	}

	return errors;
}


/*
 * Used to be ksyms.c, but merged as well...
 *
 * Get kernel symbol table(s).
 *
 * Bjorn Ekwall <bj0rn@blox.se> in 1994 (C)
 * See the file COPYING for your rights (GNU GPL)
 */

int ksyms(int argc, char **argv)
{
	struct kernel_sym *ksymtab = NULL;
	struct kernel_sym *ksym = NULL;
	struct module module_struct;
	int nksyms;
	int i;
	int kmem = 0;
	int allsyms = 0;
	int show_header = 1;
	char *p;
	char *module_name = "";

	while (argc > 1 && (argv[1][0] == '-')) {
		p = &(argv[1][1]);
		while (*p) {
			switch (*p) {
			case 'a':
				allsyms = 1;
				break;

			case 'm':
				if ((kmem = open("/dev/kmem", O_RDONLY)) < 0) {
					perror("/dev/kmem");
					exit(2);
				}
				break;

			case 'h':
				show_header = 0;
				break;
			}
			++p;
		}
		--argc;
		++argv;
	}

	if (argc < 1) {
		fputs("Usage: ksyms [-a] [-h]\n", stderr);
		return 2;
	}

	/* get the size of the current kernel symbol table */
	nksyms = get_kernel_syms(NULL);

	if (nksyms < 0) {
		insmod_error ("get_kernel_sys failed: Cannot find Kernel symbols!");
		return 2;
	}

	if (nksyms) {
		ksymtab = (struct kernel_sym *) ckalloc(nksyms * sizeof *ksymtab);
		/* NOTE!!! The order of the symbols is important */
		if (get_kernel_syms(ksymtab) != nksyms) {
			insmod_error("Kernel symbol problem");
			return 2;
		}
	}

	if (show_header)
		printf("Address  Symbol    \tDefined by\n");

	for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) {
		/* Magic in this version of the new get_kernel_syms:
		 * Every module is sent along as a symbol,
		 * where the module name is represented as "#module", and
		 * the address of the module struct is stuffed into the value.
		 * The name "#" means that the symbols that follow are
		 * kernel resident.
		 */
		if (ksym->name[0] == '#') {
			module_name = ksym->name + 1;
			if (!allsyms && (*module_name == '\0'))
				break;
			/* else */
			if (kmem) {
				if (
#if 0
		(lseek(kmem, (off_t)ksym->value, SEEK_SET) > 0) &&
#else
		/* Keith Owens <kaos@audio.apana.org.au> */
		(llseek(kmem, (off_t)ksym->value, SEEK_SET) != -1) &&
#endif
		(read(kmem, (char *)&module_struct, sizeof(struct module)) ==
		sizeof(struct module))) {
					printf("%lX --- (%dk) ---\t[%s]\n",
					(long)module_struct.addr,
					module_struct.size * 4,
					module_name);
				}
				else {
					perror("/dev/kmem");
					return 2;
				}
			}
			continue;
		}

		printf("%lX %s", ksym->value, ksym->name);
		if (*module_name)
			printf("\t[%s]", module_name);
		printf("\n");
	}

	return 0;
}
