From 31d44a623579fbca300f20bc751c7278c4375980 Mon Sep 17 00:00:00 2001 From: Guillaume Cottenceau Date: Thu, 22 Feb 2001 17:21:43 +0000 Subject: use modutils for non Intel arch's --- mdk-stage1/insmod-modutils/insmod.c | 1964 +++++++++++++++++++++++++++++++++++ 1 file changed, 1964 insertions(+) create mode 100644 mdk-stage1/insmod-modutils/insmod.c (limited to 'mdk-stage1/insmod-modutils/insmod.c') 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 + Based on original work by Bjorn Ekwall + Restructured (and partly rewritten) by: + Björn Ekwall 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 + + Merged modprobe + many fixes: Björn Ekwall February 1999 + SMP "friendliness" (and -P): Bill Zumach + + Ksymoops support: Keith Owens August 1999. + + Add -r flag: Keith Owens October 1999. + + More flexible recognition of the way the utility was called. + Suggested by Stepan Kasal, implemented in a different way by Keith + Owens December 1999. + + Rationalize common code for 32/64 bit architectures. + Keith Owens December 1999. + Add arch64(). + Keith Owens December 1999. + kallsyms support + Keith Owens April 2000. + archdata support + Keith Owens August 2000. + Add insmod -O, move print map before sys_init_module. + Keith Owens October 2000. + Add insmod -S. + Keith Owens November 2000. + Add persistent data support. + Keith Owens November 2000. + */ + +#ident "$Id$" + +#include "../insmod.h" +#include +#include +#include +#include +//#include provided by stdlib +#include +#include +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.2.1