summaryrefslogtreecommitdiffstats
path: root/mdk-stage1/insmod-modutils/insmod.c
diff options
context:
space:
mode:
authorGuillaume Cottenceau <gc@mandriva.com>2001-02-22 17:21:43 +0000
committerGuillaume Cottenceau <gc@mandriva.com>2001-02-22 17:21:43 +0000
commit31d44a623579fbca300f20bc751c7278c4375980 (patch)
treee54cb0772ebc6ffce9fc7ccdcc61d094751d3b54 /mdk-stage1/insmod-modutils/insmod.c
parent446293819c1c265f0799036bde77f98145187ecf (diff)
downloaddrakx-backup-do-not-use-31d44a623579fbca300f20bc751c7278c4375980.tar
drakx-backup-do-not-use-31d44a623579fbca300f20bc751c7278c4375980.tar.gz
drakx-backup-do-not-use-31d44a623579fbca300f20bc751c7278c4375980.tar.bz2
drakx-backup-do-not-use-31d44a623579fbca300f20bc751c7278c4375980.tar.xz
drakx-backup-do-not-use-31d44a623579fbca300f20bc751c7278c4375980.zip
use modutils for non Intel arch's
Diffstat (limited to 'mdk-stage1/insmod-modutils/insmod.c')
-rw-r--r--mdk-stage1/insmod-modutils/insmod.c1964
1 files changed, 1964 insertions, 0 deletions
diff --git a/mdk-stage1/insmod-modutils/insmod.c b/mdk-stage1/insmod-modutils/insmod.c
new file mode 100644
index 000000000..359b28837
--- /dev/null
+++ b/mdk-stage1/insmod-modutils/insmod.c
@@ -0,0 +1,1964 @@
+/* Insert a module into a running kernel.
+ Copyright 1996, 1997 Linux International.
+
+ New implementation contributed by Richard Henderson <rth@tamu.edu>
+ Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+ Restructured (and partly rewritten) by:
+ Björn Ekwall <bj0rn@blox.se> February 1999
+
+ This file is part of the Linux modutils.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+ /*
+ Fixes:
+
+ Adjust module size for mod_use_count in old_init_module:
+ B. James Phillippe <bryan@terran.org>
+
+ Merged modprobe + many fixes: Björn Ekwall <bj0rn@blox.se> February 1999
+ SMP "friendliness" (and -P): Bill Zumach <zumach+@transarc.com>
+
+ Ksymoops support: Keith Owens <kaos@ocs.com.au> August 1999.
+
+ Add -r flag: Keith Owens <kaos@ocs.com.au> October 1999.
+
+ More flexible recognition of the way the utility was called.
+ Suggested by Stepan Kasal, implemented in a different way by Keith
+ Owens <kaos@ocs.com.au> December 1999.
+
+ Rationalize common code for 32/64 bit architectures.
+ Keith Owens <kaos@ocs.com.au> December 1999.
+ Add arch64().
+ Keith Owens <kaos@ocs.com.au> December 1999.
+ kallsyms support
+ Keith Owens <kaos@ocs.com.au> April 2000.
+ archdata support
+ Keith Owens <kaos@ocs.com.au> August 2000.
+ Add insmod -O, move print map before sys_init_module.
+ Keith Owens <kaos@ocs.com.au> October 2000.
+ Add insmod -S.
+ Keith Owens <kaos@ocs.com.au> November 2000.
+ Add persistent data support.
+ Keith Owens <kaos@ocs.com.au> November 2000.
+ */
+
+#ident "$Id$"
+
+#include "../insmod.h"
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+//#include <alloca.h> provided by stdlib
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#include "module.h"
+#include "obj.h"
+#include "kallsyms.h"
+#include "util.h"
+#include "version.h"
+
+#include "modstat.h"
+#include "config.h"
+
+#define STRVERSIONLEN 32
+
+/*======================================================================*/
+
+static int flag_force_load = 0;
+static int flag_silent_probe = 0;
+static int flag_export = 1;
+static int flag_load_map = 0;
+static int flag_ksymoops = 1;
+
+static int n_ext_modules_used;
+static int m_has_modinfo;
+
+extern int insmod_main(int argc, char **argv);
+extern int insmod_main_32(int argc, char **argv);
+extern int insmod_main_64(int argc, char **argv);
+extern int modprobe_main(int argc, char **argv);
+extern int rmmod_main(int argc, char **argv);
+extern int ksyms_main(int argc, char **argv);
+extern int lsmod_main(int argc, char **argv);
+extern int kallsyms_main(int argc, char **argv);
+
+/*======================================================================*/
+
+/* Get the kernel version in the canonical integer form. */
+
+static int get_kernel_version(char str[STRVERSIONLEN])
+{
+ char *p, *q;
+ int a, b, c;
+
+ strncpy(str, uts_info.release, STRVERSIONLEN);
+ p = uts_info.release;
+
+ a = strtoul(p, &p, 10);
+ if (*p != '.')
+ return -1;
+ b = strtoul(p + 1, &p, 10);
+ if (*p != '.')
+ return -1;
+ c = strtoul(p + 1, &q, 10);
+ if (p + 1 == q)
+ return -1;
+
+ return a << 16 | b << 8 | c;
+}
+
+/* String comparison for non-co-versioned kernel and module.
+ * prefix should be the same as used by genksyms for this kernel.
+ */
+static char *ncv_prefix = NULL; /* Overridden by --prefix option */
+static int ncv_plen = 0;
+
+/* Only set prefix once. If set by the user, use it. If not set by the
+ * user, look for a well known kernel symbol and derive the prefix from
+ * there. Otherwise set the prefix depending on whether uts_info
+ * includes SMP or not for backwards compatibility.
+ */
+static void set_ncv_prefix(char *prefix)
+{
+ static char derived_prefix[256];
+ static const char *well_known_symbol[] = { "get_module_symbol_R",
+ "inter_module_get_R",
+ };
+ struct module_symbol *s;
+ int i, j, l, m, pl;
+ const char *name;
+ char *p;
+
+ if (ncv_prefix)
+ return;
+
+ if (prefix)
+ ncv_prefix = prefix;
+ else {
+ /* Extract the prefix (if any) from well known symbols */
+ for (i = 0, s = ksyms; i < nksyms; ++i, ++s) {
+ name = (char *) s->name;
+ l = strlen(name);
+ for (j = 0; j < sizeof(well_known_symbol)/sizeof(well_known_symbol[0]); ++j) {
+ m = strlen(well_known_symbol[j]);
+ if (m + 8 > l ||
+ strncmp(name, well_known_symbol[j], m))
+ continue;
+ pl = l - m - 8;
+ if (pl > sizeof(derived_prefix)-1)
+ continue; /* Prefix is wrong length */
+ /* Must end with 8 hex digits */
+ (void) strtoul(name+l-8, &p, 16);
+ if (*p == 0) {
+ strncpy(derived_prefix, name+m, pl);
+ *(derived_prefix+pl) = '\0';
+ ncv_prefix = derived_prefix;
+ break;
+ }
+ }
+ }
+ }
+ if (!ncv_prefix) {
+ p = strchr(uts_info.version, ' ');
+ if (p && *(++p) && !strncmp(p, "SMP ", 4))
+ ncv_prefix = "smp_";
+ else
+ ncv_prefix = "";
+ }
+ ncv_plen = strlen(ncv_prefix);
+ if (flag_verbose)
+ lprintf("Symbol version prefix '%s'", ncv_prefix);
+}
+
+static int ncv_strcmp(const char *a, const char *b)
+{
+ size_t alen = strlen(a), blen = strlen(b);
+
+ if (blen == alen + 10 + ncv_plen &&
+ b[alen] == '_' &&
+ b[alen + 1] == 'R' &&
+ !(ncv_plen && strncmp(b + alen + 2, ncv_prefix, ncv_plen))) {
+ return strncmp(a, b, alen);
+ } else if (alen == blen + 10 + ncv_plen &&
+ a[blen] == '_' && a[blen + 1] == 'R' &&
+ !(ncv_plen && strncmp(a + blen + 2, ncv_prefix, ncv_plen))) {
+ return strncmp(a, b, blen);
+ } else
+ return strcmp(a, b);
+}
+
+/*
+ * String hashing for non-co-versioned kernel and module.
+ * Here we are simply forced to drop the crc from the hash.
+ */
+static unsigned long ncv_symbol_hash(const char *str)
+{
+ size_t len = strlen(str);
+
+ if (len > 10 + ncv_plen &&
+ str[len - 10 - ncv_plen] == '_' &&
+ str[len - 9 - ncv_plen] == 'R' &&
+ !(
+ ncv_plen &&
+ strncmp(str + len - (8 + ncv_plen), ncv_prefix, ncv_plen)
+ ))
+ len -= 10 + ncv_plen;
+ return obj_elf_hash_n(str, len);
+}
+
+/*
+ * Conditionally add the symbols from the given symbol set
+ * to the new module.
+ */
+static int add_symbols_from(struct obj_file *f, int idx,
+ struct module_symbol *syms, size_t nsyms)
+{
+ struct module_symbol *s;
+ size_t i;
+ int used = 0;
+
+ for (i = 0, s = syms; i < nsyms; ++i, ++s) {
+ /*
+ * Only add symbols that are already marked external.
+ * If we override locals we may cause problems for
+ * argument initialization.
+ * We will also create a false dependency on the module.
+ */
+ struct obj_symbol *sym;
+
+ sym = obj_find_symbol(f, (char *) s->name);
+ if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL) {
+ sym = obj_add_symbol(f, (char *) s->name, -1,
+ ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ idx, s->value, 0);
+ /*
+ * Did our symbol just get installed?
+ * If so, mark the module as "used".
+ */
+ if (sym->secidx == idx)
+ used = 1;
+ }
+ }
+
+ return used;
+}
+
+static void add_kernel_symbols(struct obj_file *f)
+{
+ struct module_stat *m;
+ size_t i, nused = 0;
+
+ /* Add module symbols first. */
+ for (i = 0, m = module_stat; i < n_module_stat; ++i, ++m)
+ if (m->nsyms &&
+ add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms))
+ m->status = 1 /* used */, ++nused;
+ n_ext_modules_used = nused;
+
+ /* And finally the symbols from the kernel proper. */
+ if (nksyms)
+ add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
+}
+
+static void hide_special_symbols(struct obj_file *f)
+{
+ struct obj_symbol *sym;
+ const char *const *p;
+ static const char *const specials[] =
+ {
+ "cleanup_module",
+ "init_module",
+ "kernel_version",
+ NULL
+ };
+
+ for (p = specials; *p; ++p)
+ if ((sym = obj_find_symbol(f, *p)) != NULL)
+ sym->info = ELFW(ST_INFO) (STB_LOCAL, ELFW(ST_TYPE) (sym->info));
+}
+
+static void print_load_map(struct obj_file *f)
+{
+ struct obj_symbol *sym;
+ struct obj_symbol **all, **p;
+ struct obj_section *sec;
+ int load_map_cmp(const void *a, const void *b) {
+ struct obj_symbol **as = (struct obj_symbol **) a;
+ struct obj_symbol **bs = (struct obj_symbol **) b;
+ unsigned long aa = obj_symbol_final_value(f, *as);
+ unsigned long ba = obj_symbol_final_value(f, *bs);
+ return aa < ba ? -1 : aa > ba ? 1 : 0;
+ }
+ int i, nsyms, *loaded;
+
+ /* Report on the section layout. */
+
+ lprintf("Sections: Size %-*s Align",
+ (int) (2 * sizeof(void *)), "Address");
+
+ for (sec = f->load_order; sec; sec = sec->load_next) {
+ int a;
+ unsigned long tmp;
+
+ for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
+ tmp >>= 1;
+ if (a == -1)
+ a = 0;
+
+ lprintf("%-16s%08lx %0*lx 2**%d",
+ sec->name,
+ (long)sec->header.sh_size,
+ (int) (2 * sizeof(void *)),
+ (long)sec->header.sh_addr,
+ a);
+ }
+
+ /* Quick reference which section indicies are loaded. */
+
+ loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+ while (--i >= 0)
+ loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+ /* Collect the symbols we'll be listing. */
+
+ for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+ ++nsyms;
+
+ all = alloca(nsyms * sizeof(struct obj_symbol *));
+
+ for (i = 0, p = all; i < HASH_BUCKETS; ++i)
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+ *p++ = sym;
+
+ /* Sort them by final value. */
+ qsort(all, nsyms, sizeof(struct obj_file *), load_map_cmp);
+
+ /* And list them. */
+ lprintf("\nSymbols:");
+ for (p = all; p < all + nsyms; ++p) {
+ char type = '?';
+ unsigned long value;
+
+ sym = *p;
+ if (sym->secidx == SHN_ABS) {
+ type = 'A';
+ value = sym->value;
+ } else if (sym->secidx == SHN_UNDEF) {
+ type = 'U';
+ value = 0;
+ } else {
+ struct obj_section *sec = f->sections[sym->secidx];
+
+ if (sec->header.sh_type == SHT_NOBITS)
+ type = 'B';
+ else if (sec->header.sh_flags & SHF_ALLOC) {
+ if (sec->header.sh_flags & SHF_EXECINSTR)
+ type = 'T';
+ else if (sec->header.sh_flags & SHF_WRITE)
+ type = 'D';
+ else
+ type = 'R';
+ }
+ value = sym->value + sec->header.sh_addr;
+ }
+
+ if (ELFW(ST_BIND) (sym->info) == STB_LOCAL)
+ type = tolower(type);
+
+ lprintf("%0*lx %c %s", (int) (2 * sizeof(void *)), value,
+ type, sym->name);
+ }
+}
+
+/************************************************************************/
+/* begin compat */
+
+static char * get_modinfo_value(struct obj_file *f, const char *key)
+{
+ struct obj_section *sec;
+ char *p, *v, *n, *ep;
+ size_t klen = strlen(key);
+
+ sec = obj_find_section(f, ".modinfo");
+ if (sec == NULL)
+ return NULL;
+
+ p = sec->contents;
+ ep = p + sec->header.sh_size;
+ while (p < ep) {
+ v = strchr(p, '=');
+ n = strchr(p, '\0');
+ if (v) {
+ if (v - p == klen && strncmp(p, key, klen) == 0)
+ return v + 1;
+ } else {
+ if (n - p == klen && strcmp(p, key) == 0)
+ return n;
+ }
+ p = n + 1;
+ }
+
+ return NULL;
+}
+
+static int create_this_module(struct obj_file *f, const char *m_name)
+{
+ struct obj_section *sec;
+
+ sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long,
+ sizeof(struct module));
+ memset(sec->contents, 0, sizeof(struct module));
+
+ obj_add_symbol(f, "__this_module", -1, ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT),
+ sec->idx, 0, sizeof(struct module));
+
+ obj_string_patch(f, sec->idx, offsetof(struct module, name), m_name);
+
+ return 1;
+}
+
+#ifdef COMPAT_2_0
+static int old_create_mod_use_count(struct obj_file *f)
+{
+ struct obj_section *sec;
+ struct obj_symbol *got;
+
+ sec = obj_create_alloced_section_first(f, ".moduse",
+ sizeof(long), sizeof(long));
+
+ obj_add_symbol(f, "mod_use_count_",
+ -1, ELFW(ST_INFO)(STB_LOCAL, STT_OBJECT),
+ sec->idx, 0, sizeof(long));
+
+ /*
+ * patb: if there is a _GLOBAL_OFFSET_TABLE_,
+ * add .got section for PIC type modules;
+ * we have to do this here, because obj_* calls are not made until
+ * after obj_check_undefined
+ * is there a better place for this exception?
+ */
+ got = obj_find_symbol(f, "_GLOBAL_OFFSET_TABLE_");
+ if (got)
+{
+ sec = obj_create_alloced_section(f, ".got",
+ sizeof(long), sizeof(long));
+ got->secidx = sec->idx; /* mark the symbol as defined */
+ }
+ return 1;
+}
+#endif
+
+/* add an entry to the __ksymtab section, creating it if necessary */
+static void add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
+{
+ struct obj_section *sec;
+ ElfW(Addr) ofs;
+
+ /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.
+ * If __ksymtab is defined but not marked alloc, x out the first character
+ * (no obj_delete routine) and create a new __ksymtab with the correct
+ * characteristics.
+ */
+ sec = obj_find_section(f, "__ksymtab");
+ if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {
+ *((char *)(sec->name)) = 'x'; /* override const */
+ sec = NULL;
+ }
+ if (!sec)
+ sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);
+ if (!sec)
+ return;
+ sec->header.sh_flags |= SHF_ALLOC;
+
+ ofs = sec->header.sh_size;
+ obj_symbol_patch(f, sec->idx, ofs, sym);
+ obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);
+ obj_extend_section(sec, 2 * tgt_sizeof_char_p);
+}
+
+static int create_module_ksymtab(struct obj_file *f)
+{
+ struct obj_section *sec;
+ int i;
+
+ /* We must always add the module references. */
+
+ if (n_ext_modules_used) {
+ struct module_ref *dep;
+ struct obj_symbol *tm;
+
+ sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
+ (sizeof(struct module_ref)
+ * n_ext_modules_used));
+ if (!sec)
+ return 0;
+
+ tm = obj_find_symbol(f, "__this_module");
+ dep = (struct module_ref *) sec->contents;
+ for (i = 0; i < n_module_stat; ++i)
+ if (module_stat[i].status /* used */) {
+ dep->dep = module_stat[i].addr;
+ obj_symbol_patch(f, sec->idx, (char *) &dep->ref - sec->contents, tm);
+ dep->next_ref = 0;
+ ++dep;
+ }
+ }
+ if (flag_export && !obj_find_section(f, "__ksymtab")) {
+ int *loaded;
+
+ /* We don't want to export symbols residing in sections that
+ aren't loaded. There are a number of these created so that
+ we make sure certain module options don't appear twice. */
+
+ loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+ while (--i >= 0)
+ loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+ for (i = 0; i < HASH_BUCKETS; ++i) {
+ struct obj_symbol *sym;
+ for (sym = f->symtab[i]; sym; sym = sym->next) {
+ if (ELFW(ST_BIND) (sym->info) != STB_LOCAL
+ && sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE
+ || loaded[sym->secidx])) {
+ add_ksymtab(f, sym);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/* Get the module's kernel version in the canonical integer form. */
+static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+ int a, b, c;
+ char *p, *q;
+
+ if ((p = get_modinfo_value(f, "kernel_version")) == NULL) {
+ struct obj_symbol *sym;
+
+ m_has_modinfo = 0;
+ if ((sym = obj_find_symbol(f, "kernel_version")) == NULL)
+ sym = obj_find_symbol(f, "__module_kernel_version");
+ if (sym == NULL)
+ return -1;
+ p = f->sections[sym->secidx]->contents + sym->value;
+ } else
+ m_has_modinfo = 1;
+
+ strncpy(str, p, STRVERSIONLEN);
+
+ a = strtoul(p, &p, 10);
+ if (*p != '.')
+ return -1;
+ b = strtoul(p + 1, &p, 10);
+ if (*p != '.')
+ return -1;
+ c = strtoul(p + 1, &q, 10);
+ if (p + 1 == q)
+ return -1;
+
+ return a << 16 | b << 8 | c;
+}
+
+/* Return the kernel symbol checksum version, or zero if not used. */
+static int is_kernel_checksummed(void)
+{
+ struct module_symbol *s;
+ size_t i;
+
+ /*
+ * Using_Versions might not be the first symbol,
+ * but it should be in there.
+ */
+ for (i = 0, s = ksyms; i < nksyms; ++i, ++s)
+ if (strcmp((char *) s->name, "Using_Versions") == 0)
+ return s->value;
+
+ return 0;
+}
+
+static int is_module_checksummed(struct obj_file *f)
+{
+ if (m_has_modinfo) {
+ const char *p = get_modinfo_value(f, "using_checksums");
+ if (p)
+ return atoi(p);
+ else
+ return 0;
+ } else
+ return obj_find_symbol(f, "Using_Versions") != NULL;
+}
+
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections. this info is used by ksymoops to do better
+ * debugging.
+ */
+static void add_ksymoops_symbols(struct obj_file *f, const char *filename,
+ const char *m_name)
+{
+ struct obj_section *sec;
+ struct obj_symbol *sym;
+ char *name, *absolute_filename;
+ char str[STRVERSIONLEN], real[PATH_MAX];
+ int i, l, lm_name, lfilename, use_ksymtab, version;
+ struct stat statbuf;
+
+ static const char *section_names[] = {
+ ".text",
+ ".rodata",
+ ".data",
+ ".bss"
+ };
+
+ if (realpath(filename, real)) {
+ absolute_filename = xstrdup(real);
+ }
+ else {
+ int save_errno = errno;
+ error("cannot get realpath for %s", filename);
+ errno = save_errno;
+ absolute_filename = xstrdup(filename);
+ }
+
+ lm_name = strlen(m_name);
+ lfilename = strlen(absolute_filename);
+
+ /* add to ksymtab if it already exists or there is no ksymtab and other symbols
+ * are not to be exported. otherwise leave ksymtab alone for now, the
+ * "export all symbols" compatibility code will export these symbols later.
+ */
+
+ use_ksymtab = obj_find_section(f, "__ksymtab") || !flag_export;
+
+ if ((sec = obj_find_section(f, ".this"))) {
+ /* tag the module header with the object name, last modified
+ * timestamp and module version. worst case for module version
+ * is 0xffffff, decimal 16777215. putting all three fields in
+ * one symbol is less readable but saves kernel space.
+ */
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_O" */
+ lfilename+ /* object filename */
+ 2+ /* "_M" */
+ 2*sizeof(statbuf.st_mtime)+ /* mtime in hex */
+ 2+ /* "_V" */
+ 8+ /* version in dec */
+ 1; /* nul */
+ name = xmalloc(l);
+ if (stat(absolute_filename, &statbuf) != 0)
+ statbuf.st_mtime = 0;
+ version = get_module_version(f, str); /* -1 if not found */
+ snprintf(name, l, "%s%s_O%s_M%0*lX_V%d",
+ symprefix, m_name, absolute_filename,
+ 2*sizeof(statbuf.st_mtime), statbuf.st_mtime,
+ version);
+ sym = obj_add_symbol(f, name, -1,
+ ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ add_ksymtab(f, sym);
+ }
+ free(absolute_filename);
+
+ /* record where the persistent data is going, same address as previous symbol */
+
+ if (f->persist) {
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_P" */
+ strlen(f->persist)+ /* data store */
+ 1; /* nul */
+ name = xmalloc(l);
+ snprintf(name, l, "%s%s_P%s",
+ symprefix, m_name, f->persist);
+ sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ add_ksymtab(f, sym);
+ }
+
+ /* tag the desired sections if size is non-zero */
+
+ for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) {
+ if ((sec = obj_find_section(f, section_names[i])) &&
+ sec->header.sh_size) {
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_S" */
+ strlen(sec->name)+ /* section name */
+ 2+ /* "_L" */
+ 8+ /* length in dec */
+ 1; /* nul */
+ name = xmalloc(l);
+ snprintf(name, l, "%s%s_S%s_L%ld",
+ symprefix, m_name, sec->name,
+ (long)sec->header.sh_size);
+ sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ add_ksymtab(f, sym);
+ }
+ }
+}
+
+static int process_module_arguments(struct obj_file *f, int argc, char **argv, int required)
+{
+ for (; argc > 0; ++argv, --argc) {
+ struct obj_symbol *sym;
+ int c;
+ int min, max;
+ int n;
+ char *contents;
+ char *input;
+ char *fmt;
+ char *key;
+ char *loc;
+
+ if ((input = strchr(*argv, '=')) == NULL)
+ continue;
+
+ n = input - *argv;
+ input += 1; /* skip '=' */
+
+ key = alloca(n + 6);
+
+ if (m_has_modinfo) {
+ memcpy(key, "parm_", 5);
+ memcpy(key + 5, *argv, n);
+ key[n + 5] = '\0';
+ if ((fmt = get_modinfo_value(f, key)) == NULL) {
+ if (required) {
+ error("invalid parameter %s", key);
+ return 0;
+ }
+ else {
+ if (flag_verbose)
+ lprintf("ignoring %s", *argv);
+ continue; /* silently ignore optional parameters */
+ }
+ }
+ key += 5;
+
+ if (isdigit(*fmt)) {
+ min = strtoul(fmt, &fmt, 10);
+ if (*fmt == '-')
+ max = strtoul(fmt + 1, &fmt, 10);
+ else
+ max = min;
+ } else
+ min = max = 1;
+ } else { /* not m_has_modinfo */
+ memcpy(key, *argv, n);
+ key[n] = '\0';
+
+ if (isdigit(*input))
+ fmt = "i";
+ else
+ fmt = "s";
+ min = max = 0;
+ }
+
+ sym = obj_find_symbol(f, key);
+
+ /*
+ * Also check that the parameter was not
+ * resolved from the kernel.
+ */
+ if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
+ error("symbol for parameter %s not found", key);
+ return 0;
+ }
+
+ contents = f->sections[sym->secidx]->contents;
+ loc = contents + sym->value;
+ n = 1;
+
+ while (*input) {
+ char *str;
+
+ switch (*fmt) {
+ case 's':
+ case 'c':
+ /*
+ * Do C quoting if we begin with a ",
+ * else slurp the lot.
+ */
+ if (*input == '"') {
+ char *r;
+
+ str = alloca(strlen(input));
+ for (r = str, input++; *input != '"'; ++input, ++r) {
+ if (*input == '\0') {
+ error("improperly terminated string argument for %s", key);
+ return 0;
+ }
+ /* else */
+ if (*input != '\\') {
+ *r = *input;
+ continue;
+ }
+ /* else handle \ */
+ switch (*++input) {
+ case 'a': *r = '\a'; break;
+ case 'b': *r = '\b'; break;
+ case 'e': *r = '\033'; break;
+ case 'f': *r = '\f'; break;
+ case 'n': *r = '\n'; break;
+ case 'r': *r = '\r'; break;
+ case 't': *r = '\t'; break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ c = *input - '0';
+ if ('0' <= input[1] && input[1] <= '7') {
+ c = (c * 8) + *++input - '0';
+ if ('0' <= input[1] && input[1] <= '7')
+ c = (c * 8) + *++input - '0';
+ }
+ *r = c;
+ break;
+
+ default: *r = *input; break;
+ }
+ }
+ *r = '\0';
+ ++input;
+ } else {
+ /*
+ * The string is not quoted.
+ * We will break it using the comma
+ * (like for ints).
+ * If the user wants to include commas
+ * in a string, he just has to quote it
+ */
+ char *r;
+
+ /* Search the next comma */
+ if ((r = strchr(input, ',')) != NULL) {
+ /*
+ * Found a comma
+ * Recopy the current field
+ */
+ str = alloca(r - input + 1);
+ memcpy(str, input, r - input);
+ str[r - input] = '\0';
+ /* Keep next fields */
+ input = r;
+ } else {
+ /* last string */
+ str = input;
+ input = "";
+ }
+ }
+
+ if (*fmt == 's') {
+ /* Normal string */
+ obj_string_patch(f, sym->secidx, loc - contents, str);
+ loc += tgt_sizeof_char_p;
+ } else {
+ /* Array of chars (in fact, matrix !) */
+ long charssize; /* size of each member */
+
+ /* Get the size of each member */
+ /* Probably we should do that outside the loop ? */
+ if (!isdigit(*(fmt + 1))) {
+ error("parameter type 'c' for %s must be followed by"
+ " the maximum size", key);
+ return 0;
+ }
+ charssize = strtoul(fmt + 1, (char **) NULL, 10);
+
+ /* Check length */
+ if (strlen(str) >= charssize-1) {
+ error("string too long for %s (max %ld)",
+ key, charssize - 1);
+ return 0;
+ }
+ /* Copy to location */
+ strcpy((char *) loc, str); /* safe, see check above */
+ loc += charssize;
+ }
+ /*
+ * End of 's' and 'c'
+ */
+ break;
+
+ case 'b':
+ *loc++ = strtoul(input, &input, 0);
+ break;
+
+ case 'h':
+ *(short *) loc = strtoul(input, &input, 0);
+ loc += tgt_sizeof_short;
+ break;
+
+ case 'i':
+ *(int *) loc = strtoul(input, &input, 0);
+ loc += tgt_sizeof_int;
+ break;
+
+ case 'l':
+ *(long *) loc = strtoul(input, &input, 0);
+ loc += tgt_sizeof_long;
+ break;
+
+ default:
+ error("unknown parameter type '%c' for %s",
+ *fmt, key);
+ return 0;
+ }
+ /*
+ * end of switch (*fmt)
+ */
+
+ while (*input && isspace(*input))
+ ++input;
+ if (*input == '\0')
+ break; /* while (*input) */
+ /* else */
+
+ if (*input == ',') {
+ if (max && (++n > max)) {
+ error("too many values for %s (max %d)", key, max);
+ return 0;
+ }
+ ++input;
+ /* continue with while (*input) */
+ } else {
+ error("invalid argument syntax for %s: '%c'",
+ key, *input);
+ return 0;
+ }
+ } /* end of while (*input) */
+
+ if (min && (n < min)) {
+ error("too few values for %s (min %d)", key, min);
+ return 0;
+ }
+ } /* end of for (;argc > 0;) */
+
+ return 1;
+}
+
+
+/* Add a kallsyms section if the kernel supports all symbols. */
+static int add_kallsyms(struct obj_file *f,
+ struct obj_section **module_kallsyms, int force_kallsyms)
+{
+ struct module_symbol *s;
+ struct obj_file *f_kallsyms;
+ struct obj_section *sec_kallsyms;
+ size_t i;
+ int l;
+ const char *p, *pt_R;
+ unsigned long start = 0, stop = 0;
+
+ for (i = 0, s = ksyms; i < nksyms; ++i, ++s) {
+ p = (char *)s->name;
+ pt_R = strstr(p, "_R");
+ if (pt_R)
+ l = pt_R - p;
+ else
+ l = strlen(p);
+ if (strncmp(p, "__start_" KALLSYMS_SEC_NAME, l) == 0)
+ start = s->value;
+ else if (strncmp(p, "__stop_" KALLSYMS_SEC_NAME, l) == 0)
+ stop = s->value;
+ }
+
+ if (start >= stop && !force_kallsyms)
+ return(0);
+
+ /* The kernel contains all symbols, do the same for this module. */
+
+ /* Add an empty kallsyms section to the module if necessary */
+ for (i = 0; i < f->header.e_shnum; ++i) {
+ if (strcmp(f->sections[i]->name, KALLSYMS_SEC_NAME) == 0) {
+ *module_kallsyms = f->sections[i];
+ break;
+ }
+ }
+ if (!*module_kallsyms)
+ *module_kallsyms = obj_create_alloced_section(f, KALLSYMS_SEC_NAME, 0, 0);
+
+ /* Size and populate kallsyms */
+ if (obj_kallsyms(f, &f_kallsyms))
+ return(1);
+ sec_kallsyms = f_kallsyms->sections[KALLSYMS_IDX];
+ (*module_kallsyms)->header.sh_addralign = sec_kallsyms->header.sh_addralign;
+ (*module_kallsyms)->header.sh_size = sec_kallsyms->header.sh_size;
+ free((*module_kallsyms)->contents);
+ (*module_kallsyms)->contents = sec_kallsyms->contents;
+ sec_kallsyms->contents = NULL;
+ obj_free(f_kallsyms);
+
+ return 0;
+}
+
+
+/* Add an arch data section if the arch wants it. */
+static int add_archdata(struct obj_file *f,
+ struct obj_section **sec)
+{
+ size_t i;
+
+ *sec = NULL;
+ /* Add an empty archdata section to the module if necessary */
+ for (i = 0; i < f->header.e_shnum; ++i) {
+ if (strcmp(f->sections[i]->name, ARCHDATA_SEC_NAME) == 0) {
+ *sec = f->sections[i];
+ break;
+ }
+ }
+ if (!*sec)
+ *sec = obj_create_alloced_section(f, ARCHDATA_SEC_NAME, 16, 0);
+
+ /* Size and populate archdata */
+ if (arch_archdata(f, *sec))
+ return(1);
+ return 0;
+}
+
+
+static int init_module(const char *m_name, struct obj_file *f,
+ unsigned long m_size, const char *blob_name,
+ unsigned int noload, unsigned int flag_load_map)
+{
+ struct module *module;
+ struct obj_section *sec;
+ void *image;
+ int ret = 0;
+ tgt_long m_addr;
+
+ sec = obj_find_section(f, ".this");
+ module = (struct module *) sec->contents;
+ m_addr = sec->header.sh_addr;
+
+ module->size_of_struct = sizeof(*module);
+ module->size = m_size;
+ module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
+
+ sec = obj_find_section(f, "__ksymtab");
+ if (sec && sec->header.sh_size) {
+ module->syms = sec->header.sh_addr;
+ module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
+ }
+ if (n_ext_modules_used) {
+ sec = obj_find_section(f, ".kmodtab");
+ module->deps = sec->header.sh_addr;
+ module->ndeps = n_ext_modules_used;
+ }
+ module->init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
+ module->cleanup = obj_symbol_final_value(f,
+ obj_find_symbol(f, "cleanup_module"));
+
+ sec = obj_find_section(f, "__ex_table");
+ if (sec) {
+ module->ex_table_start = sec->header.sh_addr;
+ module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
+ }
+ sec = obj_find_section(f, ".text.init");
+ if (sec) {
+ module->runsize = sec->header.sh_addr - m_addr;
+ }
+ sec = obj_find_section(f, ".data.init");
+ if (sec) {
+ if (!module->runsize ||
+ module->runsize > sec->header.sh_addr - m_addr)
+ module->runsize = sec->header.sh_addr - m_addr;
+ }
+ sec = obj_find_section(f, ARCHDATA_SEC_NAME);
+ if (sec && sec->header.sh_size) {
+ module->archdata_start = sec->header.sh_addr;
+ module->archdata_end = module->archdata_start + sec->header.sh_size;
+ }
+ sec = obj_find_section(f, KALLSYMS_SEC_NAME);
+ if (sec && sec->header.sh_size) {
+ module->kallsyms_start = sec->header.sh_addr;
+ module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
+ }
+ if (!arch_init_module(f, module))
+ return 0;
+
+ /*
+ * Whew! All of the initialization is complete.
+ * Collect the final module image and give it to the kernel.
+ */
+ image = xmalloc(m_size);
+ obj_create_image(f, image);
+
+ if (flag_load_map)
+ print_load_map(f);
+
+ if (blob_name) {
+ int fd, l;
+ fd = open(blob_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (fd < 0) {
+ error("open %s failed %m", blob_name);
+ ret = -1;
+ }
+ else {
+ if ((l = write(fd, image, m_size)) != m_size) {
+ error("write %s failed %m", blob_name);
+ ret = -1;
+ }
+ close(fd);
+ }
+ }
+
+ if (ret == 0 && !noload) {
+ fflush(stdout); /* Flush any debugging output */
+ ret = sys_init_module(m_name, (struct module *) image);
+ if (ret) {
+ error("init_module: %m");
+ lprintf("Hint: insmod errors can be caused by incorrect module parameters, "
+ "including invalid IO or IRQ parameters");
+ }
+ }
+
+ free(image);
+
+ return ret == 0;
+}
+
+#ifdef COMPAT_2_0
+static int old_init_module(const char *m_name, struct obj_file *f,
+ unsigned long m_size)
+{
+ char *image;
+ struct old_mod_routines routines;
+ struct old_symbol_table *symtab;
+ int ret;
+ int nsyms = 0, strsize = 0, total;
+
+ /* Create the symbol table */
+ /* Size things first... */
+ if (flag_export) {
+ int i;
+ for (i = 0; i < HASH_BUCKETS; ++i) {
+ struct obj_symbol *sym;
+
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (ELFW(ST_BIND) (sym->info) != STB_LOCAL &&
+ sym->secidx <= SHN_HIRESERVE) {
+ sym->ksymidx = nsyms++;
+ strsize += strlen(sym->name) + 1;
+ }
+ }
+ }
+ total = (sizeof(struct old_symbol_table) +
+ nsyms * sizeof(struct old_module_symbol) +
+ n_ext_modules_used * sizeof(struct old_module_ref) +
+ strsize);
+ symtab = xmalloc(total);
+ symtab->size = total;
+ symtab->n_symbols = nsyms;
+ symtab->n_refs = n_ext_modules_used;
+
+ if (flag_export && nsyms) {
+ struct old_module_symbol *ksym;
+ char *str;
+ int i;
+
+ ksym = symtab->symbol;
+ str = ((char *) ksym +
+ nsyms * sizeof(struct old_module_symbol) +
+ n_ext_modules_used * sizeof(struct old_module_ref));
+
+ for (i = 0; i < HASH_BUCKETS; ++i) {
+ struct obj_symbol *sym;
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->ksymidx >= 0) {
+ ksym->addr = obj_symbol_final_value(f, sym);
+ ksym->name = (unsigned long) str - (unsigned long) symtab;
+
+ str = stpcpy(str, sym->name) + 1;
+ ksym++;
+ }
+ }
+ }
+
+ if (n_ext_modules_used) {
+ struct old_module_ref *ref;
+ int i;
+
+ ref = (struct old_module_ref *)
+ ((char *) symtab->symbol + nsyms * sizeof(struct old_module_symbol));
+
+ for (i = 0; i < n_module_stat; ++i) {
+ if (module_stat[i].status /* used */) {
+ ref++->module = module_stat[i].modstruct;
+ }
+ }
+ }
+
+ /* Fill in routines. */
+
+ routines.init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
+ routines.cleanup = obj_symbol_final_value(f,
+ obj_find_symbol(f, "cleanup_module"));
+
+ /*
+ * Whew! All of the initialization is complete.
+ * Collect the final module image and give it to the kernel.
+ */
+ image = xmalloc(m_size);
+ obj_create_image(f, image);
+
+ /*
+ * image holds the complete relocated module,
+ * accounting correctly for mod_use_count.
+ * However the old module kernel support assume that it
+ * is receiving something which does not contain mod_use_count.
+ */
+ ret = old_sys_init_module(m_name, image + sizeof(long),
+ (m_size - sizeof(long)) |
+ (flag_autoclean ? OLD_MOD_AUTOCLEAN : 0),
+ &routines,
+ symtab);
+ if (ret)
+ error("init_module: %m");
+
+ free(image);
+ free(symtab);
+
+ return ret == 0;
+}
+#endif
+/* end compat */
+/************************************************************************/
+
+/* Check that a module parameter has a reasonable definition */
+static int check_module_parameter(struct obj_file *f, char *key, char *value, int *persist_flag)
+{
+ struct obj_symbol *sym;
+ int min, max;
+ char *p = value;
+
+ sym = obj_find_symbol(f, key);
+ if (sym == NULL) {
+ /* FIXME: For 2.2 kernel compatibility, only issue warnings for
+ * most error conditions. Make these all errors in 2.5.
+ */
+ lprintf("Warning: %s symbol for parameter %s not found", error_file, key);
+ return(1);
+ }
+
+ if (isdigit(*p)) {
+ min = strtoul(p, &p, 10);
+ if (*p == '-')
+ max = strtoul(p + 1, &p, 10);
+ else
+ max = min;
+ } else
+ min = max = 1;
+
+ if (max < min) {
+ lprintf("Warning: %s parameter %s has max < min!", error_file, key);
+ return(1);
+ }
+
+ switch (*p) {
+ case 'c':
+ if (!isdigit(p[1])) {
+ lprintf("%s parameter %s has no size after 'c'!", error_file, key);
+ return(1);
+ }
+ while (isdigit(p[1]))
+ ++p; /* swallow c array size */
+ break;
+ case 'b': /* drop through */
+ case 'h': /* drop through */
+ case 'i': /* drop through */
+ case 'l': /* drop through */
+ case 's':
+ break;
+ case '\0':
+ lprintf("%s parameter %s has no format character!", error_file, key);
+ return(1);
+ default:
+ lprintf("%s parameter %s has unknown format character '%c'", error_file, key, *p);
+ return(1);
+ }
+ switch (*++p) {
+ case 'p':
+ if (*(p-1) == 's') {
+ error("parameter %s is invalid persistent string", key);
+ return(1);
+ }
+ *persist_flag = 1;
+ break;
+ case '\0':
+ break;
+ default:
+ lprintf("%s parameter %s has unknown format modifier '%c'", error_file, key, *p);
+ return(1);
+ }
+ return(0);
+}
+
+/* Check that all module parameters have reasonable definitions */
+static void check_module_parameters(struct obj_file *f, int *persist_flag)
+{
+ struct obj_section *sec;
+ char *ptr, *value, *n, *endptr;
+ int namelen, err = 0;
+
+ sec = obj_find_section(f, ".modinfo");
+ if (sec == NULL) {
+ /* module does not support typed parameters */
+ return;
+ }
+
+ ptr = sec->contents;
+ endptr = ptr + sec->header.sh_size;
+ while (ptr < endptr && !err) {
+ value = strchr(ptr, '=');
+ n = strchr(ptr, '\0');
+ if (value) {
+ namelen = value - ptr;
+ if (namelen >= 5 && strncmp(ptr, "parm_", 5) == 0
+ && !(namelen > 10 && strncmp(ptr, "parm_desc_", 10) == 0)) {
+ char *pname = xmalloc(namelen + 1);
+ strncpy(pname, ptr + 5, namelen - 5);
+ pname[namelen - 5] = '\0';
+ err = check_module_parameter(f, pname, value+1, persist_flag);
+ free(pname);
+ }
+ } else {
+ if (n - ptr >= 5 && strncmp(ptr, "parm_", 5) == 0) {
+ error("parameter %s found with no value", ptr);
+ err = 1;
+ }
+ }
+ ptr = n + 1;
+ }
+
+ if (err)
+ *persist_flag = 0;
+ return;
+}
+
+
+/* For common 3264 code, only compile the usage message once, in the 64 bit version */
+#if defined(COMMON_3264) && defined(ONLY_32)
+extern void insmod_usage(void); /* Use the copy in the 64 bit version */
+#else /* Common 64 bit version or any non common code - compile usage routine */
+void insmod_usage(void)
+{
+ fputs("Usage:\n"
+ "insmod [-fhkLmnpqrsSvVxXyY] [-e persist_name] [-o module_name] [-O blob_name] [-P prefix] module [ symbol=value ... ]\n"
+ "\n"
+ " module Name of a loadable kernel module ('.o' can be omitted)\n"
+ " -f, --force Force loading under wrong kernel version\n"
+ " -h, --help Print this message\n"
+ " -k, --autoclean Make module autoclean-able\n"
+ " -L, --lock Prevent simultaneous loads of the same module\n"
+ " -m, --map Generate load map (so crashes can be traced)\n"
+ " -n, --noload Don't load, just show\n"
+ " -p, --probe Probe mode; check if the module matches the kernel\n"
+ " -q, --quiet Don't print unresolved symbols\n"
+ " -r, --root Allow root to load modules not owned by root\n"
+ " -s, --syslog Report errors via syslog\n"
+ " -S, --kallsyms Force kallsyms on module\n"
+ " -v, --verbose Verbose output\n"
+ " -V, --version Show version\n"
+ " -x, --noexport Do not export externs\n"
+ " -X, --export Do export externs (default)\n"
+ " -y, --noksymoops Do not add ksymoops symbols\n"
+ " -Y, --ksymoops Do add ksymoops symbols (default)\n"
+ " -e persist_name\n"
+ " --persist=persist_name Filename to hold any persistent data from the module\n"
+ " -o NAME, --name=NAME Set internal module name to NAME\n"
+ " -O NAME, --blob=NAME Save the object as a binary blob in NAME\n"
+ " -P PREFIX\n"
+ " --prefix=PREFIX Prefix for kernel or module symbols\n"
+ ,stderr);
+ exit(1);
+}
+#endif /* defined(COMMON_3264) && defined(ONLY_32) */
+
+#if defined(COMMON_3264) && defined(ONLY_32)
+#define INSMOD_MAIN insmod_main_32 /* 32 bit version */
+#elif defined(COMMON_3264) && defined(ONLY_64)
+#define INSMOD_MAIN insmod_main_64 /* 64 bit version */
+#else
+#define INSMOD_MAIN insmod_main /* Not common code */
+#endif
+
+int INSMOD_MAIN(int argc, char **argv)
+{
+ int k_version;
+ int k_crcs;
+ char k_strversion[STRVERSIONLEN];
+ struct option long_opts[] = {
+ {"force", 0, 0, 'f'},
+ {"help", 0, 0, 'h'},
+ {"autoclean", 0, 0, 'k'},
+ {"lock", 0, 0, 'L'},
+ {"map", 0, 0, 'm'},
+ {"noload", 0, 0, 'n'},
+ {"probe", 0, 0, 'p'},
+ {"poll", 0, 0, 'p'}, /* poll is deprecated, remove in 2.5 */
+ {"quiet", 0, 0, 'q'},
+ {"root", 0, 0, 'r'},
+ {"syslog", 0, 0, 's'},
+ {"kallsyms", 0, 0, 'S'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {"noexport", 0, 0, 'x'},
+ {"export", 0, 0, 'X'},
+ {"noksymoops", 0, 0, 'y'},
+ {"ksymoops", 0, 0, 'Y'},
+
+ {"persist", 1, 0, 'e'},
+ {"name", 1, 0, 'o'},
+ {"blob", 1, 0, 'O'},
+ {"prefix", 1, 0, 'P'},
+ {0, 0, 0, 0}
+ };
+ char *m_name = NULL;
+ char *blob_name = NULL; /* Save object as binary blob */
+ int m_version;
+ ElfW(Addr) m_addr;
+ unsigned long m_size;
+ int m_crcs;
+ char m_strversion[STRVERSIONLEN];
+ char *filename;
+ char *persist_name = NULL; /* filename to hold any persistent data */
+ int fp;
+ struct obj_file *f;
+ struct obj_section *kallsyms = NULL, *archdata = NULL;
+ int o;
+ int noload = 0;
+ int dolock = 1; /*Note: was: 0; */
+ int quiet = 0;
+ int exit_status = 1;
+ int force_kallsyms = 0;
+ int persist_parms = 0; /* does module have persistent parms? */
+ int i;
+
+ error_file = "insmod";
+
+ /* To handle repeated calls from combined modprobe */
+ errors = optind = 0;
+
+ /* Process the command line. */
+ while ((o = getopt_long(argc, argv, "fhkLmnpqrsSvVxXyYe:o:O:P:R:",
+ &long_opts[0], NULL)) != EOF)
+ switch (o) {
+ case 'f': /* force loading */
+ flag_force_load = 1;
+ break;
+ case 'h': /* Print the usage message. */
+ insmod_usage();
+ break;
+ case 'k': /* module loaded by kerneld, auto-cleanable */
+ flag_autoclean = 1;
+ break;
+ case 'L': /* protect against recursion. */
+ dolock = 1;
+ break;
+ case 'm': /* generate load map */
+ flag_load_map = 1;
+ break;
+ case 'n': /* don't load, just check */
+ noload = 1;
+ break;
+ case 'p': /* silent probe mode */
+ flag_silent_probe = 1;
+ break;
+ case 'q': /* Don't print unresolved symbols */
+ quiet = 1;
+ break;
+ case 'r': /* allow root to load non-root modules */
+ root_check_off = !root_check_off;
+ break;
+ case 's': /* start syslog */
+ setsyslog("insmod");
+ break;
+ case 'S': /* Force kallsyms */
+ force_kallsyms = 1;
+ break;
+ case 'v': /* verbose output */
+ flag_verbose = 1;
+ break;
+ case 'V':
+ fputs("insmod version " MODUTILS_VERSION "\n", stderr);
+ break;
+ case 'x': /* do not export externs */
+ flag_export = 0;
+ break;
+ case 'X': /* do export externs */
+ flag_export = 1;
+ break;
+ case 'y': /* do not define ksymoops symbols */
+ flag_ksymoops = 0;
+ break;
+ case 'Y': /* do define ksymoops symbols */
+ flag_ksymoops = 1;
+ break;
+
+ case 'e': /* persistent data filename */
+ free(persist_name);
+ persist_name = xstrdup(optarg);
+ break;
+ case 'o': /* name the output module */
+ m_name = optarg;
+ break;
+ case 'O': /* save the output module object */
+ blob_name = optarg;
+ break;
+ case 'P': /* use prefix on crc */
+ set_ncv_prefix(optarg);
+ break;
+
+ default:
+ insmod_usage();
+ break;
+ }
+
+ if (optind >= argc) {
+ insmod_usage();
+ }
+ filename = argv[optind++];
+
+ if (config_read(0, NULL, "", NULL) < 0) {
+ error("Failed handle configuration");
+ }
+
+ if (persist_name && !*persist_name &&
+ (!persistdir || !*persistdir)) {
+ free(persist_name);
+ persist_name = NULL;
+ if (flag_verbose)
+ lprintf("insmod: -e \"\" ignored, no persistdir");
+ }
+
+ if (m_name == NULL) {
+ size_t len;
+ char *p;
+
+ if ((p = strrchr(filename, '/')) != NULL)
+ p++;
+ else
+ p = filename;
+ len = strlen(p);
+ if (len > 2 && p[len - 2] == '.' && p[len - 1] == 'o')
+ len -= 2;
+ else if (len > 4 && p[len - 4] == '.' && p[len - 3] == 'm'
+ && p[len - 2] == 'o' && p[len - 1] == 'd')
+ len -= 4;
+#ifdef CONFIG_USE_ZLIB
+ else if (len > 5 && !strcmp(p + len - 5, ".o.gz"))
+ len -= 5;
+#endif
+
+ m_name = xmalloc(len + 1);
+ memcpy(m_name, p, len);
+ m_name[len] = '\0';
+ }
+
+ /* Locate the file to be loaded. */
+ if (!strchr(filename, '/') && !strchr(filename, '.')) {
+ char *tmp = search_module_path(filename);
+ if (tmp == NULL) {
+ error("%s: no module by that name found", filename);
+ return 1;
+ }
+ filename = tmp;
+ lprintf("Using %s", filename);
+ } else if (flag_verbose)
+ lprintf("Using %s", filename);
+
+ /* And open it. */
+ if ((fp = gzf_open(filename, O_RDONLY)) == -1) {
+ error("%s: %m", filename);
+ return 1;
+ }
+ /* Try to prevent multiple simultaneous loads. */
+ if (dolock)
+ flock(fp, LOCK_EX);
+
+ if (!get_kernel_info(K_SYMBOLS))
+ goto out;
+
+ /*
+ * Set the genksyms prefix if this is a versioned kernel
+ * and it's not already set.
+ */
+ set_ncv_prefix(NULL);
+
+ for (i = 0; i < n_module_stat; ++i) {
+ if (strcmp(module_stat[i].name, m_name) == 0) {
+ error("a module named %s already exists", m_name);
+ goto out;
+ }
+ }
+
+ error_file = filename;
+ if ((f = obj_load(fp, ET_REL, filename)) == NULL)
+ goto out;
+
+ /* Version correspondence? */
+ k_version = get_kernel_version(k_strversion);
+ m_version = get_module_version(f, m_strversion);
+ if (m_version == -1) {
+ error("couldn't find the kernel version the module was compiled for");
+ goto out;
+ }
+
+ k_crcs = is_kernel_checksummed();
+ m_crcs = is_module_checksummed(f);
+ if ((m_crcs == 0 || k_crcs == 0) &&
+ strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) {
+ if (flag_force_load) {
+ lprintf("Warning: kernel-module version mismatch\n"
+ "\t%s was compiled for kernel version %s\n"
+ "\twhile this kernel is version %s",
+ filename, m_strversion, k_strversion);
+ } else {
+ if (!quiet)
+ error("kernel-module version mismatch\n"
+ "\t%s was compiled for kernel version %s\n"
+ "\twhile this kernel is version %s.",
+ filename, m_strversion, k_strversion);
+ goto out;
+ }
+ }
+ if (m_crcs != k_crcs)
+ obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
+
+ /* Let the module know about the kernel symbols. */
+ add_kernel_symbols(f);
+
+ /* Allocate common symbols, symbol tables, and string tables.
+ *
+ * The calls marked DEPMOD indicate the bits of code that depmod
+ * uses to do a pseudo relocation, ignoring undefined symbols.
+ * Any changes made to the relocation sequence here should be
+ * checked against depmod.
+ */
+#ifdef COMPAT_2_0
+ if (k_new_syscalls
+ ? !create_this_module(f, m_name)
+ : !old_create_mod_use_count(f))
+ goto out;
+#else
+ if (!create_this_module(f, m_name))
+ goto out;
+#endif
+
+ if (!obj_check_undefineds(f, quiet)) /* DEPMOD, obj_clear_undefineds */
+ goto out;
+ obj_allocate_commons(f); /* DEPMOD */
+
+ check_module_parameters(f, &persist_parms);
+
+ if (optind < argc) {
+ if (!process_module_arguments(f, argc - optind, argv + optind, 1))
+ goto out;
+ }
+ arch_create_got(f); /* DEPMOD */
+ hide_special_symbols(f);
+
+ if (persist_parms && persist_name && *persist_name) {
+ f->persist = persist_name;
+ persist_name = NULL;
+ }
+
+ if (persist_parms &&
+ persist_name && !*persist_name) {
+ /* -e "". This is ugly. Take the filename, compare it against
+ * each of the module paths until we find a match on the start
+ * of the filename, assume the rest is the relative path. Have
+ * to do it this way because modprobe uses absolute filenames
+ * for module names in modules.dep and the format of modules.dep
+ * does not allow for any backwards compatible changes, so there
+ * is nowhere to store the relative filename. The only way this
+ * should fail to calculate a relative path is "insmod ./xxx", for
+ * that case the user has to specify -e filename.
+ */
+ int j, l = strlen(filename);
+ char *relative = NULL;
+ char *p;
+ for (i = 0; i < nmodpath; ++i) {
+ p = modpath[i].path;
+ j = strlen(p);
+ while (j && p[j] == '/')
+ --j;
+ if (j < l && strncmp(filename, p, j) == 0 && filename[j] == '/') {
+ while (filename[j] == '/')
+ ++j;
+ relative = xstrdup(filename+j);
+ break;
+ }
+ }
+ if (relative) {
+ i = strlen(relative);
+ if (i > 3 && strcmp(relative+i-3, ".gz") == 0)
+ relative[i -= 3] = '\0';
+ if (i > 2 && strcmp(relative+i-2, ".o") == 0)
+ relative[i -= 2] = '\0';
+ else if (i > 4 && strcmp(relative+i-4, ".mod") == 0)
+ relative[i -= 4] = '\0';
+ f->persist = xmalloc(strlen(persistdir) + 1 + i + 1);
+ strcpy(f->persist, persistdir); /* safe, xmalloc */
+ strcat(f->persist, "/"); /* safe, xmalloc */
+ strcat(f->persist, relative); /* safe, xmalloc */
+ free(relative);
+ }
+ else
+ error("Cannot calculate persistent filename");
+ }
+
+ if (f->persist && *(f->persist) != '/') {
+ error("Persistent filenames must be absolute, ignoring '%s'",
+ f->persist);
+ free(f->persist);
+ f->persist = NULL;
+ }
+
+ if (f->persist && !flag_ksymoops) {
+ error("has persistent data but ksymoops symbols are not available");
+ free(f->persist);
+ f->persist = NULL;
+ }
+
+ if (f->persist && !k_new_syscalls) {
+ error("has persistent data but the kernel is too old to support it");
+ free(f->persist);
+ f->persist = NULL;
+ }
+
+ if (persist_parms && flag_verbose) {
+ if (f->persist)
+ lprintf("Persist filename '%s'", f->persist);
+ else
+ lprintf("No persistent filename available");
+ }
+
+ if (f->persist) {
+ FILE *fp = fopen(f->persist, "r");
+ if (!fp) {
+ if (flag_verbose)
+ lprintf("Cannot open persist file '%s' %m", f->persist);
+ }
+ else {
+ int pargc = 0;
+ char *pargv[1000]; /* hard coded but big enough */
+ char line[3000]; /* hard coded but big enough */
+ char *p;
+ while (fgets(line, sizeof(line), fp)) {
+ p = strchr(line, '\n');
+ if (!p) {
+ error("Persistent data line is too long\n%s", line);
+ break;
+ }
+ *p = '\0';
+ p = line;
+ while (isspace(*p))
+ ++p;
+ if (!*p || *p == '#')
+ continue;
+ if (pargc == sizeof(pargv)/sizeof(pargv[0])) {
+ error("More than %d persistent parameters", pargc);
+ break;
+ }
+ pargv[pargc++] = xstrdup(p);
+ }
+ fclose(fp);
+ if (!process_module_arguments(f, pargc, pargv, 0))
+ goto out;
+ while (pargc--)
+ free(pargv[pargc]);
+ }
+ }
+
+ if (flag_ksymoops)
+ add_ksymoops_symbols(f, filename, m_name);
+
+ if (k_new_syscalls)
+ create_module_ksymtab(f);
+
+ /* archdata based on relocatable addresses */
+ if (add_archdata(f, &archdata))
+ goto out;
+
+ /* kallsyms based on relocatable addresses */
+ if (add_kallsyms(f, &kallsyms, force_kallsyms))
+ goto out;
+ /**** No symbols or sections to be changed after kallsyms above ***/
+
+ if (errors)
+ goto out;
+
+ /* If we were just checking, we made it. */
+ if (flag_silent_probe) {
+ exit_status = 0;
+ goto out;
+ }
+ /* Module has now finished growing; find its size and install it. */
+ m_size = obj_load_size(f); /* DEPMOD */
+
+ if (noload) {
+ /* Don't bother actually touching the kernel. */
+ m_addr = 0x12340000;
+ } else {
+ errno = 0;
+ m_addr = create_module(m_name, m_size);
+ switch (errno) {
+ case 0:
+ break;
+ case EEXIST:
+ if (dolock) {
+ /*
+ * Assume that we were just invoked
+ * simultaneous with another insmod
+ * and return success.
+ */
+ exit_status = 0;
+ goto out;
+ }
+ error("a module named %s already exists", m_name);
+ goto out;
+ case ENOMEM:
+ error("can't allocate kernel memory for module; needed %lu bytes",
+ m_size);
+ goto out;
+ default:
+ error("create_module: %m");
+ goto out;
+ }
+ }
+
+ /* module is already built, complete with ksymoops symbols for the
+ * persistent filename. If the kernel does not support persistent data
+ * then give an error but continue. It is too difficult to clean up at
+ * this stage and this error will only occur on backported modules.
+ * rmmod will also get an error so warn the user now.
+ */
+ if (f->persist && !noload) {
+ struct {
+ struct module m;
+ int data;
+ } test_read;
+ memset(&test_read, 0, sizeof(test_read));
+ test_read.m.size_of_struct = -sizeof(test_read.m); /* -ve size => read, not write */
+ test_read.m.read_start = m_addr + sizeof(struct module);
+ test_read.m.read_end = test_read.m.read_start + sizeof(test_read.data);
+ if (sys_init_module(m_name, (struct module *) &test_read)) {
+ int old_errors = errors;
+ error("has persistent data but the kernel is too old to support it."
+ " Expect errors during rmmod as well");
+ errors = old_errors;
+ }
+ }
+
+ if (!obj_relocate(f, m_addr)) { /* DEPMOD */
+ if (!noload)
+ delete_module(m_name);
+ goto out;
+ }
+
+ /* Do archdata again, this time we have the final addresses */
+ if (add_archdata(f, &archdata))
+ goto out;
+
+ /* Do kallsyms again, this time we have the final addresses */
+ if (add_kallsyms(f, &kallsyms, force_kallsyms))
+ goto out;
+
+#ifdef COMPAT_2_0
+ if (k_new_syscalls)
+ init_module(m_name, f, m_size, blob_name, noload, flag_load_map);
+ else if (!noload)
+ old_init_module(m_name, f, m_size);
+#else
+ init_module(m_name, f, m_size, blob_name, noload, flag_load_map);
+#endif
+ if (errors) {
+ if (!noload)
+ delete_module(m_name);
+ goto out;
+ }
+ exit_status = 0;
+
+ out:
+ if (dolock)
+ flock(fp, LOCK_UN);
+ close(fp);
+ if (!noload)
+ snap_shot(NULL, 0);
+
+ return exit_status;
+}
+
+/* For common 3264 code, add an overall insmod_main, in the 64 bit version. */
+#if defined(COMMON_3264) && defined(ONLY_64)
+int insmod_main(int argc, char **argv)
+{
+ if (arch64())
+ return insmod_main_64(argc, argv);
+ else
+ return insmod_main_32(argc, argv);
+}
+#endif /* defined(COMMON_3264) && defined(ONLY_64) */
+
+
+
+int insmod_call(char * full_filename, char * params)
+{
+ int argc = 2;
+ char *argv[50];
+ char * ptr = params;
+ argv[0] = "stage1";
+ argv[1] = full_filename;
+
+ while (ptr != NULL) {
+ argv[argc] = ptr;
+ argc++;
+ ptr = strchr(ptr, ' ');
+ if (ptr) {
+ ptr[0] = '\0';
+ ptr++;
+ }
+ }
+
+ return insmod_main(argc, argv);
+}