modutils/*: rewrite by Timo Teras <timo.teras AT iki.fi>
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 13 Sep 2008 14:59:38 +0000 (14:59 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 13 Sep 2008 14:59:38 +0000 (14:59 -0000)
- a lot faster (linear algorithmic complexity, smaller memory foot print)
- a lot smaller (the old code was overly complicated)
- loading of aliases is now module-init-tools compliant
- blacklisting is done correctly (-b option added)
- module argument quoting done right
- depmod now correctly generates modules.symbols and modules.alias

add/remove: 16/21 grow/shrink: 4/6 up/down: 6930/-9316 Total: -2386 bytes
   text    data     bss     dec     hex filename
 806039     592    6680  813311   c68ff busybox_old
 803498     592    6676  810766   c5f0e busybox_unstripped

12 files changed:
include/usage.h
libbb/llist.c
modutils/Config.in
modutils/Kbuild
modutils/depmod.c
modutils/insmod.c
modutils/lsmod.c
modutils/modprobe.c
modutils/modutils-24.c [new file with mode: 0644]
modutils/modutils.c [new file with mode: 0644]
modutils/modutils.h [new file with mode: 0644]
modutils/rmmod.c

index f4fc2e67a5c056b985693b734d32e02e3975fa54..71e9f42e86163b96cf9e48a013266424fd9c6f81 100644 (file)
        "[-knqrsv] MODULE [symbol=value...]"
 #define modprobe_full_usage "\n\n" \
        "Options:" \
+       USE_FEATURE_2_4_MODULES( \
      "\n       -k      Make module autoclean-able" \
+       ) \
      "\n       -n      Dry run" \
      "\n       -q      Quiet" \
      "\n       -r      Remove module (stacks) or do autoclean" \
      "\n       -s      Report via syslog instead of stderr" \
      "\n       -v      Verbose" \
+       USE_FEATURE_MODPROBE_BLACKLIST( \
+     "\n       -b      Apply blacklist to module names too" \
+        )
 
 #define modprobe_notes_usage \
 "modprobe can (un)load a stack of modules, passing each module options (when\n" \
index 094c6524619d26a0ffacca3d36184a240960023c..2bef4315129419d20582b5c8e796c425aa502e2a 100644 (file)
@@ -90,7 +90,6 @@ void FAST_FUNC llist_free(llist_t *elm, void (*freeit) (void *data))
        }
 }
 
-#ifdef UNUSED
 /* Reverse list order. */
 llist_t* FAST_FUNC llist_rev(llist_t *list)
 {
@@ -105,4 +104,3 @@ llist_t* FAST_FUNC llist_rev(llist_t *list)
        }
        return rev;
 }
-#endif
index a430fcac33e92a2b37c3c1c246260773f484a770..e7b839db9f27278aa5ca02a029bc31ca07c314a4 100644 (file)
@@ -5,6 +5,20 @@
 
 menu "Linux Module Utilities"
 
+config DEFAULT_MODULES_DIR
+       string "Default directory containing modules"
+       default "/lib/modules"
+       help
+         Directory that contains kernel modules.
+         Defaults to "/lib/modules"
+
+config DEFAULT_DEPMOD_FILE
+       string "Default name of modules.dep"
+       default "modules.dep"
+       help
+         Filename that contains kernel modules dependencies.
+         Defaults to "modules.dep"
+
 config MODPROBE_SMALL
        bool "Simplified modutils"
        default n
@@ -54,48 +68,83 @@ config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
          Check if the module is already loaded.
          N.B. It's racy.
 
-config DEPMOD
-       bool "depmod"
+config INSMOD
+       bool "insmod"
        default n
        depends on !MODPROBE_SMALL
        help
-         depmod generates modules.dep (FIXME: elaborate)
+         insmod is used to load specified modules in the running kernel.
 
-config FEATURE_DEPMOD_PRUNE_FANCY
-       bool "Fancy dependency pruning"
+config RMMOD
+       bool "rmmod"
        default n
-       depends on DEPMOD
+       depends on !MODPROBE_SMALL
        help
-         By default modules.dep contains all dependencies as listed by
-         the modules.
-         If you enable this option then we remove implied modules from
-         the dependencies.
-         This makes depmod somewhat bigger but generates a smaller
-         modules.dep file.
+         rmmod is used to unload specified modules from the kernel.
 
-         If unsure, say N.
+config LSMOD
+       bool "lsmod"
+       default n
+       depends on !MODPROBE_SMALL
+       help
+         lsmod is used to display a list of loaded modules.
 
-config FEATURE_DEPMOD_ALIAS
-       bool "Alias support"
+config FEATURE_LSMOD_PRETTY_2_6_OUTPUT
+       bool "Pretty output"
        default n
-       depends on DEPMOD
+       depends on LSMOD
        help
-         By default modules.dep does not contain alias information.
-         Enable this to emit aliases of the form:
+         This option makes output format of lsmod adjusted to
+         the format of module-init-tools for Linux kernel 2.6.
+         Increases size somewhat.
 
-           alias pcmcia:m*c*f03fn*pfn*pa*pb*pc*pd* parport_cs
+config MODPROBE
+       bool "modprobe"
+       default n
+       depends on !MODPROBE_SMALL
+       help
+         Handle the loading of modules, and their dependencies on a high
+         level.
 
-config INSMOD
-       bool "insmod"
+         Note that in the state, modprobe does not understand multiple
+         module options from the configuration file. See option below.
+
+config FEATURE_MODPROBE_BLACKLIST
+       bool
+       prompt "Blacklist support"
+       default n
+       depends on MODPROBE
+       help
+         Say 'y' here to enable support for the 'blacklist' command in
+         modprobe.conf. This prevents the alias resolver to resolve
+         blacklisted modules. This is useful if you want to prevent your
+         hardware autodetection scripts to load modules like evdev, frame
+         buffer drivers etc.
+
+config DEPMOD
+       bool "depmod"
        default n
        depends on !MODPROBE_SMALL
        help
-         insmod is used to load specified modules in the running kernel.
+         depmod generates modules.dep (and potentially modules.alias
+         and modules.symbols) that contain dependency information
+         for modprobe.
+
+comment "Options common to multiple modutils"
+
+config FEATURE_2_4_MODULES
+       bool "Support version 2.2/2.4 Linux kernels"
+       default n
+       depends on INSMOD || RMMOD || LSMOD
+       help
+         Support module loading for 2.2.x and 2.4.x Linux kernels.
+         This increases size considerably. Say N unless you plan
+         to run ancient kernels.
 
 config FEATURE_INSMOD_VERSION_CHECKING
-       bool "Module version checking"
+       bool "Enable module version checking"
        default n
-       depends on INSMOD && FEATURE_2_4_MODULES
+       depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE)
        help
          Support checking of versions for modules. This is used to
          ensure that the kernel and module are made for each other.
@@ -103,7 +152,7 @@ config FEATURE_INSMOD_VERSION_CHECKING
 config FEATURE_INSMOD_KSYMOOPS_SYMBOLS
        bool "Add module symbols to kernel symbol table"
        default n
-       depends on INSMOD && FEATURE_2_4_MODULES
+       depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE)
        help
          By adding module symbols to the kernel symbol table, Oops messages
          occuring within kernel modules can be properly debugged. By enabling
@@ -114,7 +163,7 @@ config FEATURE_INSMOD_KSYMOOPS_SYMBOLS
 config FEATURE_INSMOD_LOADINKMEM
        bool "In kernel memory optimization (uClinux only)"
        default n
-       depends on INSMOD && FEATURE_2_4_MODULES
+       depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE)
        help
          This is a special uClinux only memory optimization that lets insmod
          load the specified kernel module directly into kernel space, reducing
@@ -122,9 +171,9 @@ config FEATURE_INSMOD_LOADINKMEM
          being loaded into memory.
 
 config FEATURE_INSMOD_LOAD_MAP
-       bool "Enable load map (-m) option"
+       bool "Enable insmod load map (-m) option"
        default n
-       depends on INSMOD && ( FEATURE_2_4_MODULES || FEATURE_2_6_MODULES )
+       depends on FEATURE_2_4_MODULES && INSMOD
        help
          Enabling this, one would be able to get a load map
          output on stdout. This makes kernel module debugging
@@ -135,138 +184,47 @@ config FEATURE_INSMOD_LOAD_MAP
 config FEATURE_INSMOD_LOAD_MAP_FULL
        bool "Symbols in load map"
        default y
-       depends on FEATURE_INSMOD_LOAD_MAP
+       depends on FEATURE_INSMOD_LOAD_MAP && !MODPROBE_SMALL
        help
          Without this option, -m will only output section
          load map. With this option, -m will also output
          symbols load map.
 
-config RMMOD
-       bool "rmmod"
-       default n
-       depends on !MODPROBE_SMALL
-       help
-         rmmod is used to unload specified modules from the kernel.
-
-config LSMOD
-       bool "lsmod"
-       default n
-       depends on !MODPROBE_SMALL
-       help
-         lsmod is used to display a list of loaded modules.
-
-config FEATURE_LSMOD_PRETTY_2_6_OUTPUT
-       bool "Pretty output for 2.6.x Linux kernels"
-       default n
-       depends on LSMOD
-       help
-         This option makes output format of lsmod adjusted to
-         the format of module-init-tools for Linux kernel 2.6.
-
-config MODPROBE
-       bool "modprobe"
-       default n
-       depends on !MODPROBE_SMALL
-       help
-         Handle the loading of modules, and their dependencies on a high
-         level.
-
-         Note that in the state, modprobe does not understand multiple
-         module options from the configuration file. See option below.
-
-config FEATURE_MODPROBE_MULTIPLE_OPTIONS
-       bool
-       prompt "Multiple options parsing"
-       default y
-       depends on MODPROBE
-       help
-         Allow modprobe to understand more than one option to pass to
-         modules.
-
-         This is a WIP, while waiting for a common argument parsing
-         common amongst all BB applets (shell, modprobe, etc...) and
-         adds around 600 bytes on x86, 700 bytes on ARM. The code is
-         biggish and uggly, but just works.
-
-         Saying Y here is not a bad idea if you're not that short
-         on storage capacity.
-
-config FEATURE_MODPROBE_FANCY_ALIAS
-       bool
-       prompt "Fancy alias parsing"
-       default y
-       depends on MODPROBE && FEATURE_2_6_MODULES
-       help
-         Say 'y' here to enable parsing of aliases with underscore/dash
-         mismatch between module name and file name, along with bus-specific
-         aliases (such as pci:... or usb:... aliases).
-
-config FEATURE_MODPROBE_BLACKLIST
-       bool
-       prompt "Blacklist support"
-       default n
-       depends on MODPROBE && FEATURE_2_6_MODULES
-       help
-         Say 'y' here to enable support for the 'blacklist' command in
-         modprobe.conf. This prevents the alias resolver to resolve
-         blacklisted modules. This is useful if you want to prevent your
-         hardware autodetection scripts to load modules like evdev, frame
-         buffer drivers etc.
-
-comment "Options common to multiple modutils"
-       depends on INSMOD || RMMOD || MODPROBE || LSMOD || DEPMOD
-
 config FEATURE_CHECK_TAINTED_MODULE
-       # Simulate indentation
        bool "Support tainted module checking with new kernels"
        default y
-       depends on INSMOD || LSMOD
+       depends on !MODPROBE_SMALL
        help
          Support checking for tainted modules. These are usually binary
          only modules that will make the linux-kernel list ignore your
          support request.
          This option is required to support GPLONLY modules.
 
-config FEATURE_2_4_MODULES
-       # Simulate indentation
-       bool "Support version 2.2.x to 2.4.x Linux kernels"
+config FEATURE_MODUTILS_ALIAS
+       bool "Support for module.aliases file"
        default y
-       depends on INSMOD || RMMOD || MODPROBE
+       depends on DEPMOD || MODPROBE
        help
-         Support module loading for 2.2.x and 2.4.x Linux kernels.
+         Generate and parse modules.alias containing aliases for bus
+         identifiers:
+           alias pcmcia:m*c*f03fn*pfn*pa*pb*pc*pd* parport_cs
 
-         Note:
-         This is automatically enabled if 2.6 modules are not enabled.
+         and aliases for logical modules names e.g.:
+           alias padlock_aes aes
+           alias aes_i586 aes
+           alias aes_generic aes
 
-config FEATURE_2_6_MODULES
-       # Simulate indentation
-       bool "Support version 2.6.x Linux kernels"
-       default y
-       depends on INSMOD || RMMOD || MODPROBE
-       help
-         Support module loading for newer 2.6.x Linux kernels.
-
-config DEFAULT_MODULES_DIR
-       # Simulate indentation
-       string "Default directory containing modules"
-       default "/lib/modules"
-       depends on INSMOD || RMMOD || MODPROBE || MODPROBE_SMALL || DEPMOD
-       help
-         Directory that contains kernel modules.
-         Defaults to "/lib/modules"
+         Say Y if unsure.
 
-config DEFAULT_DEPMOD_FILE
-       # Simulate indentation
-       string "Default name of modules.dep"
-       default "modules.dep"
-       depends on INSMOD || RMMOD || MODPROBE || MODPROBE_SMALL || DEPMOD
+config FEATURE_MODUTILS_SYMBOLS
+       bool "Support for module.symbols file"
+       default y
+       depends on DEPMOD || MODPROBE
        help
-         Filename that contains kernel modules dependencies.
-         Defaults to "modules.dep"
+         Generate and parse modules.symbols containing aliases for
+         symbol_request() kernel calls, such as:
+           alias symbol:usb_sg_init usbcore
 
-config FEATURE_QUERY_MODULE_INTERFACE
-       bool
-       default y
-       depends on FEATURE_2_4_MODULES && !FEATURE_2_6_MODULES
+         Say Y if unsure.
 
 endmenu
index 828070114e85f726d8befe147fa344c0fb40b970..31f7cbf93a1055a95fc3d4f4c00d27828c429d35 100644 (file)
@@ -5,9 +5,10 @@
 # Licensed under the GPL v2, see the file LICENSE in this tarball.
 
 lib-y:=
-lib-$(CONFIG_DEPMOD)            += depmod.o
-lib-$(CONFIG_INSMOD)            += insmod.o
-lib-$(CONFIG_LSMOD)             += lsmod.o
-lib-$(CONFIG_MODPROBE)          += modprobe.o
-lib-$(CONFIG_MODPROBE_SMALL)    += modprobe-small.o
-lib-$(CONFIG_RMMOD)             += rmmod.o
+lib-$(CONFIG_MODPROBE_SMALL)      += modprobe-small.o
+lib-$(CONFIG_DEPMOD)              += depmod.o modutils.o
+lib-$(CONFIG_INSMOD)              += insmod.o modutils.o
+lib-$(CONFIG_LSMOD)               += lsmod.o modutils.o
+lib-$(CONFIG_MODPROBE)            += modprobe.o modutils.o
+lib-$(CONFIG_RMMOD)               += rmmod.o modutils.o
+lib-$(CONFIG_FEATURE_2_4_MODULES) += modutils-24.o
index b6a914eb051ccdf0b7c9b76912940d313200557f..35df4a8e2ade14c2d963366cd5183f555353c465 100644 (file)
@@ -2,6 +2,8 @@
 /*
  * depmod - generate modules.dep
  * Copyright (c) 2008 Bernhard Fischer
+ * Copyrihgt (c) 2008 Timo Teras <timo.teras@iki.fi>
+ * Copyright (c) 2008 Vladimir Dronnikov
  *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
@@ -10,6 +12,8 @@
 #define _GNU_SOURCE
 #include <libbb.h>
 #include <sys/utsname.h> /* uname() */
+#include "modutils.h"
+
 /*
  * Theory of operation:
  * - iterate over all modules and record their full path
  *   for each depends, look through our list of full paths and emit if found
  */
 
-typedef struct dep_lst_t {
-       char *name;
+typedef struct module_info {
+       struct module_info *next;
+       char *name, *modname;
        llist_t *dependencies;
        llist_t *aliases;
-       struct dep_lst_t *next;
-} dep_lst_t;
-
-struct globals {
-       dep_lst_t *lst; /* modules without their corresponding extension */
+       llist_t *symbols;
+       struct module_info *dnext, *dprev;
+} module_info;
+
+enum {
+       ARG_a = (1<<0), /* All modules, ignore mods in argv */
+       ARG_A = (1<<1), /* Only emit .ko that are newer than modules.dep file */
+       ARG_b = (1<<2), /* not /lib/modules/$(uname -r)/ but this base-dir */
+       ARG_e = (1<<3), /* with -F, print unresolved symbols */
+       ARG_F = (1<<4), /* System.map that contains the symbols */
+       ARG_n = (1<<5)  /* dry-run, print to stdout only */
 };
-#define G (*(struct globals*)&bb_common_bufsiz1)
-/* We have to zero it out because of NOEXEC */
-#define INIT_G() memset(&G, 0, sizeof(G))
 
-static char* find_keyword(void *the_module, size_t len, const char * const word)
+static int FAST_FUNC parse_module(const char *fname, struct stat *sb,
+                                 void *data, int UNUSED_PARAM depth)
 {
-       char *ptr = the_module;
-       do {
-               /* search for the first char in word */
-               ptr = memchr(ptr, *word, len - (ptr - (char*)the_module));
-               if (ptr == NULL) /* no occurance left, done */
-                       return NULL;
-               if (!strncmp(ptr, word, strlen(word))) {
-                       ptr += strlen(word);
-                       break;
+       module_info **first = (module_info **) data;
+       char *image, *ptr;
+       module_info *info;
+       size_t len = sb->st_size;
+
+       if (strrstr(fname, ".ko") == NULL)
+               return TRUE;
+
+       image = (char *) xmalloc_open_zipped_read_close(fname, &len);
+       info = xzalloc(sizeof(module_info));
+
+       info->next = *first;
+       *first = info;
+
+       info->dnext = info->dprev = info;
+       info->name = xstrdup(fname);
+       info->modname = filename2modname(fname, NULL);
+       for (ptr = image; ptr < image + len - 10; ptr++) {
+               if (strncmp(ptr, "depends=", 8) == 0) {
+                       char *u;
+
+                       ptr += 8;
+                       for (u = ptr; *u; u++)
+                               if (*u == '-')
+                                       *u = '_';
+                       ptr += string_to_llist(ptr, &info->dependencies, ",");
+               } else if (ENABLE_FEATURE_MODUTILS_ALIAS &&
+                          strncmp(ptr, "alias=", 6) == 0) {
+                       llist_add_to(&info->aliases, xstrdup(ptr + 6));
+                       ptr += strlen(ptr);
+               } else if (ENABLE_FEATURE_MODUTILS_SYMBOLS &&
+                          strncmp(ptr, "__ksymtab_", 10) == 0) {
+                       ptr += 10;
+                       if (strncmp(ptr, "gpl", 3) == 0 ||
+                           strcmp(ptr, "strings") == 0)
+                               continue;
+                       llist_add_to(&info->symbols, xstrdup(ptr));
+                       ptr += strlen(ptr);
                }
-               ++ptr;
-       } while (1);
-       return ptr;
+       }
+       free(image);
+
+       return TRUE;
 }
-static int FAST_FUNC fileAction(const char *fname, struct stat *sb,
-                                       void UNUSED_PARAM *data, int UNUSED_PARAM depth)
-{
-       size_t len = sb->st_size;
-       void *the_module;
-       char *ptr;
-       int fd;
-       char *depends, *deps;
-       dep_lst_t *this;
 
-       if (strrstr(fname, ".ko") == NULL) /* not a module */
-               goto skip;
+static module_info *find_module(module_info *modules, const char *modname)
+{
+       module_info *m;
 
-/*XXX: FIXME: does not handle compressed modules!
- * There should be a function that looks at the extension and sets up
- * open_transformer for us.
- */
-       fd = xopen(fname, O_RDONLY);
-       the_module = mmap(NULL, len, PROT_READ, MAP_SHARED
-#if defined MAP_POPULATE
-                                               |MAP_POPULATE
-#endif
-                                               , fd, 0);
-       close(fd);
-       if (the_module == MAP_FAILED)
-               bb_perror_msg_and_die("mmap");
+       for (m = modules; m != NULL; m = m->next)
+               if (strcmp(m->modname, modname) == 0)
+                       return m;
+       return NULL;
+}
 
-       this = xzalloc(sizeof(dep_lst_t));
-       this->name = xstrdup(fname);
-       this->next = G.lst;
-       G.lst = this;
-//bb_info_msg("fname='%s'", fname);
-       ptr = find_keyword(the_module, len, "depends=");
-       if (!*ptr)
-               goto d_none;
-       deps = depends = xstrdup(ptr);
-//bb_info_msg(" depends='%s'", depends);
-       while (deps) {
-               ptr = strsep(&deps, ",");
-//bb_info_msg("[%s] -> '%s'", fname, (char*)ptr);
-               llist_add_to_end(&this->dependencies, xstrdup(ptr));
-       }
-       free(depends);
- d_none:
-       if (ENABLE_FEATURE_DEPMOD_ALIAS)
-       {
-               size_t pos = 0;
-               do {
-                       ptr = find_keyword(the_module + pos, len - pos, "alias=");
-                       if (ptr) {
-//bb_info_msg("[%s] alias '%s'", fname, (char*)ptr);
-                                       llist_add_to_end(&this->aliases, xstrdup(ptr));
-                       } else
-                               break;
-                       pos = (ptr - (char*)the_module);
-               } while (1);
+static void order_dep_list(module_info *modules, module_info *start,
+                          llist_t *add)
+{
+       module_info *m;
+       llist_t *n;
+
+       for (n = add; n != NULL; n = n->link) {
+               m = find_module(modules, n->data);
+               if (m == NULL)
+                       continue;
+
+               /* unlink current entry */
+               m->dnext->dprev = m->dprev;
+               m->dprev->dnext = m->dnext;
+
+               /* and add it to tail */
+               m->dnext = start;
+               m->dprev = start->dprev;
+               start->dprev->dnext = m;
+               start->dprev = m;
+
+               /* recurse */
+               order_dep_list(modules, start, m->dependencies);
        }
-       munmap(the_module, sb->st_size);
- skip:
-       return TRUE;
 }
 
 int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int depmod_main(int argc UNUSED_PARAM, char **argv)
 {
-       int ret;
-       size_t moddir_base_len = 0; /* length of the "-b basedir" */
-       char *moddir_base = NULL, *moddir, *system_map, *chp;
-       FILE *filedes = stdout;
-       enum {
-               ARG_a = (1<<0), /* All modules, ignore mods in argv */
-               ARG_A = (1<<1), /* Only emit .ko that are newer than modules.dep file */
-               ARG_b = (1<<2), /* not /lib/modules/$(uname -r)/ but this base-dir */
-               ARG_e = (1<<3), /* with -F, print unresolved symbols */
-               ARG_F = (1<<4), /* System.map that contains the symbols */
-               ARG_n = (1<<5)  /* dry-run, print to stdout only */
-       };
-       INIT_G();
+       module_info *modules = NULL, *m, *dep;
+       char *moddir_base = (char *)CONFIG_DEFAULT_MODULES_DIR;
+       int tmp;
 
-       getopt32(argv, "aAb:eF:n", &moddir_base, &system_map);
+       getopt32(argv, "aAb:eF:n", &moddir_base, NULL);
        argv += optind;
 
-       /* If a version is provided, then that kernel version’s module directory
+       /* goto modules location */
+
+       /* If a version is provided, then that kernel version's module directory
         * is used, rather than the current kernel version (as returned by
         * "uname -r").  */
-       if (*argv && (sscanf(*argv, "%d.%d.%d", &ret, &ret, &ret) == 3)) {
-               moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, *argv++);
+       xchdir(moddir_base);
+       if (*argv && (sscanf(*argv, "%d.%d.%d", &tmp, &tmp, &tmp) == 3)) {
+               xchdir(*argv++);
        } else {
                struct utsname uts;
-               if (uname(&uts) < 0)
-                       bb_simple_perror_msg_and_die("uname");
-               moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, uts.release);
+               uname(&uts);
+               xchdir(uts.release);
        }
        /* If no modules are given on the command-line, -a is on per default.  */
        option_mask32 |= *argv == NULL;
 
-       if (option_mask32 & ARG_b) {
-               moddir_base_len = strlen(moddir_base) + 1;
-               xchdir(moddir_base);
-       }
-
-       if (!(option_mask32 & ARG_n)) { /* --dry-run */
-               chp = concat_path_file(moddir, CONFIG_DEFAULT_DEPMOD_FILE);
-               filedes = xfopen_for_write(chp);
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       free(chp);
-       }
-       ret = EXIT_SUCCESS;
+       /* Scan modules */
+       moddir_base = xrealloc_getcwd_or_warn(NULL);
        do {
-               chp = option_mask32 & ARG_a ? moddir : (*argv + moddir_base_len);
-
-               if (!recursive_action(chp,
-                               ACTION_RECURSE, /* flags */
-                               fileAction, /* file action */
-                               NULL, /* dir action */
-                               NULL, /* user data */
-                               0)) { /* depth */
-                       ret = EXIT_FAILURE;
+               recursive_action((option_mask32 & ARG_a) ? moddir_base : *argv,
+                               ACTION_RECURSE, parse_module, NULL,  &modules, 0);
+       } while (!(option_mask32 & ARG_a) && *(++argv));
+       if (ENABLE_FEATURE_CLEAN_UP)
+               free(moddir_base);
+
+       /* Generate dependency and alias files */
+       if (!(option_mask32 & ARG_n))
+               freopen(CONFIG_DEFAULT_DEPMOD_FILE, "w", stdout);
+       for (m = modules; m != NULL; m = m->next) {
+               printf("%s:", m->name);
+
+               order_dep_list(modules, m, m->dependencies);
+               while (m->dnext != m) {
+                       dep = m->dnext;
+                       printf(" %s", dep->name);
+
+                       /* unlink current entry */
+                       dep->dnext->dprev = dep->dprev;
+                       dep->dprev->dnext = dep->dnext;
+                       dep->dnext = dep->dprev = dep;
                }
-       } while (!(option_mask32 & ARG_a) && *++argv);
-
-       {
-       dep_lst_t *mods = G.lst;
-
-       /* Fixup the module names in the depends list */
-       while (mods) {
-               llist_t *deps = NULL, *old_deps = mods->dependencies;
-
-               while (old_deps) {
-                       dep_lst_t *all = G.lst;
-                       char *longname = NULL;
-                       char *shortname = llist_pop(&old_deps);
-
-                       while (all) {
-                               char *nam =
-                                       xstrdup(bb_get_last_path_component_nostrip(all->name));
-                               char *tmp = strrstr(nam, ".ko");
-
-                               *tmp = '\0';
-                               if (!strcmp(nam, shortname)) {
-                                       if (ENABLE_FEATURE_CLEAN_UP)
-                                               free(nam);
-                                       longname = all->name;
-                                       break;
-                               }
-                               free(nam);
-                               all = all->next;
-                       }
-                       llist_add_to_end(&deps, longname);
-               }
-               mods->dependencies = deps;
-               mods = mods->next;
+               puts("");
        }
 
-#if ENABLE_FEATURE_DEPMOD_PRUNE_FANCY
-       /* modprobe allegedly wants dependencies without duplicates, i.e.
-        * mod1: mod2 mod3
-        * mod2: mod3
-        * mod3:
-        * implies that mod1 directly depends on mod2 and _not_ mod3 as mod3 is
-        * already implicitely pulled in via mod2. This leaves us with:
-        * mod1: mod2
-        * mod2: mod3
-        * mod3:
-        */
-       mods = G.lst;
-       while (mods) {
-               llist_t *deps = mods->dependencies;
-               while (deps) {
-                       dep_lst_t *all = G.lst;
-                       while (all) {
-                               if (!strcmp(all->name, deps->data)) {
-                                       llist_t *implied = all->dependencies;
-                                       while (implied) {
-                                               /* XXX:FIXME: erm, it would be nicer to just
-                                                * llist_unlink(&mods->dependencies, implied)  */
-                                               llist_t *prune = mods->dependencies;
-                                               while (prune) {
-                                                       if (!strcmp(implied->data, prune->data))
-                                                               break;
-                                                       prune = prune->link;
-                                               }
-//if (prune) bb_info_msg("[%s] '%s' implies '%s', removing", mods->name, all->name, implied->data);
-                                               llist_unlink(&mods->dependencies, prune);
-                                               implied = implied->link;
-                                       }
-                               }
-                               all = all->next;
-                       }
-                       deps = deps->link;
+#if ENABLE_FEATURE_MODUTILS_ALIAS
+       if (!(option_mask32 & ARG_n))
+               freopen("modules.alias", "w", stdout);
+       for (m = modules; m != NULL; m = m->next) {
+               while (m->aliases) {
+                       printf("alias %s %s\n",
+                               (char*)llist_pop(&m->aliases),
+                               m->modname);
                }
-               mods = mods->next;
        }
 #endif
-
-       mods = G.lst;
-       /* Finally print them.  */
-       while (mods) {
-               fprintf(filedes, "%s:", mods->name);
-               /* If we did not resolve all modules, then it's likely that we just did
-                * not see the names of all prerequisites (which will be NULL in this
-                * case).  */
-               while (mods->dependencies) {
-                       char *the_dep = llist_pop(&mods->dependencies);
-                       if (the_dep)
-                               fprintf(filedes, " %s", the_dep);
-               }
-               fprintf(filedes, "\n");
-               if (ENABLE_FEATURE_DEPMOD_ALIAS)
-               {
-                       char *shortname =
-                               xstrdup(bb_get_last_path_component_nostrip(mods->name));
-                       char *tmp = strrstr(shortname, ".ko");
-
-                       *tmp = '\0';
-
-                       while (mods->aliases) {
-                               fprintf(filedes, "alias %s %s\n",
-                                       (char*)llist_pop(&mods->aliases),
-                                       shortname);
-                       }
-                       free(shortname);
+#if ENABLE_FEATURE_MODUTILS_SYMBOLS
+       if (!(option_mask32 & ARG_n))
+               freopen("modules.symbols", "w", stdout);
+       for (m = modules; m != NULL; m = m->next) {
+               while (m->symbols) {
+                       printf("alias symbol:%s %s\n",
+                               (char*)llist_pop(&m->symbols),
+                               m->modname);
                }
-               mods = mods->next;
-       }
        }
+#endif
 
        if (ENABLE_FEATURE_CLEAN_UP) {
-               fclose_if_not_stdin(filedes);
-               free(moddir);
-               while (G.lst) {
-                       dep_lst_t *old = G.lst;
-                       G.lst = G.lst->next;
+               while (modules) {
+                       module_info *old = modules;
+                       modules = modules->next;
                        free(old->name);
+                       free(old->modname);
                        free(old);
                }
        }
-       return ret;
+
+       return EXIT_SUCCESS;
 }
index f1bb4cdecde8761e74294f046418b0d953f4099e..61ee5009b9bee281e8eb1a6c1887b31eeec4d59c 100644 (file)
 /*
  * Mini insmod implementation for busybox
  *
- * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64,
- * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64.
+ * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>
  *
- * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
- * and Ron Alder <alder@lineo.com>
- *
- * Rodney Radford <rradford@mindspring.com> 17-Aug-2004.
- *   Added x86_64 support.
- *
- * Miles Bader <miles@gnu.org> added NEC V850E support.
- *
- * Modified by Bryan Rittmeyer <bryan@ixiacom.com> to support SH4
- * and (theoretically) SH3. I have only tested SH4 in little endian mode.
- *
- * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
- * Nicolas Ferre <nicolas.ferre@alcove.fr> to support ARM7TDMI.  Only
- * very minor changes required to also work with StrongArm and presumably
- * all ARM based systems.
- *
- * Yoshinori Sato <ysato@users.sourceforge.jp> 19-May-2004.
- *   added Renesas H8/300 support.
- *
- * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
- *   Integrated support for sh64 (SH-5), from preliminary modutils
- *   patches from Benedict Gaster <benedict.gaster@superh.com>.
- *   Currently limited to support for 32bit ABI.
- *
- * Magnus Damm <damm@opensource.se> 22-May-2002.
- *   The plt and got code are now using the same structs.
- *   Added generic linked list code to fully support PowerPC.
- *   Replaced the mess in arch_apply_relocation() with architecture blocks.
- *   The arch_create_got() function got cleaned up with architecture blocks.
- *   These blocks should be easy maintain and sync with obj_xxx.c in modutils.
- *
- * Magnus Damm <damm@opensource.se> added PowerPC support 20-Feb-2001.
- *   PowerPC specific code stolen from modutils-2.3.16,
- *   written by Paul Mackerras, Copyright 1996, 1997 Linux International.
- *   I've only tested the code on mpc8xx platforms in big-endian mode.
- *   Did some cleanup and added USE_xxx_ENTRIES...
- *
- * Quinn Jensen <jensenq@lineo.com> added MIPS support 23-Feb-2001.
- *   based on modutils-2.4.2
- *   MIPS specific support for Elf loading and relocation.
- *   Copyright 1996, 1997 Linux International.
- *   Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>
- *
- * Based almost entirely on the Linux modutils-2.3.11 implementation.
- *   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
- *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-#include <libgen.h>
-#include <sys/utsname.h>
-
-#if !ENABLE_FEATURE_2_4_MODULES && !ENABLE_FEATURE_2_6_MODULES
-#undef ENABLE_FEATURE_2_4_MODULES
-#define ENABLE_FEATURE_2_4_MODULES 1
-#endif
-
-/*
- * Big piece of 2.4-specific code
- */
-#if ENABLE_FEATURE_2_4_MODULES
-
-#if ENABLE_FEATURE_2_6_MODULES
-static int insmod_ng_main(int argc, char **argv);
-#endif
-
-#if ENABLE_FEATURE_INSMOD_LOADINKMEM
-#define LOADBITS 0
-#else
-#define LOADBITS 1
-#endif
-
-/* Alpha */
-#if defined(__alpha__)
-#define MATCH_MACHINE(x) (x == EM_ALPHA)
-#define SHT_RELM       SHT_RELA
-#define Elf64_RelM     Elf64_Rela
-#define ELFCLASSM      ELFCLASS64
-#endif
-
-/* ARM support */
-#if defined(__arm__)
-#define MATCH_MACHINE(x) (x == EM_ARM)
-#define SHT_RELM       SHT_REL
-#define Elf32_RelM     Elf32_Rel
-#define ELFCLASSM      ELFCLASS32
-#define USE_PLT_ENTRIES
-#define PLT_ENTRY_SIZE 8
-#define USE_GOT_ENTRIES
-#define GOT_ENTRY_SIZE 8
-#define USE_SINGLE
-#endif
-
-/* blackfin */
-#if defined(BFIN)
-#define MATCH_MACHINE(x) (x == EM_BLACKFIN)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#endif
-
-/* CRIS */
-#if defined(__cris__)
-#define MATCH_MACHINE(x) (x == EM_CRIS)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#ifndef EM_CRIS
-#define EM_CRIS 76
-#define R_CRIS_NONE 0
-#define R_CRIS_32   3
-#endif
-#endif
-
-/* H8/300 */
-#if defined(__H8300H__) || defined(__H8300S__)
-#define MATCH_MACHINE(x) (x == EM_H8_300)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#define USE_SINGLE
-#define SYMBOL_PREFIX  "_"
-#endif
-
-/* PA-RISC / HP-PA */
-#if defined(__hppa__)
-#define MATCH_MACHINE(x) (x == EM_PARISC)
-#define SHT_RELM       SHT_RELA
-#if defined(__LP64__)
-#define Elf64_RelM     Elf64_Rela
-#define ELFCLASSM      ELFCLASS64
-#else
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#endif
-#endif
-
-/* x86 */
-#if defined(__i386__)
-#ifndef EM_486
-#define MATCH_MACHINE(x) (x == EM_386)
-#else
-#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486)
-#endif
-#define SHT_RELM       SHT_REL
-#define Elf32_RelM     Elf32_Rel
-#define ELFCLASSM      ELFCLASS32
-#define USE_GOT_ENTRIES
-#define GOT_ENTRY_SIZE 4
-#define USE_SINGLE
-#endif
-
-/* IA64, aka Itanium */
-#if defined(__ia64__)
-#define MATCH_MACHINE(x) (x == EM_IA_64)
-#define SHT_RELM       SHT_RELA
-#define Elf64_RelM     Elf64_Rela
-#define ELFCLASSM      ELFCLASS64
-#endif
-
-/* m68k */
-#if defined(__mc68000__)
-#define MATCH_MACHINE(x) (x == EM_68K)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#define USE_GOT_ENTRIES
-#define GOT_ENTRY_SIZE 4
-#define USE_SINGLE
-#endif
-
-/* Microblaze */
-#if defined(__microblaze__)
-#define USE_SINGLE
-#include <linux/elf-em.h>
-#define MATCH_MACHINE(x) (x == EM_XILINX_MICROBLAZE)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#endif
-
-/* MIPS */
-#if defined(__mips__)
-#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE)
-#define SHT_RELM       SHT_REL
-#define Elf32_RelM     Elf32_Rel
-#define ELFCLASSM      ELFCLASS32
-/* Account for ELF spec changes.  */
-#ifndef EM_MIPS_RS3_LE
-#ifdef EM_MIPS_RS4_BE
-#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE
-#else
-#define EM_MIPS_RS3_LE 10
-#endif
-#endif /* !EM_MIPS_RS3_LE */
-#define ARCHDATAM       "__dbe_table"
-#endif
-
-/* Nios II */
-#if defined(__nios2__)
-#define MATCH_MACHINE(x) (x == EM_ALTERA_NIOS2)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#endif
-
-/* PowerPC */
-#if defined(__powerpc64__)
-#define MATCH_MACHINE(x) (x == EM_PPC64)
-#define SHT_RELM       SHT_RELA
-#define Elf64_RelM     Elf64_Rela
-#define ELFCLASSM      ELFCLASS64
-#elif defined(__powerpc__)
-#define MATCH_MACHINE(x) (x == EM_PPC)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#define USE_PLT_ENTRIES
-#define PLT_ENTRY_SIZE 16
-#define USE_PLT_LIST
-#define LIST_ARCHTYPE ElfW(Addr)
-#define USE_LIST
-#define ARCHDATAM       "__ftr_fixup"
-#endif
-
-/* S390 */
-#if defined(__s390__)
-#define MATCH_MACHINE(x) (x == EM_S390)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#define USE_PLT_ENTRIES
-#define PLT_ENTRY_SIZE 8
-#define USE_GOT_ENTRIES
-#define GOT_ENTRY_SIZE 8
-#define USE_SINGLE
-#endif
-
-/* SuperH */
-#if defined(__sh__)
-#define MATCH_MACHINE(x) (x == EM_SH)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#define USE_GOT_ENTRIES
-#define GOT_ENTRY_SIZE 4
-#define USE_SINGLE
-#endif
-
-/* Sparc */
-#if defined(__sparc__)
-#define MATCH_MACHINE(x) (x == EM_SPARC)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#endif
-
-/* v850e */
-#if defined(__v850e__)
-#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850)
-#define SHT_RELM       SHT_RELA
-#define Elf32_RelM     Elf32_Rela
-#define ELFCLASSM      ELFCLASS32
-#define USE_PLT_ENTRIES
-#define PLT_ENTRY_SIZE 8
-#define USE_SINGLE
-#ifndef EM_CYGNUS_V850 /* grumble */
-#define EM_CYGNUS_V850 0x9080
-#endif
-#define SYMBOL_PREFIX  "_"
-#endif
-
-/* X86_64  */
-#if defined(__x86_64__)
-#define MATCH_MACHINE(x) (x == EM_X86_64)
-#define SHT_RELM       SHT_RELA
-#define USE_GOT_ENTRIES
-#define GOT_ENTRY_SIZE 8
-#define USE_SINGLE
-#define Elf64_RelM     Elf64_Rela
-#define ELFCLASSM      ELFCLASS64
-#endif
-
-#ifndef SHT_RELM
-#error Sorry, but insmod.c does not yet support this architecture...
-#endif
-
-
-//----------------------------------------------------------------------------
-//--------modutils module.h, lines 45-242
-//----------------------------------------------------------------------------
-
-/* Definitions for the Linux module syscall interface.
-   Copyright 1996, 1997 Linux International.
-
-   Contributed by Richard Henderson <rth@tamu.edu>
-
-   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.  */
-
-
-#ifndef MODUTILS_MODULE_H
-
-/*======================================================================*/
-/* For sizeof() which are related to the module platform and not to the
-   environment isnmod is running in, use sizeof_xx instead of sizeof(xx).  */
-
-#define tgt_sizeof_char                sizeof(char)
-#define tgt_sizeof_short       sizeof(short)
-#define tgt_sizeof_int         sizeof(int)
-#define tgt_sizeof_long                sizeof(long)
-#define tgt_sizeof_char_p      sizeof(char *)
-#define tgt_sizeof_void_p      sizeof(void *)
-#define tgt_long               long
-
-#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64)
-#undef tgt_sizeof_long
-#undef tgt_sizeof_char_p
-#undef tgt_sizeof_void_p
-#undef tgt_long
-enum {
-       tgt_sizeof_long = 8,
-       tgt_sizeof_char_p = 8,
-       tgt_sizeof_void_p = 8
-};
-#define tgt_long               long long
-#endif
-
-/*======================================================================*/
-/* The structures used in Linux 2.1.  */
-
-/* Note: new_module_symbol does not use tgt_long intentionally */
-struct new_module_symbol {
-       unsigned long value;
-       unsigned long name;
-};
-
-struct new_module_persist;
-
-struct new_module_ref {
-       unsigned tgt_long dep;          /* kernel addresses */
-       unsigned tgt_long ref;
-       unsigned tgt_long next_ref;
-};
-
-struct new_module {
-       unsigned tgt_long size_of_struct;       /* == sizeof(module) */
-       unsigned tgt_long next;
-       unsigned tgt_long name;
-       unsigned tgt_long size;
-
-       tgt_long usecount;
-       unsigned tgt_long flags;                /* AUTOCLEAN et al */
-
-       unsigned nsyms;
-       unsigned ndeps;
-
-       unsigned tgt_long syms;
-       unsigned tgt_long deps;
-       unsigned tgt_long refs;
-       unsigned tgt_long init;
-       unsigned tgt_long cleanup;
-       unsigned tgt_long ex_table_start;
-       unsigned tgt_long ex_table_end;
-#ifdef __alpha__
-       unsigned tgt_long gp;
-#endif
-       /* Everything after here is extension.  */
-       unsigned tgt_long persist_start;
-       unsigned tgt_long persist_end;
-       unsigned tgt_long can_unload;
-       unsigned tgt_long runsize;
-       const char *kallsyms_start;     /* All symbols for kernel debugging */
-       const char *kallsyms_end;
-       const char *archdata_start;     /* arch specific data for module */
-       const char *archdata_end;
-       const char *kernel_data;        /* Reserved for kernel internal use */
-};
-
-#ifdef ARCHDATAM
-#define ARCHDATA_SEC_NAME ARCHDATAM
-#else
-#define ARCHDATA_SEC_NAME "__archdata"
-#endif
-#define KALLSYMS_SEC_NAME "__kallsyms"
-
-
-struct new_module_info {
-       unsigned long addr;
-       unsigned long size;
-       unsigned long flags;
-       long usecount;
-};
-
-/* Bits of module.flags.  */
-enum {
-       NEW_MOD_RUNNING = 1,
-       NEW_MOD_DELETED = 2,
-       NEW_MOD_AUTOCLEAN = 4,
-       NEW_MOD_VISITED = 8,
-       NEW_MOD_USED_ONCE = 16
-};
-
-int init_module(const char *name, const struct new_module *);
-int query_module(const char *name, int which, void *buf,
-               size_t bufsize, size_t *ret);
-
-/* Values for query_module's which.  */
-enum {
-       QM_MODULES = 1,
-       QM_DEPS = 2,
-       QM_REFS = 3,
-       QM_SYMBOLS = 4,
-       QM_INFO = 5
-};
-
-/*======================================================================*/
-/* The system calls unchanged between 2.0 and 2.1.  */
-
-unsigned long create_module(const char *, size_t);
-int delete_module(const char *module, unsigned int flags);
-
-
-#endif /* module.h */
-
-//----------------------------------------------------------------------------
-//--------end of modutils module.h
-//----------------------------------------------------------------------------
-
-
-
-//----------------------------------------------------------------------------
-//--------modutils obj.h, lines 253-462
-//----------------------------------------------------------------------------
-
-/* Elf object file loading and relocation routines.
-   Copyright 1996, 1997 Linux International.
-
-   Contributed by Richard Henderson <rth@tamu.edu>
-
-   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.  */
-
-
-#ifndef MODUTILS_OBJ_H
-
-/* The relocatable object is manipulated using elfin types.  */
-
-#include <elf.h>
-#include <endian.h>
-
-#ifndef ElfW
-# if ELFCLASSM == ELFCLASS32
-#  define ElfW(x)  Elf32_ ## x
-#  define ELFW(x)  ELF32_ ## x
-# else
-#  define ElfW(x)  Elf64_ ## x
-#  define ELFW(x)  ELF64_ ## x
-# endif
-#endif
-
-/* For some reason this is missing from some ancient C libraries....  */
-#ifndef ELF32_ST_INFO
-# define ELF32_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
-#endif
-
-#ifndef ELF64_ST_INFO
-# define ELF64_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
-#endif
-
-#define ELF_ST_BIND(info) ELFW(ST_BIND)(info)
-#define ELF_ST_TYPE(info) ELFW(ST_TYPE)(info)
-#define ELF_ST_INFO(bind, type) ELFW(ST_INFO)(bind, type)
-#define ELF_R_TYPE(val) ELFW(R_TYPE)(val)
-#define ELF_R_SYM(val) ELFW(R_SYM)(val)
-
-struct obj_string_patch;
-struct obj_symbol_patch;
-
-struct obj_section
-{
-       ElfW(Shdr) header;
-       const char *name;
-       char *contents;
-       struct obj_section *load_next;
-       int idx;
-};
-
-struct obj_symbol
-{
-       struct obj_symbol *next;        /* hash table link */
-       const char *name;
-       unsigned long value;
-       unsigned long size;
-       int secidx;                     /* the defining section index/module */
-       int info;
-       int ksymidx;                    /* for export to the kernel symtab */
-       int referenced;         /* actually used in the link */
-};
-
-/* Hardcode the hash table size.  We shouldn't be needing so many
-   symbols that we begin to degrade performance, and we get a big win
-   by giving the compiler a constant divisor.  */
-
-#define HASH_BUCKETS  521
-
-struct obj_file {
-       ElfW(Ehdr) header;
-       ElfW(Addr) baseaddr;
-       struct obj_section **sections;
-       struct obj_section *load_order;
-       struct obj_section **load_order_search_start;
-       struct obj_string_patch *string_patches;
-       struct obj_symbol_patch *symbol_patches;
-       int (*symbol_cmp)(const char *, const char *);
-       unsigned long (*symbol_hash)(const char *);
-       unsigned long local_symtab_size;
-       struct obj_symbol **local_symtab;
-       struct obj_symbol *symtab[HASH_BUCKETS];
-};
-
-enum obj_reloc {
-       obj_reloc_ok,
-       obj_reloc_overflow,
-       obj_reloc_dangerous,
-       obj_reloc_unhandled
-};
-
-struct obj_string_patch {
-       struct obj_string_patch *next;
-       int reloc_secidx;
-       ElfW(Addr) reloc_offset;
-       ElfW(Addr) string_offset;
-};
-
-struct obj_symbol_patch {
-       struct obj_symbol_patch *next;
-       int reloc_secidx;
-       ElfW(Addr) reloc_offset;
-       struct obj_symbol *sym;
-};
-
-
-/* Generic object manipulation routines.  */
-
-static unsigned long obj_elf_hash(const char *);
-
-static unsigned long obj_elf_hash_n(const char *, unsigned long len);
-
-static struct obj_symbol *obj_find_symbol(struct obj_file *f,
-                                        const char *name);
-
-static ElfW(Addr) obj_symbol_final_value(struct obj_file *f,
-                                 struct obj_symbol *sym);
-
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-static void obj_set_symbol_compare(struct obj_file *f,
-                           int (*cmp)(const char *, const char *),
-                           unsigned long (*hash)(const char *));
-#endif
-
-static struct obj_section *obj_find_section(struct obj_file *f,
-                                          const char *name);
-
-static void obj_insert_section_load_order(struct obj_file *f,
-                                   struct obj_section *sec);
-
-static struct obj_section *obj_create_alloced_section(struct obj_file *f,
-                                               const char *name,
-                                               unsigned long align,
-                                               unsigned long size);
-
-static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
-                                                     const char *name,
-                                                     unsigned long align,
-                                                     unsigned long size);
-
-static void *obj_extend_section(struct obj_section *sec, unsigned long more);
-
-static void obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
-                    const char *string);
-
-static void obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
-                    struct obj_symbol *sym);
-
-static void obj_check_undefineds(struct obj_file *f);
-
-static void obj_allocate_commons(struct obj_file *f);
-
-static unsigned long obj_load_size(struct obj_file *f);
-
-static int obj_relocate(struct obj_file *f, ElfW(Addr) base);
-
-static struct obj_file *obj_load(FILE *f, int loadprogbits);
-
-static int obj_create_image(struct obj_file *f, char *image);
-
-/* Architecture specific manipulation routines.  */
-
-static struct obj_file *arch_new_file(void);
-
-static struct obj_section *arch_new_section(void);
-
-static struct obj_symbol *arch_new_symbol(void);
-
-static enum obj_reloc arch_apply_relocation(struct obj_file *f,
-                                     struct obj_section *targsec,
-                                     /*struct obj_section *symsec,*/
-                                     struct obj_symbol *sym,
-                                     ElfW(RelM) *rel, ElfW(Addr) value);
-
-static void arch_create_got(struct obj_file *f);
-#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
-static int obj_gpl_license(struct obj_file *f, const char **license);
-#endif /* FEATURE_CHECK_TAINTED_MODULE */
-#endif /* obj.h */
-//----------------------------------------------------------------------------
-//--------end of modutils obj.h
-//----------------------------------------------------------------------------
-
-
-/* SPFX is always a string, so it can be concatenated to string constants.  */
-#ifdef SYMBOL_PREFIX
-#define SPFX   SYMBOL_PREFIX
-#else
-#define SPFX   ""
-#endif
-
-enum { STRVERSIONLEN = 64 };
-
-/*======================================================================*/
-
-#define OPTION_STR "sLo:fkvqx" USE_FEATURE_INSMOD_LOAD_MAP("m")
-enum {
-       OPT_s = 0x1, // -s /* log to syslog */
-               /* Not supported but kernel needs this for request_module(),
-                  as this calls: modprobe -k -s -- <module>
-                  so silently ignore this flag */
-       OPT_L = 0x2, // -L /* Stub warning */
-               /* Compatibility with modprobe.
-                  In theory, this does locking, but we don't do
-                  that.  So be careful and plan your life around not
-                  loading the same module 50 times concurrently. */
-       OPT_o = 0x4, // -o /* name the output module */
-       OPT_f = 0x8, // -f /* force loading */
-       OPT_k = 0x10, // -k /* module loaded by kerneld, auto-cleanable */
-       OPT_v = 0x20, // -v /* verbose output */
-       OPT_q = 0x40, // -q /* silent */
-       OPT_x = 0x80, // -x /* do not export externs */
-       OPT_m = 0x100, // -m /* print module load map */
-};
-#define flag_force_load (option_mask32 & OPT_f)
-#define flag_autoclean (option_mask32 & OPT_k)
-#define flag_verbose (option_mask32 & OPT_v)
-#define flag_quiet (option_mask32 & OPT_q)
-#define flag_noexport (option_mask32 & OPT_x)
-#if ENABLE_FEATURE_INSMOD_LOAD_MAP
-#define flag_print_load_map (option_mask32 & OPT_m)
-#else
-#define flag_print_load_map 0
-#endif
-
-/*======================================================================*/
-
-#if defined(USE_LIST)
-
-struct arch_list_entry
-{
-       struct arch_list_entry *next;
-       LIST_ARCHTYPE addend;
-       int offset;
-       int inited : 1;
-};
-
-#endif
-
-#if defined(USE_SINGLE)
-
-struct arch_single_entry
-{
-       int offset;
-       int inited : 1;
-       int allocated : 1;
-};
-
-#endif
-
-#if defined(__mips__)
-struct mips_hi16
-{
-       struct mips_hi16 *next;
-       ElfW(Addr) *addr;
-       ElfW(Addr) value;
-};
-#endif
-
-struct arch_file {
-       struct obj_file root;
-#if defined(USE_PLT_ENTRIES)
-       struct obj_section *plt;
-#endif
-#if defined(USE_GOT_ENTRIES)
-       struct obj_section *got;
-#endif
-#if defined(__mips__)
-       struct mips_hi16 *mips_hi16_list;
-#endif
-};
-
-struct arch_symbol {
-       struct obj_symbol root;
-#if defined(USE_PLT_ENTRIES)
-#if defined(USE_PLT_LIST)
-       struct arch_list_entry *pltent;
-#else
-       struct arch_single_entry pltent;
-#endif
-#endif
-#if defined(USE_GOT_ENTRIES)
-       struct arch_single_entry gotent;
-#endif
-};
-
-
-struct external_module {
-       const char *name;
-       ElfW(Addr) addr;
-       int used;
-       size_t nsyms;
-       struct new_module_symbol *syms;
-};
-
-static struct new_module_symbol *ksyms;
-static size_t nksyms;
-
-static struct external_module *ext_modules;
-static int n_ext_modules;
-static int n_ext_modules_used;
-
-static char *m_filename;
-static char *m_fullName;
-
-
-/*======================================================================*/
-
-
-static int FAST_FUNC check_module_name_match(const char *filename,
-               struct stat *statbuf UNUSED_PARAM,
-               void *userdata, int depth UNUSED_PARAM)
-{
-       char *fullname = (char *) userdata;
-       char *tmp;
-
-       if (fullname[0] == '\0')
-               return FALSE;
-
-       tmp = bb_get_last_path_component_nostrip(filename);
-       if (strcmp(tmp, fullname) == 0) {
-               /* Stop searching if we find a match */
-               m_filename = xstrdup(filename);
-               return FALSE;
-       }
-       return TRUE;
-}
-
-
-/*======================================================================*/
-
-static struct obj_file *arch_new_file(void)
-{
-       struct arch_file *f;
-       f = xzalloc(sizeof(*f));
-       return &f->root; /* it's a first member */
-}
-
-static struct obj_section *arch_new_section(void)
-{
-       return xzalloc(sizeof(struct obj_section));
-}
-
-static struct obj_symbol *arch_new_symbol(void)
-{
-       struct arch_symbol *sym;
-       sym = xzalloc(sizeof(*sym));
-       return &sym->root;
-}
-
-static enum obj_reloc
-arch_apply_relocation(struct obj_file *f,
-                               struct obj_section *targsec,
-                               /*struct obj_section *symsec,*/
-                               struct obj_symbol *sym,
-                               ElfW(RelM) *rel, ElfW(Addr) v)
-{
-#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \
- || defined(__sh__) || defined(__s390__) || defined(__x86_64__) \
- || defined(__powerpc__) || defined(__mips__)
-       struct arch_file *ifile = (struct arch_file *) f;
-#endif
-       enum obj_reloc ret = obj_reloc_ok;
-       ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset);
-#if defined(__arm__) || defined(__H8300H__) || defined(__H8300S__) \
- || defined(__i386__) || defined(__mc68000__) || defined(__microblaze__) \
- || defined(__mips__) || defined(__nios2__) || defined(__powerpc__) \
- || defined(__s390__) || defined(__sh__) || defined(__x86_64__)
-       ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset;
-#endif
-#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
-       struct arch_symbol *isym = (struct arch_symbol *) sym;
-#endif
-#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \
- || defined(__sh__) || defined(__s390__)
-#if defined(USE_GOT_ENTRIES)
-       ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0;
-#endif
-#endif
-#if defined(USE_PLT_ENTRIES)
-       ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0;
-       unsigned long *ip;
-# if defined(USE_PLT_LIST)
-       struct arch_list_entry *pe;
-# else
-       struct arch_single_entry *pe;
-# endif
-#endif
-
-       switch (ELF_R_TYPE(rel->r_info)) {
-
-#if defined(__arm__)
-
-               case R_ARM_NONE:
-                       break;
-
-               case R_ARM_ABS32:
-                       *loc += v;
-                       break;
-
-               case R_ARM_GOT32:
-                       goto bb_use_got;
-
-               case R_ARM_GOTPC:
-                       /* relative reloc, always to _GLOBAL_OFFSET_TABLE_
-                        * (which is .got) similar to branch,
-                        * but is full 32 bits relative */
-
-                       *loc += got - dot;
-                       break;
-
-               case R_ARM_PC24:
-               case R_ARM_PLT32:
-                       goto bb_use_plt;
-
-               case R_ARM_GOTOFF: /* address relative to the got */
-                       *loc += v - got;
-                       break;
-
-#elif defined(__cris__)
-
-               case R_CRIS_NONE:
-                       break;
-
-               case R_CRIS_32:
-                       /* CRIS keeps the relocation value in the r_addend field and
-                        * should not use whats in *loc at all
-                        */
-                       *loc = v;
-                       break;
-
-#elif defined(__H8300H__) || defined(__H8300S__)
-
-               case R_H8_DIR24R8:
-                       loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1);
-                       *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v);
-                       break;
-               case R_H8_DIR24A8:
-                       *loc += v;
-                       break;
-               case R_H8_DIR32:
-               case R_H8_DIR32A16:
-                       *loc += v;
-                       break;
-               case R_H8_PCREL16:
-                       v -= dot + 2;
-                       if ((ElfW(Sword))v > 0x7fff ||
-                           (ElfW(Sword))v < -(ElfW(Sword))0x8000)
-                               ret = obj_reloc_overflow;
-                       else
-                               *(unsigned short *)loc = v;
-                       break;
-               case R_H8_PCREL8:
-                       v -= dot + 1;
-                       if ((ElfW(Sword))v > 0x7f ||
-                           (ElfW(Sword))v < -(ElfW(Sword))0x80)
-                               ret = obj_reloc_overflow;
-                       else
-                               *(unsigned char *)loc = v;
-                       break;
-
-#elif defined(__i386__)
-
-               case R_386_NONE:
-                       break;
-
-               case R_386_32:
-                       *loc += v;
-                       break;
-
-               case R_386_PLT32:
-               case R_386_PC32:
-               case R_386_GOTOFF:
-                       *loc += v - dot;
-                       break;
-
-               case R_386_GLOB_DAT:
-               case R_386_JMP_SLOT:
-                       *loc = v;
-                       break;
-
-               case R_386_RELATIVE:
-                       *loc += f->baseaddr;
-                       break;
-
-               case R_386_GOTPC:
-                       *loc += got - dot;
-                       break;
-
-               case R_386_GOT32:
-                       goto bb_use_got;
-                       break;
-
-#elif defined(__microblaze__)
-               case R_MICROBLAZE_NONE:
-               case R_MICROBLAZE_64_NONE:
-               case R_MICROBLAZE_32_SYM_OP_SYM:
-               case R_MICROBLAZE_32_PCREL:
-                       break;
-
-               case R_MICROBLAZE_64_PCREL: {
-                       /* dot is the address of the current instruction.
-                        * v is the target symbol address.
-                        * So we need to extract the offset in the code,
-                        * adding v, then subtrating the current address
-                        * of this instruction.
-                        * Ex: "IMM 0xFFFE  bralid 0x0000" = "bralid 0xFFFE0000"
-                        */
-
-                       /* Get split offset stored in code */
-                       unsigned int temp = (loc[0] & 0xFFFF) << 16 |
-                                               (loc[1] & 0xFFFF);
-
-                       /* Adjust relative offset. -4 adjustment required
-                        * because dot points to the IMM insn, but branch
-                        * is computed relative to the branch instruction itself.
-                        */
-                       temp += v - dot - 4;
-
-                       /* Store back into code */
-                       loc[0] = (loc[0] & 0xFFFF0000) | temp >> 16;
-                       loc[1] = (loc[1] & 0xFFFF0000) | (temp & 0xFFFF);
-
-                       break;
-               }
-
-               case R_MICROBLAZE_32:
-                       *loc += v;
-                       break;
-
-               case R_MICROBLAZE_64: {
-                       /* Get split pointer stored in code */
-                       unsigned int temp1 = (loc[0] & 0xFFFF) << 16 |
-                                               (loc[1] & 0xFFFF);
-
-                       /* Add reloc offset */
-                       temp1+=v;
-
-                       /* Store back into code */
-                       loc[0] = (loc[0] & 0xFFFF0000) | temp1 >> 16;
-                       loc[1] = (loc[1] & 0xFFFF0000) | (temp1 & 0xFFFF);
-
-                       break;
-               }
-
-               case R_MICROBLAZE_32_PCREL_LO:
-               case R_MICROBLAZE_32_LO:
-               case R_MICROBLAZE_SRO32:
-               case R_MICROBLAZE_SRW32:
-                       ret = obj_reloc_unhandled;
-                       break;
-
-#elif defined(__mc68000__)
-
-               case R_68K_NONE:
-                       break;
-
-               case R_68K_32:
-                       *loc += v;
-                       break;
-
-               case R_68K_8:
-                       if (v > 0xff) {
-                               ret = obj_reloc_overflow;
-                       }
-                       *(char *)loc = v;
-                       break;
-
-               case R_68K_16:
-                       if (v > 0xffff) {
-                               ret = obj_reloc_overflow;
-                       }
-                       *(short *)loc = v;
-                       break;
-
-               case R_68K_PC8:
-                       v -= dot;
-                       if ((ElfW(Sword))v > 0x7f
-                        || (ElfW(Sword))v < -(ElfW(Sword))0x80
-                       ) {
-                               ret = obj_reloc_overflow;
-                       }
-                       *(char *)loc = v;
-                       break;
-
-               case R_68K_PC16:
-                       v -= dot;
-                       if ((ElfW(Sword))v > 0x7fff
-                        || (ElfW(Sword))v < -(ElfW(Sword))0x8000
-                       ) {
-                               ret = obj_reloc_overflow;
-                       }
-                       *(short *)loc = v;
-                       break;
-
-               case R_68K_PC32:
-                       *(int *)loc = v - dot;
-                       break;
-
-               case R_68K_GLOB_DAT:
-               case R_68K_JMP_SLOT:
-                       *loc = v;
-                       break;
-
-               case R_68K_RELATIVE:
-                       *(int *)loc += f->baseaddr;
-                       break;
-
-               case R_68K_GOT32:
-                       goto bb_use_got;
-
-# ifdef R_68K_GOTOFF
-               case R_68K_GOTOFF:
-                       *loc += v - got;
-                       break;
-# endif
-
-#elif defined(__mips__)
-
-               case R_MIPS_NONE:
-                       break;
-
-               case R_MIPS_32:
-                       *loc += v;
-                       break;
-
-               case R_MIPS_26:
-                       if (v % 4)
-                               ret = obj_reloc_dangerous;
-                       if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000))
-                               ret = obj_reloc_overflow;
-                       *loc =
-                               (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) &
-                                                                               0x03ffffff);
-                       break;
-
-               case R_MIPS_HI16:
-                       {
-                               struct mips_hi16 *n;
-
-                               /* We cannot relocate this one now because we don't know the value
-                                  of the carry we need to add.  Save the information, and let LO16
-                                  do the actual relocation.  */
-                               n = xmalloc(sizeof *n);
-                               n->addr = loc;
-                               n->value = v;
-                               n->next = ifile->mips_hi16_list;
-                               ifile->mips_hi16_list = n;
-                               break;
-                       }
-
-               case R_MIPS_LO16:
-                       {
-                               unsigned long insnlo = *loc;
-                               ElfW(Addr) val, vallo;
-
-                               /* Sign extend the addend we extract from the lo insn.  */
-                               vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
-
-                               if (ifile->mips_hi16_list != NULL) {
-                                       struct mips_hi16 *l;
-
-                                       l = ifile->mips_hi16_list;
-                                       while (l != NULL) {
-                                               struct mips_hi16 *next;
-                                               unsigned long insn;
-
-                                               /* Do the HI16 relocation.  Note that we actually don't
-                                                  need to know anything about the LO16 itself, except where
-                                                  to find the low 16 bits of the addend needed by the LO16.  */
-                                               insn = *l->addr;
-                                               val =
-                                                       ((insn & 0xffff) << 16) +
-                                                       vallo;
-                                               val += v;
-
-                                               /* Account for the sign extension that will happen in the
-                                                  low bits.  */
-                                               val =
-                                                       ((val >> 16) +
-                                                        ((val & 0x8000) !=
-                                                         0)) & 0xffff;
-
-                                               insn = (insn & ~0xffff) | val;
-                                               *l->addr = insn;
-
-                                               next = l->next;
-                                               free(l);
-                                               l = next;
-                                       }
-
-                                       ifile->mips_hi16_list = NULL;
-                               }
-
-                               /* Ok, we're done with the HI16 relocs.  Now deal with the LO16.  */
-                               val = v + vallo;
-                               insnlo = (insnlo & ~0xffff) | (val & 0xffff);
-                               *loc = insnlo;
-                               break;
-                       }
-
-#elif defined(__nios2__)
-
-               case R_NIOS2_NONE:
-                       break;
-
-               case R_NIOS2_BFD_RELOC_32:
-                       *loc += v;
-                       break;
-
-               case R_NIOS2_BFD_RELOC_16:
-                       if (v > 0xffff) {
-                               ret = obj_reloc_overflow;
-                       }
-                       *(short *)loc = v;
-                       break;
-
-               case R_NIOS2_BFD_RELOC_8:
-                       if (v > 0xff) {
-                               ret = obj_reloc_overflow;
-                       }
-                       *(char *)loc = v;
-                       break;
-
-               case R_NIOS2_S16:
-                       {
-                               Elf32_Addr word;
-
-                               if ((Elf32_Sword)v > 0x7fff
-                                || (Elf32_Sword)v < -(Elf32_Sword)0x8000
-                               ) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc;
-                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
-                                      (word & 0x3f);
-                       }
-                       break;
-
-               case R_NIOS2_U16:
-                       {
-                               Elf32_Addr word;
-
-                               if (v > 0xffff) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc;
-                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
-                                      (word & 0x3f);
-                       }
-                       break;
-
-               case R_NIOS2_PCREL16:
-                       {
-                               Elf32_Addr word;
-
-                               v -= dot + 4;
-                               if ((Elf32_Sword)v > 0x7fff
-                                || (Elf32_Sword)v < -(Elf32_Sword)0x8000
-                               ) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc;
-                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
-                       }
-                       break;
-
-               case R_NIOS2_GPREL:
-                       {
-                               Elf32_Addr word, gp;
-                               /* get _gp */
-                               gp = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "_gp"));
-                               v -= gp;
-                               if ((Elf32_Sword)v > 0x7fff
-                                || (Elf32_Sword)v < -(Elf32_Sword)0x8000
-                               ) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc;
-                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
-                       }
-                       break;
-
-               case R_NIOS2_CALL26:
-                       if (v & 3)
-                               ret = obj_reloc_dangerous;
-                       if ((v >> 28) != (dot >> 28))
-                               ret = obj_reloc_overflow;
-                       *loc = (*loc & 0x3f) | ((v >> 2) << 6);
-                       break;
-
-               case R_NIOS2_IMM5:
-                       {
-                               Elf32_Addr word;
-
-                               if (v > 0x1f) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc & ~0x7c0;
-                               *loc = word | ((v & 0x1f) << 6);
-                       }
-                       break;
-
-               case R_NIOS2_IMM6:
-                       {
-                               Elf32_Addr word;
-
-                               if (v > 0x3f) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc & ~0xfc0;
-                               *loc = word | ((v & 0x3f) << 6);
-                       }
-                       break;
-
-               case R_NIOS2_IMM8:
-                       {
-                               Elf32_Addr word;
-
-                               if (v > 0xff) {
-                                       ret = obj_reloc_overflow;
-                               }
-
-                               word = *loc & ~0x3fc0;
-                               *loc = word | ((v & 0xff) << 6);
-                       }
-                       break;
-
-               case R_NIOS2_HI16:
-                       {
-                               Elf32_Addr word;
-
-                               word = *loc;
-                               *loc = ((((word >> 22) << 16) | ((v >>16) & 0xffff)) << 6) |
-                                      (word & 0x3f);
-                       }
-                       break;
-
-               case R_NIOS2_LO16:
-                       {
-                               Elf32_Addr word;
-
-                               word = *loc;
-                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
-                                      (word & 0x3f);
-                       }
-                       break;
-
-               case R_NIOS2_HIADJ16:
-                       {
-                               Elf32_Addr word1, word2;
-
-                               word1 = *loc;
-                               word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff;
-                               *loc = ((((word1 >> 22) << 16) | word2) << 6) |
-                                      (word1 & 0x3f);
-                       }
-                       break;
-
-#elif defined(__powerpc64__)
-               /* PPC64 needs a 2.6 kernel, 2.4 module relocation irrelevant */
-
-#elif defined(__powerpc__)
-
-               case R_PPC_ADDR16_HA:
-                       *(unsigned short *)loc = (v + 0x8000) >> 16;
-                       break;
-
-               case R_PPC_ADDR16_HI:
-                       *(unsigned short *)loc = v >> 16;
-                       break;
-
-               case R_PPC_ADDR16_LO:
-                       *(unsigned short *)loc = v;
-                       break;
-
-               case R_PPC_REL24:
-                       goto bb_use_plt;
-
-               case R_PPC_REL32:
-                       *loc = v - dot;
-                       break;
-
-               case R_PPC_ADDR32:
-                       *loc = v;
-                       break;
-
-#elif defined(__s390__)
-
-               case R_390_32:
-                       *(unsigned int *) loc += v;
-                       break;
-               case R_390_16:
-                       *(unsigned short *) loc += v;
-                       break;
-               case R_390_8:
-                       *(unsigned char *) loc += v;
-                       break;
-
-               case R_390_PC32:
-                       *(unsigned int *) loc += v - dot;
-                       break;
-               case R_390_PC16DBL:
-                       *(unsigned short *) loc += (v - dot) >> 1;
-                       break;
-               case R_390_PC16:
-                       *(unsigned short *) loc += v - dot;
-                       break;
-
-               case R_390_PLT32:
-               case R_390_PLT16DBL:
-                       /* find the plt entry and initialize it.  */
-                       pe = (struct arch_single_entry *) &isym->pltent;
-                       if (pe->inited == 0) {
-                               ip = (unsigned long *)(ifile->plt->contents + pe->offset);
-                               ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
-                               ip[1] = 0x100607f1;
-                               if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
-                                       ip[2] = v - 2;
-                               else
-                                       ip[2] = v;
-                               pe->inited = 1;
-                       }
-
-                       /* Insert relative distance to target.  */
-                       v = plt + pe->offset - dot;
-                       if (ELF_R_TYPE(rel->r_info) == R_390_PLT32)
-                               *(unsigned int *) loc = (unsigned int) v;
-                       else if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
-                               *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
-                       break;
-
-               case R_390_GLOB_DAT:
-               case R_390_JMP_SLOT:
-                       *loc = v;
-                       break;
-
-               case R_390_RELATIVE:
-                       *loc += f->baseaddr;
-                       break;
-
-               case R_390_GOTPC:
-                       *(unsigned long *) loc += got - dot;
-                       break;
-
-               case R_390_GOT12:
-               case R_390_GOT16:
-               case R_390_GOT32:
-                       if (!isym->gotent.inited)
-                       {
-                               isym->gotent.inited = 1;
-                               *(ElfW(Addr) *)(ifile->got->contents + isym->gotent.offset) = v;
-                       }
-                       if (ELF_R_TYPE(rel->r_info) == R_390_GOT12)
-                               *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
-                       else if (ELF_R_TYPE(rel->r_info) == R_390_GOT16)
-                               *(unsigned short *) loc += isym->gotent.offset;
-                       else if (ELF_R_TYPE(rel->r_info) == R_390_GOT32)
-                               *(unsigned int *) loc += isym->gotent.offset;
-                       break;
-
-# ifndef R_390_GOTOFF32
-#  define R_390_GOTOFF32 R_390_GOTOFF
-# endif
-               case R_390_GOTOFF32:
-                       *loc += v - got;
-                       break;
-
-#elif defined(__sh__)
-
-               case R_SH_NONE:
-                       break;
-
-               case R_SH_DIR32:
-                       *loc += v;
-                       break;
-
-               case R_SH_REL32:
-                       *loc += v - dot;
-                       break;
-
-               case R_SH_PLT32:
-                       *loc = v - dot;
-                       break;
-
-               case R_SH_GLOB_DAT:
-               case R_SH_JMP_SLOT:
-                       *loc = v;
-                       break;
-
-               case R_SH_RELATIVE:
-                       *loc = f->baseaddr + rel->r_addend;
-                       break;
-
-               case R_SH_GOTPC:
-                       *loc = got - dot + rel->r_addend;
-                       break;
-
-               case R_SH_GOT32:
-                       goto bb_use_got;
-
-               case R_SH_GOTOFF:
-                       *loc = v - got;
-                       break;
-
-# if defined(__SH5__)
-               case R_SH_IMM_MEDLOW16:
-               case R_SH_IMM_LOW16:
-                       {
-                               ElfW(Addr) word;
-
-                               if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
-                                       v >>= 16;
-
-                               /*
-                                *  movi and shori have the format:
-                                *
-                                *  |  op  | imm  | reg | reserved |
-                                *   31..26 25..10 9.. 4 3   ..   0
-                                *
-                                * so we simply mask and or in imm.
-                                */
-                               word = *loc & ~0x3fffc00;
-                               word |= (v & 0xffff) << 10;
-
-                               *loc = word;
-
-                               break;
-                       }
-
-               case R_SH_IMM_MEDLOW16_PCREL:
-               case R_SH_IMM_LOW16_PCREL:
-                       {
-                               ElfW(Addr) word;
-
-                               word = *loc & ~0x3fffc00;
-
-                               v -= dot;
-
-                               if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
-                                       v >>= 16;
-
-                               word |= (v & 0xffff) << 10;
-
-                               *loc = word;
-
-                               break;
-                       }
-# endif /* __SH5__ */
-
-#elif defined(__v850e__)
-
-               case R_V850_NONE:
-                       break;
-
-               case R_V850_32:
-                       /* We write two shorts instead of a long because even
-                          32-bit insns only need half-word alignment, but
-                          32-bit data needs to be long-word aligned.  */
-                       v += ((unsigned short *)loc)[0];
-                       v += ((unsigned short *)loc)[1] << 16;
-                       ((unsigned short *)loc)[0] = v & 0xffff;
-                       ((unsigned short *)loc)[1] = (v >> 16) & 0xffff;
-                       break;
-
-               case R_V850_22_PCREL:
-                       goto bb_use_plt;
-
-#elif defined(__x86_64__)
-
-               case R_X86_64_NONE:
-                       break;
-
-               case R_X86_64_64:
-                       *loc += v;
-                       break;
-
-               case R_X86_64_32:
-                       *(unsigned int *) loc += v;
-                       if (v > 0xffffffff)
-                       {
-                               ret = obj_reloc_overflow; /* Kernel module compiled without -mcmodel=kernel. */
-                               /* error("Possibly is module compiled without -mcmodel=kernel!"); */
-                       }
-                       break;
-
-               case R_X86_64_32S:
-                       *(signed int *) loc += v;
-                       break;
-
-               case R_X86_64_16:
-                       *(unsigned short *) loc += v;
-                       break;
-
-               case R_X86_64_8:
-                       *(unsigned char *) loc += v;
-                       break;
-
-               case R_X86_64_PC32:
-                       *(unsigned int *) loc += v - dot;
-                       break;
-
-               case R_X86_64_PC16:
-                       *(unsigned short *) loc += v - dot;
-                       break;
-
-               case R_X86_64_PC8:
-                       *(unsigned char *) loc += v - dot;
-                       break;
-
-               case R_X86_64_GLOB_DAT:
-               case R_X86_64_JUMP_SLOT:
-                       *loc = v;
-                       break;
-
-               case R_X86_64_RELATIVE:
-                       *loc += f->baseaddr;
-                       break;
-
-               case R_X86_64_GOT32:
-               case R_X86_64_GOTPCREL:
-                       goto bb_use_got;
-# if 0
-                       if (!isym->gotent.reloc_done)
-                       {
-                               isym->gotent.reloc_done = 1;
-                               *(Elf64_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
-                       }
-                       /* XXX are these really correct?  */
-                       if (ELF64_R_TYPE(rel->r_info) == R_X86_64_GOTPCREL)
-                               *(unsigned int *) loc += v + isym->gotent.offset;
-                       else
-                               *loc += isym->gotent.offset;
-                       break;
-# endif
-
-#else
-# warning "no idea how to handle relocations on your arch"
-#endif
-
-               default:
-                       printf("Warning: unhandled reloc %d\n",(int)ELF_R_TYPE(rel->r_info));
-                       ret = obj_reloc_unhandled;
-                       break;
-
-#if defined(USE_PLT_ENTRIES)
-
-bb_use_plt:
-
-                       /* find the plt entry and initialize it if necessary */
-
-#if defined(USE_PLT_LIST)
-                       for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;)
-                               pe = pe->next;
-#else
-                       pe = &isym->pltent;
-#endif
-
-                       if (! pe->inited) {
-                               ip = (unsigned long *) (ifile->plt->contents + pe->offset);
-
-                               /* generate some machine code */
-
-#if defined(__arm__)
-                               ip[0] = 0xe51ff004;                     /* ldr pc,[pc,#-4] */
-                               ip[1] = v;                              /* sym@ */
-#endif
-#if defined(__powerpc__)
-                               ip[0] = 0x3d600000 + ((v + 0x8000) >> 16);  /* lis r11,sym@ha */
-                               ip[1] = 0x396b0000 + (v & 0xffff);          /* addi r11,r11,sym@l */
-                               ip[2] = 0x7d6903a6;                           /* mtctr r11 */
-                               ip[3] = 0x4e800420;                           /* bctr */
-#endif
-#if defined(__v850e__)
-                               /* We have to trash a register, so we assume that any control
-                                  transfer more than 21-bits away must be a function call
-                                  (so we can use a call-clobbered register).  */
-                               ip[0] = 0x0621 + ((v & 0xffff) << 16);   /* mov sym, r1 ... */
-                               ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
-#endif
-                               pe->inited = 1;
-                       }
-
-                       /* relative distance to target */
-                       v -= dot;
-                       /* if the target is too far away.... */
-#if defined(__arm__) || defined(__powerpc__)
-                       if ((int)v < -0x02000000 || (int)v >= 0x02000000)
-#elif defined(__v850e__)
-                               if ((ElfW(Sword))v > 0x1fffff || (ElfW(Sword))v < (ElfW(Sword))-0x200000)
-#endif
-                                       /* go via the plt */
-                                       v = plt + pe->offset - dot;
-
-#if defined(__v850e__)
-                       if (v & 1)
-#else
-                               if (v & 3)
-#endif
-                                       ret = obj_reloc_dangerous;
-
-                       /* merge the offset into the instruction. */
-#if defined(__arm__)
-                       /* Convert to words. */
-                       v >>= 2;
-
-                       *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff);
-#endif
-#if defined(__powerpc__)
-                       *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
-#endif
-#if defined(__v850e__)
-                       /* We write two shorts instead of a long because even 32-bit insns
-                          only need half-word alignment, but the 32-bit data write needs
-                          to be long-word aligned.  */
-                       ((unsigned short *)loc)[0] =
-                               (*(unsigned short *)loc & 0xffc0) /* opcode + reg */
-                               | ((v >> 16) & 0x3f);             /* offs high part */
-                       ((unsigned short *)loc)[1] =
-                               (v & 0xffff);                    /* offs low part */
-#endif
-                       break;
-#endif /* USE_PLT_ENTRIES */
-
-#if defined(USE_GOT_ENTRIES)
-bb_use_got:
-
-                       /* needs an entry in the .got: set it, once */
-                       if (!isym->gotent.inited) {
-                               isym->gotent.inited = 1;
-                               *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v;
-                       }
-                       /* make the reloc with_respect_to_.got */
-#if defined(__sh__)
-                       *loc += isym->gotent.offset + rel->r_addend;
-#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__)
-                       *loc += isym->gotent.offset;
-#endif
-                       break;
-
-#endif /* USE_GOT_ENTRIES */
-       }
-
-       return ret;
-}
-
-
-#if defined(USE_LIST)
-
-static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list,
-                         int offset, int size)
-{
-       struct arch_list_entry *pe;
-
-       for (pe = *list; pe != NULL; pe = pe->next) {
-               if (pe->addend == rel->r_addend) {
-                       break;
-               }
-       }
-
-       if (pe == NULL) {
-               pe = xmalloc(sizeof(struct arch_list_entry));
-               pe->next = *list;
-               pe->addend = rel->r_addend;
-               pe->offset = offset;
-               pe->inited = 0;
-               *list = pe;
-               return size;
-       }
-       return 0;
-}
-
-#endif
-
-#if defined(USE_SINGLE)
-
-static int arch_single_init(/*ElfW(RelM) *rel,*/ struct arch_single_entry *single,
-                            int offset, int size)
-{
-       if (single->allocated == 0) {
-               single->allocated = 1;
-               single->offset = offset;
-               single->inited = 0;
-               return size;
-       }
-       return 0;
-}
-
-#endif
-
-#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
-
-static struct obj_section *arch_xsect_init(struct obj_file *f, const char *name,
-                                          int offset, int size)
-{
-       struct obj_section *myrelsec = obj_find_section(f, name);
-
-       if (offset == 0) {
-               offset += size;
-       }
-
-       if (myrelsec) {
-               obj_extend_section(myrelsec, offset);
-       } else {
-               myrelsec = obj_create_alloced_section(f, name,
-                               size, offset);
-       }
-
-       return myrelsec;
-}
-
-#endif
-
-static void arch_create_got(struct obj_file *f)
-{
-#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
-       struct arch_file *ifile = (struct arch_file *) f;
-       int i;
-#if defined(USE_GOT_ENTRIES)
-       int got_offset = 0, got_needed = 0, got_allocate;
-#endif
-#if defined(USE_PLT_ENTRIES)
-       int plt_offset = 0, plt_needed = 0, plt_allocate;
-#endif
-       struct obj_section *relsec, *symsec, *strsec;
-       ElfW(RelM) *rel, *relend;
-       ElfW(Sym) *symtab, *extsym;
-       const char *strtab, *name;
-       struct arch_symbol *intsym;
-
-       for (i = 0; i < f->header.e_shnum; ++i) {
-               relsec = f->sections[i];
-               if (relsec->header.sh_type != SHT_RELM)
-                       continue;
-
-               symsec = f->sections[relsec->header.sh_link];
-               strsec = f->sections[symsec->header.sh_link];
-
-               rel = (ElfW(RelM) *) relsec->contents;
-               relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
-               symtab = (ElfW(Sym) *) symsec->contents;
-               strtab = (const char *) strsec->contents;
-
-               for (; rel < relend; ++rel) {
-                       extsym = &symtab[ELF_R_SYM(rel->r_info)];
-
-#if defined(USE_GOT_ENTRIES)
-                       got_allocate = 0;
-#endif
-#if defined(USE_PLT_ENTRIES)
-                       plt_allocate = 0;
-#endif
-
-                       switch (ELF_R_TYPE(rel->r_info)) {
-#if defined(__arm__)
-                       case R_ARM_PC24:
-                       case R_ARM_PLT32:
-                               plt_allocate = 1;
-                               break;
-
-                       case R_ARM_GOTOFF:
-                       case R_ARM_GOTPC:
-                               got_needed = 1;
-                               continue;
-
-                       case R_ARM_GOT32:
-                               got_allocate = 1;
-                               break;
-
-#elif defined(__i386__)
-                       case R_386_GOTPC:
-                       case R_386_GOTOFF:
-                               got_needed = 1;
-                               continue;
-
-                       case R_386_GOT32:
-                               got_allocate = 1;
-                               break;
-
-#elif defined(__powerpc__)
-                       case R_PPC_REL24:
-                               plt_allocate = 1;
-                               break;
-
-#elif defined(__mc68000__)
-                       case R_68K_GOT32:
-                               got_allocate = 1;
-                               break;
-
-#ifdef R_68K_GOTOFF
-                       case R_68K_GOTOFF:
-                               got_needed = 1;
-                               continue;
-#endif
-
-#elif defined(__sh__)
-                       case R_SH_GOT32:
-                               got_allocate = 1;
-                               break;
-
-                       case R_SH_GOTPC:
-                       case R_SH_GOTOFF:
-                               got_needed = 1;
-                               continue;
-
-#elif defined(__v850e__)
-                       case R_V850_22_PCREL:
-                               plt_needed = 1;
-                               break;
-
-#endif
-                       default:
-                               continue;
-                       }
-
-                       if (extsym->st_name != 0) {
-                               name = strtab + extsym->st_name;
-                       } else {
-                               name = f->sections[extsym->st_shndx]->name;
-                       }
-                       intsym = (struct arch_symbol *) obj_find_symbol(f, name);
-#if defined(USE_GOT_ENTRIES)
-                       if (got_allocate) {
-                               got_offset += arch_single_init(
-                                               /*rel,*/ &intsym->gotent,
-                                               got_offset, GOT_ENTRY_SIZE);
-
-                               got_needed = 1;
-                       }
-#endif
-#if defined(USE_PLT_ENTRIES)
-                       if (plt_allocate) {
-#if defined(USE_PLT_LIST)
-                               plt_offset += arch_list_add(
-                                               rel, &intsym->pltent,
-                                               plt_offset, PLT_ENTRY_SIZE);
-#else
-                               plt_offset += arch_single_init(
-                                               /*rel,*/ &intsym->pltent,
-                                               plt_offset, PLT_ENTRY_SIZE);
-#endif
-                               plt_needed = 1;
-                       }
-#endif
-               }
-       }
-
-#if defined(USE_GOT_ENTRIES)
-       if (got_needed) {
-               ifile->got = arch_xsect_init(f, ".got", got_offset,
-                               GOT_ENTRY_SIZE);
-       }
-#endif
-
-#if defined(USE_PLT_ENTRIES)
-       if (plt_needed) {
-               ifile->plt = arch_xsect_init(f, ".plt", plt_offset,
-                               PLT_ENTRY_SIZE);
-       }
-#endif
-
-#endif /* defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) */
-}
-
-/*======================================================================*/
-
-/* Standard ELF hash function.  */
-static unsigned long obj_elf_hash_n(const char *name, unsigned long n)
-{
-       unsigned long h = 0;
-       unsigned long g;
-       unsigned char ch;
-
-       while (n > 0) {
-               ch = *name++;
-               h = (h << 4) + ch;
-               g = (h & 0xf0000000);
-               if (g != 0) {
-                       h ^= g >> 24;
-                       h &= ~g;
-               }
-               n--;
-       }
-       return h;
-}
-
-static unsigned long obj_elf_hash(const char *name)
-{
-       return obj_elf_hash_n(name, strlen(name));
-}
-
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-/* String comparison for non-co-versioned kernel and module.  */
-
-static int ncv_strcmp(const char *a, const char *b)
-{
-       size_t alen = strlen(a), blen = strlen(b);
-
-       if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R')
-               return strncmp(a, b, alen);
-       else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R')
-               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 && str[len - 10] == '_' && str[len - 9] == 'R')
-               len -= 10;
-       return obj_elf_hash_n(str, len);
-}
-
-static void
-obj_set_symbol_compare(struct obj_file *f,
-                                          int (*cmp) (const char *, const char *),
-                                          unsigned long (*hash) (const char *))
-{
-       if (cmp)
-               f->symbol_cmp = cmp;
-       if (hash) {
-               struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
-               int i;
-
-               f->symbol_hash = hash;
-
-               memcpy(tmptab, f->symtab, sizeof(tmptab));
-               memset(f->symtab, 0, sizeof(f->symtab));
-
-               for (i = 0; i < HASH_BUCKETS; ++i)
-                       for (sym = tmptab[i]; sym; sym = next) {
-                               unsigned long h = hash(sym->name) % HASH_BUCKETS;
-                               next = sym->next;
-                               sym->next = f->symtab[h];
-                               f->symtab[h] = sym;
-                       }
-       }
-}
-
-#endif /* FEATURE_INSMOD_VERSION_CHECKING */
-
-static struct obj_symbol *
-obj_add_symbol(struct obj_file *f, const char *name,
-                               unsigned long symidx, int info,
-                               int secidx, ElfW(Addr) value,
-                               unsigned long size)
-{
-       struct obj_symbol *sym;
-       unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
-       int n_type = ELF_ST_TYPE(info);
-       int n_binding = ELF_ST_BIND(info);
-
-       for (sym = f->symtab[hash]; sym; sym = sym->next) {
-               if (f->symbol_cmp(sym->name, name) == 0) {
-                       int o_secidx = sym->secidx;
-                       int o_info = sym->info;
-                       int o_type = ELF_ST_TYPE(o_info);
-                       int o_binding = ELF_ST_BIND(o_info);
-
-                       /* A redefinition!  Is it legal?  */
-
-                       if (secidx == SHN_UNDEF)
-                               return sym;
-                       else if (o_secidx == SHN_UNDEF)
-                               goto found;
-                       else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) {
-                               /* Cope with local and global symbols of the same name
-                                  in the same object file, as might have been created
-                                  by ld -r.  The only reason locals are now seen at this
-                                  level at all is so that we can do semi-sensible things
-                                  with parameters.  */
-
-                               struct obj_symbol *nsym, **p;
-
-                               nsym = arch_new_symbol();
-                               nsym->next = sym->next;
-                               nsym->ksymidx = -1;
-
-                               /* Excise the old (local) symbol from the hash chain.  */
-                               for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
-                                       continue;
-                               *p = sym = nsym;
-                               goto found;
-                       } else if (n_binding == STB_LOCAL) {
-                               /* Another symbol of the same name has already been defined.
-                                  Just add this to the local table.  */
-                               sym = arch_new_symbol();
-                               sym->next = NULL;
-                               sym->ksymidx = -1;
-                               f->local_symtab[symidx] = sym;
-                               goto found;
-                       } else if (n_binding == STB_WEAK)
-                               return sym;
-                       else if (o_binding == STB_WEAK)
-                               goto found;
-                       /* Don't unify COMMON symbols with object types the programmer
-                          doesn't expect.  */
-                       else if (secidx == SHN_COMMON
-                                       && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
-                               return sym;
-                       else if (o_secidx == SHN_COMMON
-                                       && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
-                               goto found;
-                       else {
-                               /* Don't report an error if the symbol is coming from
-                                  the kernel or some external module.  */
-                               if (secidx <= SHN_HIRESERVE)
-                                       bb_error_msg("%s multiply defined", name);
-                               return sym;
-                       }
-               }
-       }
-
-       /* Completely new symbol.  */
-       sym = arch_new_symbol();
-       sym->next = f->symtab[hash];
-       f->symtab[hash] = sym;
-       sym->ksymidx = -1;
-       if (ELF_ST_BIND(info) == STB_LOCAL && symidx != (unsigned long)(-1)) {
-               if (symidx >= f->local_symtab_size)
-                       bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
-                                       name, (long) symidx, (long) f->local_symtab_size);
-               else
-                       f->local_symtab[symidx] = sym;
-       }
-
-found:
-       sym->name = name;
-       sym->value = value;
-       sym->size = size;
-       sym->secidx = secidx;
-       sym->info = info;
-
-       return sym;
-}
-
-static struct obj_symbol *
-obj_find_symbol(struct obj_file *f, const char *name)
-{
-       struct obj_symbol *sym;
-       unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
-
-       for (sym = f->symtab[hash]; sym; sym = sym->next)
-               if (f->symbol_cmp(sym->name, name) == 0)
-                       return sym;
-       return NULL;
-}
-
-static ElfW(Addr) obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym)
-{
-       if (sym) {
-               if (sym->secidx >= SHN_LORESERVE)
-                       return sym->value;
-               return sym->value + f->sections[sym->secidx]->header.sh_addr;
-       }
-       /* As a special case, a NULL sym has value zero.  */
-       return 0;
-}
-
-static struct obj_section *obj_find_section(struct obj_file *f, const char *name)
-{
-       int i, n = f->header.e_shnum;
-
-       for (i = 0; i < n; ++i)
-               if (strcmp(f->sections[i]->name, name) == 0)
-                       return f->sections[i];
-       return NULL;
-}
-
-static int obj_load_order_prio(struct obj_section *a)
-{
-       unsigned long af, ac;
-
-       af = a->header.sh_flags;
-
-       ac = 0;
-       if (a->name[0] != '.' || strlen(a->name) != 10
-        || strcmp(a->name + 5, ".init") != 0
-       ) {
-               ac |= 32;
-       }
-       if (af & SHF_ALLOC)
-               ac |= 16;
-       if (!(af & SHF_WRITE))
-               ac |= 8;
-       if (af & SHF_EXECINSTR)
-               ac |= 4;
-       if (a->header.sh_type != SHT_NOBITS)
-               ac |= 2;
-
-       return ac;
-}
-
-static void
-obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec)
-{
-       struct obj_section **p;
-       int prio = obj_load_order_prio(sec);
-       for (p = f->load_order_search_start; *p; p = &(*p)->load_next)
-               if (obj_load_order_prio(*p) < prio)
-                       break;
-       sec->load_next = *p;
-       *p = sec;
-}
-
-static struct obj_section *obj_create_alloced_section(struct obj_file *f,
-                               const char *name,
-                               unsigned long align,
-                               unsigned long size)
-{
-       int newidx = f->header.e_shnum++;
-       struct obj_section *sec;
-
-       f->sections = xrealloc_vector(f->sections, 2, newidx);
-       f->sections[newidx] = sec = arch_new_section();
-
-       sec->header.sh_type = SHT_PROGBITS;
-       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
-       sec->header.sh_size = size;
-       sec->header.sh_addralign = align;
-       sec->name = name;
-       sec->idx = newidx;
-       if (size)
-               sec->contents = xzalloc(size);
-
-       obj_insert_section_load_order(f, sec);
-
-       return sec;
-}
-
-static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
-                               const char *name,
-                               unsigned long align,
-                               unsigned long size)
-{
-       int newidx = f->header.e_shnum++;
-       struct obj_section *sec;
-
-       f->sections = xrealloc_vector(f->sections, 2, newidx);
-       f->sections[newidx] = sec = arch_new_section();
-
-       sec->header.sh_type = SHT_PROGBITS;
-       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
-       sec->header.sh_size = size;
-       sec->header.sh_addralign = align;
-       sec->name = name;
-       sec->idx = newidx;
-       if (size)
-               sec->contents = xzalloc(size);
-
-       sec->load_next = f->load_order;
-       f->load_order = sec;
-       if (f->load_order_search_start == &f->load_order)
-               f->load_order_search_start = &sec->load_next;
-
-       return sec;
-}
-
-static void *obj_extend_section(struct obj_section *sec, unsigned long more)
-{
-       unsigned long oldsize = sec->header.sh_size;
-       if (more) {
-               sec->header.sh_size += more;
-               sec->contents = xrealloc(sec->contents, sec->header.sh_size);
-       }
-       return sec->contents + oldsize;
-}
-
-
-/* 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 new_module_symbol *syms, size_t nsyms)
-{
-       struct new_module_symbol *s;
-       size_t i;
-       int used = 0;
-#ifdef SYMBOL_PREFIX
-       char *name_buf = 0;
-       size_t name_alloced_size = 0;
-#endif
-#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
-       int gpl;
-
-       gpl = obj_gpl_license(f, NULL) == 0;
-#endif
-       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;
-               char *name;
-
-               /* GPL licensed modules can use symbols exported with
-                * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
-                * exported names.  Non-GPL modules never see any GPLONLY_
-                * symbols so they cannot fudge it by adding the prefix on
-                * their references.
-                */
-               if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
-#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
-                       if (gpl)
-                               s->name += 8;
-                       else
-#endif
-                               continue;
-               }
-               name = (char *)s->name;
-
-#ifdef SYMBOL_PREFIX
-               /* Prepend SYMBOL_PREFIX to the symbol's name (the
-                  kernel exports `C names', but module object files
-                  reference `linker names').  */
-               size_t extra = sizeof SYMBOL_PREFIX;
-               size_t name_size = strlen(name) + extra;
-               if (name_size > name_alloced_size) {
-                       name_alloced_size = name_size * 2;
-                       name_buf = alloca(name_alloced_size);
-               }
-               strcpy(name_buf, SYMBOL_PREFIX);
-               strcpy(name_buf + extra - 1, name);
-               name = name_buf;
-#endif /* SYMBOL_PREFIX */
-
-               sym = obj_find_symbol(f, name);
-               if (sym && !(ELF_ST_BIND(sym->info) == STB_LOCAL)) {
-#ifdef SYMBOL_PREFIX
-                       /* Put NAME_BUF into more permanent storage.  */
-                       name = xmalloc(name_size);
-                       strcpy(name, name_buf);
-#endif
-                       sym = obj_add_symbol(f, name, -1,
-                                       ELF_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 external_module *m;
-       int i, nused = 0;
-
-       /* Add module symbols first.  */
-
-       for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m) {
-               if (m->nsyms
-                && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms)
-               ) {
-                       m->used = 1;
-                       ++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 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 (p + klen == v && strncmp(p, key, klen) == 0)
-                               return v + 1;
-               } else {
-                       if (p + klen == n && strcmp(p, key) == 0)
-                               return n;
-               }
-               p = n + 1;
-       }
-
-       return NULL;
-}
-
-
-/*======================================================================*/
-/* Functions relating to module loading after 2.1.18.  */
-
-static void
-new_process_module_arguments(struct obj_file *f, int argc, char **argv)
-{
-       while (argc > 0) {
-               char *p, *q, *key, *sym_name;
-               struct obj_symbol *sym;
-               char *contents, *loc;
-               int min, max, n;
-
-               p = *argv;
-               q = strchr(p, '=');
-               if (q == NULL) {
-                       argc--;
-                       continue;
-               }
-
-               key = alloca(q - p + 6);
-               memcpy(key, "parm_", 5);
-               memcpy(key + 5, p, q - p);
-               key[q - p + 5] = 0;
-
-               p = get_modinfo_value(f, key);
-               key += 5;
-               if (p == NULL) {
-                       bb_error_msg_and_die("invalid parameter %s", key);
-               }
-
-#ifdef SYMBOL_PREFIX
-               sym_name = alloca(strlen(key) + sizeof SYMBOL_PREFIX);
-               strcpy(sym_name, SYMBOL_PREFIX);
-               strcat(sym_name, key);
-#else
-               sym_name = key;
-#endif
-               sym = obj_find_symbol(f, sym_name);
-
-               /* Also check that the parameter was not resolved from the kernel.  */
-               if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
-                       bb_error_msg_and_die("symbol for parameter %s not found", key);
-               }
-
-               if (isdigit(*p)) {
-                       min = strtoul(p, &p, 10);
-                       if (*p == '-')
-                               max = strtoul(p + 1, &p, 10);
-                       else
-                               max = min;
-               } else
-                       min = max = 1;
-
-               contents = f->sections[sym->secidx]->contents;
-               loc = contents + sym->value;
-               n = (*++q != '\0');
-
-               while (1) {
-                       if ((*p == 's') || (*p == 'c')) {
-                               char *str;
-
-                               /* Do C quoting if we begin with a ", else slurp the lot.  */
-                               if (*q == '"') {
-                                       char *r;
-
-                                       str = alloca(strlen(q));
-                                       for (r = str, q++; *q != '"'; ++q, ++r) {
-                                               if (*q == '\0')
-                                                       bb_error_msg_and_die("improperly terminated string argument for %s",
-                                                                       key);
-                                               if (*q == '\\')
-                                                       switch (*++q) {
-                                                       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':
-                                                               {
-                                                                       int c = *q - '0';
-                                                                       if (q[1] >= '0' && q[1] <= '7') {
-                                                                               c = (c * 8) + *++q - '0';
-                                                                               if (q[1] >= '0' && q[1] <= '7')
-                                                                                       c = (c * 8) + *++q - '0';
-                                                                       }
-                                                                       *r = c;
-                                                               }
-                                                               break;
-
-                                                       default:
-                                                               *r = *q;
-                                                               break;
-                                                       }
-                                               else
-                                                       *r = *q;
-                                       }
-                                       *r = '\0';
-                                       ++q;
-                               } else {
-                                       char *r;
-
-                                       /* In this case, the string is not quoted. We will break
-                                          it using the coma (like for ints). If the user wants to
-                                          include comas in a string, he just has to quote it */
-
-                                       /* Search the next coma */
-                                       r = strchr(q, ',');
-
-                                       /* Found ? */
-                                       if (r != (char *) NULL) {
-                                               /* Recopy the current field */
-                                               str = alloca(r - q + 1);
-                                               memcpy(str, q, r - q);
-
-                                               /* I don't know if it is useful, as the previous case
-                                                  doesn't nul terminate the string ??? */
-                                               str[r - q] = '\0';
-
-                                               /* Keep next fields */
-                                               q = r;
-                                       } else {
-                                               /* last string */
-                                               str = q;
-                                               q = (char*)"";
-                                       }
-                               }
-
-                               if (*p == 's') {
-                                       /* Normal string */
-                                       obj_string_patch(f, sym->secidx, loc - contents, str);
-                                       loc += tgt_sizeof_char_p;
-                               } else {
-                                       /* Array of chars (in fact, matrix!) */
-                                       unsigned long charssize;        /* size of each member */
-
-                                       /* Get the size of each member */
-                                       /* Probably we should do that outside the loop ? */
-                                       if (!isdigit(*(p + 1))) {
-                                               bb_error_msg_and_die("parameter type 'c' for %s must be followed by"
-                                                               " the maximum size", key);
-                                       }
-                                       charssize = strtoul(p + 1, (char **) NULL, 10);
-
-                                       /* Check length */
-                                       if (strlen(str) >= charssize) {
-                                               bb_error_msg_and_die("string too long for %s (max %ld)", key,
-                                                               charssize - 1);
-                                       }
-
-                                       /* Copy to location */
-                                       strcpy((char *) loc, str);
-                                       loc += charssize;
-                               }
-                       } else {
-                               long v = strtoul(q, &q, 0);
-                               switch (*p) {
-                               case 'b':
-                                       *loc++ = v;
-                                       break;
-                               case 'h':
-                                       *(short *) loc = v;
-                                       loc += tgt_sizeof_short;
-                                       break;
-                               case 'i':
-                                       *(int *) loc = v;
-                                       loc += tgt_sizeof_int;
-                                       break;
-                               case 'l':
-                                       *(long *) loc = v;
-                                       loc += tgt_sizeof_long;
-                                       break;
-
-                               default:
-                                       bb_error_msg_and_die("unknown parameter type '%c' for %s", *p, key);
-                               }
-                       }
- retry_end_of_value:
-                       switch (*q) {
-                       case '\0':
-                               goto end_of_arg;
-
-                       case ' ':
-                       case '\t':
-                       case '\n':
-                       case '\r':
-                               ++q;
-                               goto retry_end_of_value;
-
-                       case ',':
-                               if (++n > max) {
-                                       bb_error_msg_and_die("too many values for %s (max %d)", key, max);
-                               }
-                               ++q;
-                               break;
-
-                       default:
-                               bb_error_msg_and_die("invalid argument syntax for %s", key);
-                       }
-               }
- end_of_arg:
-               if (n < min) {
-                       bb_error_msg_and_die("too few values for %s (min %d)", key, min);
-               }
-
-               argc--;
-               argv++;
-       }
-}
-
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-static int new_is_module_checksummed(struct obj_file *f)
-{
-       const char *p = get_modinfo_value(f, "using_checksums");
-       if (p)
-               return xatoi(p);
-       return 0;
-}
-
-/* Get the module's kernel version in the canonical integer form.  */
-
-static int
-new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
-{
-       char *p, *q;
-       int a, b, c;
-
-       p = get_modinfo_value(f, "kernel_version");
-       if (p == NULL)
-               return -1;
-       safe_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;
-}
-
-#endif   /* FEATURE_INSMOD_VERSION_CHECKING */
-
-
-/* Fetch the loaded modules, and all currently exported symbols.  */
-
-static void new_get_kernel_symbols(void)
-{
-       char *module_names, *mn;
-       struct external_module *modules, *m;
-       struct new_module_symbol *syms, *s;
-       size_t ret, bufsize, nmod, nsyms, i, j;
-
-       /* Collect the loaded modules.  */
-
-       bufsize = 256;
-       module_names = xmalloc(bufsize);
-
- retry_modules_load:
-       if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
-               if (errno == ENOSPC && bufsize < ret) {
-                       bufsize = ret;
-                       module_names = xrealloc(module_names, bufsize);
-                       goto retry_modules_load;
-               }
-               bb_perror_msg_and_die("QM_MODULES");
-       }
-
-       n_ext_modules = nmod = ret;
-
-       /* Collect the modules' symbols.  */
-
-       if (nmod) {
-               ext_modules = modules = xzalloc(nmod * sizeof(*modules));
-               for (i = 0, mn = module_names, m = modules;
-                               i < nmod; ++i, ++m, mn += strlen(mn) + 1) {
-                       struct new_module_info info;
-
-                       if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) {
-                               if (errno == ENOENT) {
-                                       /* The module was removed out from underneath us.  */
-                                       continue;
-                               }
-                               bb_perror_msg_and_die("query_module: QM_INFO: %s", mn);
-                       }
-
-                       bufsize = 1024;
-                       syms = xmalloc(bufsize);
- retry_mod_sym_load:
-                       if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) {
-                               switch (errno) {
-                                       case ENOSPC:
-                                               bufsize = ret;
-                                               syms = xrealloc(syms, bufsize);
-                                               goto retry_mod_sym_load;
-                                       case ENOENT:
-                                               /* The module was removed out from underneath us.  */
-                                               continue;
-                                       default:
-                                               bb_perror_msg_and_die("query_module: QM_SYMBOLS: %s", mn);
-                               }
-                       }
-                       nsyms = ret;
-
-                       m->name = mn;
-                       m->addr = info.addr;
-                       m->nsyms = nsyms;
-                       m->syms = syms;
-
-                       for (j = 0, s = syms; j < nsyms; ++j, ++s) {
-                               s->name += (unsigned long) syms;
-                       }
-               }
-       }
-
-       /* Collect the kernel's symbols.  */
-
-       syms = xmalloc(bufsize = 16 * 1024);
- retry_kern_sym_load:
-       if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) {
-               if (errno == ENOSPC && bufsize < ret) {
-                       bufsize = ret;
-                       syms = xrealloc(syms, bufsize);
-                       goto retry_kern_sym_load;
-               }
-               bb_perror_msg_and_die("kernel: QM_SYMBOLS");
-       }
-       nksyms = nsyms = ret;
-       ksyms = syms;
-
-       for (j = 0, s = syms; j < nsyms; ++j, ++s) {
-               s->name += (unsigned long) syms;
-       }
-}
-
-
-/* Return the kernel symbol checksum version, or zero if not used.  */
-
-static int new_is_kernel_checksummed(void)
-{
-       struct new_module_symbol *s;
-       size_t i;
-
-       /* Using_Versions is not 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 void new_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 new_module));
-       /* done by obj_create_alloced_section_first: */
-       /*memset(sec->contents, 0, sizeof(struct new_module));*/
-
-       obj_add_symbol(f, SPFX "__this_module", -1,
-                       ELF_ST_INFO(STB_LOCAL, STT_OBJECT), sec->idx, 0,
-                       sizeof(struct new_module));
-
-       obj_string_patch(f, sec->idx, offsetof(struct new_module, name),
-                       m_name);
-}
-
-#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
-/* add an entry to the __ksymtab section, creating it if necessary */
-static void new_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;
-       /* Empty section might be byte-aligned */
-       sec->header.sh_addralign = tgt_sizeof_void_p;
-       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);
-}
-#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
-
-static int new_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 new_module_ref *dep;
-               struct obj_symbol *tm;
-
-               sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
-                               (sizeof(struct new_module_ref)
-                                * n_ext_modules_used));
-               if (!sec)
-                       return 0;
-
-               tm = obj_find_symbol(f, SPFX "__this_module");
-               dep = (struct new_module_ref *) sec->contents;
-               for (i = 0; i < n_ext_modules; ++i)
-                       if (ext_modules[i].used) {
-                               dep->dep = ext_modules[i].addr;
-                               obj_symbol_patch(f, sec->idx,
-                                               (char *) &dep->ref - sec->contents, tm);
-                               dep->next_ref = 0;
-                               ++dep;
-                       }
-       }
-
-       if (!flag_noexport && !obj_find_section(f, "__ksymtab")) {
-               size_t nsyms;
-               int *loaded;
-
-               sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);
-
-               /* 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.  */
-               i = f->header.e_shnum;
-               loaded = alloca(sizeof(int) * i);
-               while (--i >= 0)
-                       loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
-
-               for (nsyms = i = 0; i < HASH_BUCKETS; ++i) {
-                       struct obj_symbol *sym;
-                       for (sym = f->symtab[i]; sym; sym = sym->next) {
-                               if (ELF_ST_BIND(sym->info) != STB_LOCAL
-                                               && sym->secidx <= SHN_HIRESERVE
-                                               && (sym->secidx >= SHN_LORESERVE
-                                                       || loaded[sym->secidx])
-                               ) {
-                                       ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p;
-
-                                       obj_symbol_patch(f, sec->idx, ofs, sym);
-                                       obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p,
-                                                       sym->name);
-
-                                       nsyms++;
-                               }
-                       }
-               }
-
-               obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p);
-       }
-
-       return 1;
-}
-
-
-static int
-new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
-{
-       struct new_module *module;
-       struct obj_section *sec;
-       void *image;
-       int ret;
-       tgt_long m_addr;
-
-       sec = obj_find_section(f, ".this");
-       if (!sec || !sec->contents) {
-               bb_perror_msg_and_die("corrupt module %s?", m_name);
-       }
-       module = (struct new_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, SPFX "init_module"));
-       module->cleanup =
-               obj_symbol_final_value(f, obj_find_symbol(f, SPFX "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 = (void*)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 = (void*)sec->header.sh_addr;
-               module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
-       }
-
-       /* 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);
-
-       ret = init_module(m_name, (struct new_module *) image);
-       if (ret)
-               bb_perror_msg("init_module: %s", m_name);
-
-       free(image);
-
-       return ret == 0;
-}
-
-
-/*======================================================================*/
-
-static void
-obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
-                                const char *string)
-{
-       struct obj_string_patch *p;
-       struct obj_section *strsec;
-       size_t len = strlen(string) + 1;
-       char *loc;
-
-       p = xmalloc(sizeof(*p));
-       p->next = f->string_patches;
-       p->reloc_secidx = secidx;
-       p->reloc_offset = offset;
-       f->string_patches = p;
-
-       strsec = obj_find_section(f, ".kstrtab");
-       if (strsec == NULL) {
-               strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);
-               p->string_offset = 0;
-               loc = strsec->contents;
-       } else {
-               p->string_offset = strsec->header.sh_size;
-               loc = obj_extend_section(strsec, len);
-       }
-       memcpy(loc, string, len);
-}
-
-static void
-obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
-                                struct obj_symbol *sym)
-{
-       struct obj_symbol_patch *p;
-
-       p = xmalloc(sizeof(*p));
-       p->next = f->symbol_patches;
-       p->reloc_secidx = secidx;
-       p->reloc_offset = offset;
-       p->sym = sym;
-       f->symbol_patches = p;
-}
-
-static void obj_check_undefineds(struct obj_file *f)
-{
-       unsigned i;
-
-       for (i = 0; i < HASH_BUCKETS; ++i) {
-               struct obj_symbol *sym;
-               for (sym = f->symtab[i]; sym; sym = sym->next)
-                       if (sym->secidx == SHN_UNDEF) {
-                               if (ELF_ST_BIND(sym->info) == STB_WEAK) {
-                                       sym->secidx = SHN_ABS;
-                                       sym->value = 0;
-                               } else {
-                                       if (!flag_quiet)
-                                               bb_error_msg_and_die("unresolved symbol %s", sym->name);
-                               }
-                       }
-       }
-}
-
-static void obj_allocate_commons(struct obj_file *f)
-{
-       struct common_entry {
-               struct common_entry *next;
-               struct obj_symbol *sym;
-       } *common_head = NULL;
-
-       unsigned long i;
-
-       for (i = 0; i < HASH_BUCKETS; ++i) {
-               struct obj_symbol *sym;
-               for (sym = f->symtab[i]; sym; sym = sym->next)
-                       if (sym->secidx == SHN_COMMON) {
-                               /* Collect all COMMON symbols and sort them by size so as to
-                                  minimize space wasted by alignment requirements.  */
-                               {
-                                       struct common_entry **p, *n;
-                                       for (p = &common_head; *p; p = &(*p)->next)
-                                               if (sym->size <= (*p)->sym->size)
-                                                       break;
-
-                                       n = alloca(sizeof(*n));
-                                       n->next = *p;
-                                       n->sym = sym;
-                                       *p = n;
-                               }
-                       }
-       }
-
-       for (i = 1; i < f->local_symtab_size; ++i) {
-               struct obj_symbol *sym = f->local_symtab[i];
-               if (sym && sym->secidx == SHN_COMMON) {
-                       struct common_entry **p, *n;
-                       for (p = &common_head; *p; p = &(*p)->next)
-                               if (sym == (*p)->sym)
-                                       break;
-                               else if (sym->size < (*p)->sym->size) {
-                                       n = alloca(sizeof(*n));
-                                       n->next = *p;
-                                       n->sym = sym;
-                                       *p = n;
-                                       break;
-                               }
-               }
-       }
-
-       if (common_head) {
-               /* Find the bss section.  */
-               for (i = 0; i < f->header.e_shnum; ++i)
-                       if (f->sections[i]->header.sh_type == SHT_NOBITS)
-                               break;
-
-               /* If for some reason there hadn't been one, create one.  */
-               if (i == f->header.e_shnum) {
-                       struct obj_section *sec;
-
-                       f->header.e_shnum++;
-                       f->sections = xrealloc_vector(f->sections, 2, i);
-                       f->sections[i] = sec = arch_new_section();
-
-                       sec->header.sh_type = SHT_PROGBITS;
-                       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
-                       sec->name = ".bss";
-                       sec->idx = i;
-               }
-
-               /* Allocate the COMMONS.  */
-               {
-                       ElfW(Addr) bss_size = f->sections[i]->header.sh_size;
-                       ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;
-                       struct common_entry *c;
-
-                       for (c = common_head; c; c = c->next) {
-                               ElfW(Addr) align = c->sym->value;
-
-                               if (align > max_align)
-                                       max_align = align;
-                               if (bss_size & (align - 1))
-                                       bss_size = (bss_size | (align - 1)) + 1;
-
-                               c->sym->secidx = i;
-                               c->sym->value = bss_size;
-
-                               bss_size += c->sym->size;
-                       }
-
-                       f->sections[i]->header.sh_size = bss_size;
-                       f->sections[i]->header.sh_addralign = max_align;
-               }
-       }
-
-       /* For the sake of patch relocation and parameter initialization,
-          allocate zeroed data for NOBITS sections now.  Note that after
-          this we cannot assume NOBITS are really empty.  */
-       for (i = 0; i < f->header.e_shnum; ++i) {
-               struct obj_section *s = f->sections[i];
-               if (s->header.sh_type == SHT_NOBITS) {
-                       s->contents = NULL;
-                       if (s->header.sh_size != 0)
-                               s->contents = xzalloc(s->header.sh_size),
-                       s->header.sh_type = SHT_PROGBITS;
-               }
-       }
-}
-
-static unsigned long obj_load_size(struct obj_file *f)
-{
-       unsigned long dot = 0;
-       struct obj_section *sec;
-
-       /* Finalize the positions of the sections relative to one another.  */
-
-       for (sec = f->load_order; sec; sec = sec->load_next) {
-               ElfW(Addr) align;
-
-               align = sec->header.sh_addralign;
-               if (align && (dot & (align - 1)))
-                       dot = (dot | (align - 1)) + 1;
-
-               sec->header.sh_addr = dot;
-               dot += sec->header.sh_size;
-       }
-
-       return dot;
-}
-
-static int obj_relocate(struct obj_file *f, ElfW(Addr) base)
-{
-       int i, n = f->header.e_shnum;
-       int ret = 1;
-
-       /* Finalize the addresses of the sections.  */
-
-       f->baseaddr = base;
-       for (i = 0; i < n; ++i)
-               f->sections[i]->header.sh_addr += base;
-
-       /* And iterate over all of the relocations.  */
-
-       for (i = 0; i < n; ++i) {
-               struct obj_section *relsec, *symsec, *targsec, *strsec;
-               ElfW(RelM) * rel, *relend;
-               ElfW(Sym) * symtab;
-               const char *strtab;
-
-               relsec = f->sections[i];
-               if (relsec->header.sh_type != SHT_RELM)
-                       continue;
-
-               symsec = f->sections[relsec->header.sh_link];
-               targsec = f->sections[relsec->header.sh_info];
-               strsec = f->sections[symsec->header.sh_link];
-
-               rel = (ElfW(RelM) *) relsec->contents;
-               relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
-               symtab = (ElfW(Sym) *) symsec->contents;
-               strtab = (const char *) strsec->contents;
-
-               for (; rel < relend; ++rel) {
-                       ElfW(Addr) value = 0;
-                       struct obj_symbol *intsym = NULL;
-                       unsigned long symndx;
-                       ElfW(Sym) * extsym = 0;
-                       const char *errmsg;
-
-                       /* Attempt to find a value to use for this relocation.  */
-
-                       symndx = ELF_R_SYM(rel->r_info);
-                       if (symndx) {
-                               /* Note we've already checked for undefined symbols.  */
-
-                               extsym = &symtab[symndx];
-                               if (ELF_ST_BIND(extsym->st_info) == STB_LOCAL) {
-                                       /* Local symbols we look up in the local table to be sure
-                                          we get the one that is really intended.  */
-                                       intsym = f->local_symtab[symndx];
-                               } else {
-                                       /* Others we look up in the hash table.  */
-                                       const char *name;
-                                       if (extsym->st_name)
-                                               name = strtab + extsym->st_name;
-                                       else
-                                               name = f->sections[extsym->st_shndx]->name;
-                                       intsym = obj_find_symbol(f, name);
-                               }
-
-                               value = obj_symbol_final_value(f, intsym);
-                               intsym->referenced = 1;
-                       }
-#if SHT_RELM == SHT_RELA
-#if defined(__alpha__) && defined(AXP_BROKEN_GAS)
-                       /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9.  */
-                       if (!extsym || !extsym->st_name
-                        || ELF_ST_BIND(extsym->st_info) != STB_LOCAL)
-#endif
-                               value += rel->r_addend;
-#endif
-
-                       /* Do it! */
-                       switch (arch_apply_relocation
-                                       (f, targsec, /*symsec,*/ intsym, rel, value)
-                       ) {
-                       case obj_reloc_ok:
-                               break;
-
-                       case obj_reloc_overflow:
-                               errmsg = "Relocation overflow";
-                               goto bad_reloc;
-                       case obj_reloc_dangerous:
-                               errmsg = "Dangerous relocation";
-                               goto bad_reloc;
-                       case obj_reloc_unhandled:
-                               errmsg = "Unhandled relocation";
-bad_reloc:
-                               if (extsym) {
-                                       bb_error_msg("%s of type %ld for %s", errmsg,
-                                                       (long) ELF_R_TYPE(rel->r_info),
-                                                       strtab + extsym->st_name);
-                               } else {
-                                       bb_error_msg("%s of type %ld", errmsg,
-                                                       (long) ELF_R_TYPE(rel->r_info));
-                               }
-                               ret = 0;
-                               break;
-                       }
-               }
-       }
-
-       /* Finally, take care of the patches.  */
-
-       if (f->string_patches) {
-               struct obj_string_patch *p;
-               struct obj_section *strsec;
-               ElfW(Addr) strsec_base;
-               strsec = obj_find_section(f, ".kstrtab");
-               strsec_base = strsec->header.sh_addr;
-
-               for (p = f->string_patches; p; p = p->next) {
-                       struct obj_section *targsec = f->sections[p->reloc_secidx];
-                       *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
-                               = strsec_base + p->string_offset;
-               }
-       }
-
-       if (f->symbol_patches) {
-               struct obj_symbol_patch *p;
-
-               for (p = f->symbol_patches; p; p = p->next) {
-                       struct obj_section *targsec = f->sections[p->reloc_secidx];
-                       *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
-                               = obj_symbol_final_value(f, p->sym);
-               }
-       }
-
-       return ret;
-}
-
-static int obj_create_image(struct obj_file *f, char *image)
-{
-       struct obj_section *sec;
-       ElfW(Addr) base = f->baseaddr;
-
-       for (sec = f->load_order; sec; sec = sec->load_next) {
-               char *secimg;
-
-               if (sec->contents == 0 || sec->header.sh_size == 0)
-                       continue;
-
-               secimg = image + (sec->header.sh_addr - base);
-
-               /* Note that we allocated data for NOBITS sections earlier.  */
-               memcpy(secimg, sec->contents, sec->header.sh_size);
-       }
-
-       return 1;
-}
-
-/*======================================================================*/
-
-static struct obj_file *obj_load(FILE *fp, int loadprogbits UNUSED_PARAM)
-{
-       struct obj_file *f;
-       ElfW(Shdr) * section_headers;
-       size_t shnum, i;
-       char *shstrtab;
-
-       /* Read the file header.  */
-
-       f = arch_new_file();
-       f->symbol_cmp = strcmp;
-       f->symbol_hash = obj_elf_hash;
-       f->load_order_search_start = &f->load_order;
-
-       fseek(fp, 0, SEEK_SET);
-       if (fread(&f->header, sizeof(f->header), 1, fp) != 1) {
-               bb_perror_msg_and_die("error reading ELF header");
-       }
-
-       if (f->header.e_ident[EI_MAG0] != ELFMAG0
-        || f->header.e_ident[EI_MAG1] != ELFMAG1
-        || f->header.e_ident[EI_MAG2] != ELFMAG2
-        || f->header.e_ident[EI_MAG3] != ELFMAG3
-       ) {
-               bb_error_msg_and_die("not an ELF file");
-       }
-       if (f->header.e_ident[EI_CLASS] != ELFCLASSM
-        || f->header.e_ident[EI_DATA] != (BB_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB)
-        || f->header.e_ident[EI_VERSION] != EV_CURRENT
-        || !MATCH_MACHINE(f->header.e_machine)
-       ) {
-               bb_error_msg_and_die("ELF file not for this architecture");
-       }
-       if (f->header.e_type != ET_REL) {
-               bb_error_msg_and_die("ELF file not a relocatable object");
-       }
-
-       /* Read the section headers.  */
-
-       if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
-               bb_error_msg_and_die("section header size mismatch: %lu != %lu",
-                               (unsigned long) f->header.e_shentsize,
-                               (unsigned long) sizeof(ElfW(Shdr)));
-       }
-
-       shnum = f->header.e_shnum;
-       /* Growth of ->sections vector will be done by
-        * xrealloc_vector(..., 2, ...), therefore we must allocate
-        * at least 2^2 = 4 extra elements here. */
-       f->sections = xzalloc(sizeof(f->sections[0]) * (shnum + 4));
-
-       section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
-       fseek(fp, f->header.e_shoff, SEEK_SET);
-       if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) {
-               bb_perror_msg_and_die("error reading ELF section headers");
-       }
-
-       /* Read the section data.  */
-
-       for (i = 0; i < shnum; ++i) {
-               struct obj_section *sec;
-
-               f->sections[i] = sec = arch_new_section();
-
-               sec->header = section_headers[i];
-               sec->idx = i;
-
-               if (sec->header.sh_size) {
-                       switch (sec->header.sh_type) {
-                       case SHT_NULL:
-                       case SHT_NOTE:
-                       case SHT_NOBITS:
-                               /* ignore */
-                               break;
-
-                       case SHT_PROGBITS:
-#if LOADBITS
-                               if (!loadprogbits) {
-                                       sec->contents = NULL;
-                                       break;
-                               }
-#endif
-                       case SHT_SYMTAB:
-                       case SHT_STRTAB:
-                       case SHT_RELM:
-                               sec->contents = NULL;
-                               if (sec->header.sh_size > 0) {
-                                       sec->contents = xzalloc(sec->header.sh_size);
-                                       fseek(fp, sec->header.sh_offset, SEEK_SET);
-                                       if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
-                                               bb_perror_msg_and_die("error reading ELF section data");
-                                       }
-                               }
-                               break;
-
-#if SHT_RELM == SHT_REL
-                       case SHT_RELA:
-                               bb_error_msg_and_die("RELA relocations not supported on this architecture");
-#else
-                       case SHT_REL:
-                               bb_error_msg_and_die("REL relocations not supported on this architecture");
-#endif
-                       default:
-                               if (sec->header.sh_type >= SHT_LOPROC) {
-                                       /* Assume processor specific section types are debug
-                                          info and can safely be ignored.  If this is ever not
-                                          the case (Hello MIPS?), don't put ifdefs here but
-                                          create an arch_load_proc_section().  */
-                                       break;
-                               }
-
-                               bb_error_msg_and_die("can't handle sections of type %ld",
-                                               (long) sec->header.sh_type);
-                       }
-               }
-       }
-
-       /* Do what sort of interpretation as needed by each section.  */
-
-       shstrtab = f->sections[f->header.e_shstrndx]->contents;
-
-       for (i = 0; i < shnum; ++i) {
-               struct obj_section *sec = f->sections[i];
-               sec->name = shstrtab + sec->header.sh_name;
-       }
-
-       for (i = 0; i < shnum; ++i) {
-               struct obj_section *sec = f->sections[i];
-
-               /* .modinfo should be contents only but gcc has no attribute for that.
-                * The kernel may have marked .modinfo as ALLOC, ignore this bit.
-                */
-               if (strcmp(sec->name, ".modinfo") == 0)
-                       sec->header.sh_flags &= ~SHF_ALLOC;
-
-               if (sec->header.sh_flags & SHF_ALLOC)
-                       obj_insert_section_load_order(f, sec);
-
-               switch (sec->header.sh_type) {
-               case SHT_SYMTAB:
-                       {
-                               unsigned long nsym, j;
-                               char *strtab;
-                               ElfW(Sym) * sym;
-
-                               if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
-                                       bb_error_msg_and_die("symbol size mismatch: %lu != %lu",
-                                                       (unsigned long) sec->header.sh_entsize,
-                                                       (unsigned long) sizeof(ElfW(Sym)));
-                               }
-
-                               nsym = sec->header.sh_size / sizeof(ElfW(Sym));
-                               strtab = f->sections[sec->header.sh_link]->contents;
-                               sym = (ElfW(Sym) *) sec->contents;
-
-                               /* Allocate space for a table of local symbols.  */
-                               j = f->local_symtab_size = sec->header.sh_info;
-                               f->local_symtab = xzalloc(j * sizeof(struct obj_symbol *));
-
-                               /* Insert all symbols into the hash table.  */
-                               for (j = 1, ++sym; j < nsym; ++j, ++sym) {
-                                       ElfW(Addr) val = sym->st_value;
-                                       const char *name;
-                                       if (sym->st_name)
-                                               name = strtab + sym->st_name;
-                                       else if (sym->st_shndx < shnum)
-                                               name = f->sections[sym->st_shndx]->name;
-                                       else
-                                               continue;
-#if defined(__SH5__)
-                                       /*
-                                        * For sh64 it is possible that the target of a branch
-                                        * requires a mode switch (32 to 16 and back again).
-                                        *
-                                        * This is implied by the lsb being set in the target
-                                        * address for SHmedia mode and clear for SHcompact.
-                                        */
-                                       val |= sym->st_other & 4;
-#endif
-                                       obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
-                                                       val, sym->st_size);
-                               }
-                       }
-                       break;
-
-               case SHT_RELM:
-                       if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
-                               bb_error_msg_and_die("relocation entry size mismatch: %lu != %lu",
-                                               (unsigned long) sec->header.sh_entsize,
-                                               (unsigned long) sizeof(ElfW(RelM)));
-                       }
-                       break;
-                       /* XXX  Relocation code from modutils-2.3.19 is not here.
-                        * Why?  That's about 20 lines of code from obj/obj_load.c,
-                        * which gets done in a second pass through the sections.
-                        * This BusyBox insmod does similar work in obj_relocate(). */
-               }
-       }
-
-       return f;
-}
-
-#if ENABLE_FEATURE_INSMOD_LOADINKMEM
-/*
- * load the unloaded sections directly into the memory allocated by
- * kernel for the module
- */
-
-static int obj_load_progbits(FILE *fp, struct obj_file *f, char *imagebase)
-{
-       ElfW(Addr) base = f->baseaddr;
-       struct obj_section* sec;
-
-       for (sec = f->load_order; sec; sec = sec->load_next) {
-
-               /* section already loaded? */
-               if (sec->contents != NULL)
-                       continue;
-
-               if (sec->header.sh_size == 0)
-                       continue;
-
-               sec->contents = imagebase + (sec->header.sh_addr - base);
-               fseek(fp, sec->header.sh_offset, SEEK_SET);
-               if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
-                       bb_perror_msg("error reading ELF section data");
-                       return 0;
-               }
-
-       }
-       return 1;
-}
-#endif
-
-static void hide_special_symbols(struct obj_file *f)
-{
-       static const char *const specials[] = {
-               SPFX "cleanup_module",
-               SPFX "init_module",
-               SPFX "kernel_version",
-               NULL
-       };
-
-       struct obj_symbol *sym;
-       const char *const *p;
-
-       for (p = specials; *p; ++p) {
-               sym = obj_find_symbol(f, *p);
-               if (sym != NULL)
-                       sym->info = ELF_ST_INFO(STB_LOCAL, ELF_ST_TYPE(sym->info));
-       }
-}
-
-
-#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
-static int obj_gpl_license(struct obj_file *f, const char **license)
-{
-       struct obj_section *sec;
-       /* This list must match *exactly* the list of allowable licenses in
-        * linux/include/linux/module.h.  Checking for leading "GPL" will not
-        * work, somebody will use "GPL sucks, this is proprietary".
-        */
-       static const char *const gpl_licenses[] = {
-               "GPL",
-               "GPL v2",
-               "GPL and additional rights",
-               "Dual BSD/GPL",
-               "Dual MPL/GPL"
-       };
-
-       sec = obj_find_section(f, ".modinfo");
-       if (sec) {
-               const char *value, *ptr, *endptr;
-               ptr = sec->contents;
-               endptr = ptr + sec->header.sh_size;
-               while (ptr < endptr) {
-                       value = strchr(ptr, '=');
-                       if (value && strncmp(ptr, "license", value-ptr) == 0) {
-                               unsigned i;
-                               if (license)
-                                       *license = value+1;
-                               for (i = 0; i < ARRAY_SIZE(gpl_licenses); ++i) {
-                                       if (strcmp(value+1, gpl_licenses[i]) == 0)
-                                               return 0;
-                               }
-                               return 2;
-                       }
-                       ptr = strchr(ptr, '\0');
-                       if (ptr)
-                               ptr++;
-                       else
-                               ptr = endptr;
-               }
-       }
-       return 1;
-}
-
-#define TAINT_FILENAME                  "/proc/sys/kernel/tainted"
-#define TAINT_PROPRIETORY_MODULE        (1 << 0)
-#define TAINT_FORCED_MODULE             (1 << 1)
-#define TAINT_UNSAFE_SMP                (1 << 2)
-#define TAINT_URL                       "http://www.tux.org/lkml/#export-tainted"
-
-static void set_tainted(int fd, char *m_name,
-               int kernel_has_tainted, int taint, const char *text1, const char *text2)
-{
-       static smallint printed_info;
-
-       char buf[80];
-       int oldval;
-
-       if (fd < 0 && !kernel_has_tainted)
-               return;         /* New modutils on old kernel */
-       printf("Warning: loading %s will taint the kernel: %s%s\n",
-                       m_name, text1, text2);
-       if (!printed_info) {
-               printf("  See %s for information about tainted modules\n", TAINT_URL);
-               printed_info = 1;
-       }
-       if (fd >= 0) {
-               read(fd, buf, sizeof(buf)-1);
-               buf[sizeof(buf)-1] = '\0';
-               oldval = strtoul(buf, NULL, 10);
-               sprintf(buf, "%d\n", oldval | taint);
-               write(fd, buf, strlen(buf));
-       }
-}
-
-/* Check if loading this module will taint the kernel. */
-static void check_tainted_module(struct obj_file *f, char *m_name)
-{
-       static const char tainted_file[] ALIGN1 = TAINT_FILENAME;
-
-       int fd, kernel_has_tainted;
-       const char *ptr;
-
-       kernel_has_tainted = 1;
-       fd = open(tainted_file, O_RDWR);
-       if (fd < 0) {
-               if (errno == ENOENT)
-                       kernel_has_tainted = 0;
-               else if (errno == EACCES)
-                       kernel_has_tainted = 1;
-               else {
-                       perror(tainted_file);
-                       kernel_has_tainted = 0;
-               }
-       }
-
-       switch (obj_gpl_license(f, &ptr)) {
-               case 0:
-                       break;
-               case 1:
-                       set_tainted(fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
-                       break;
-               case 2:
-                       /* The module has a non-GPL license so we pretend that the
-                        * kernel always has a taint flag to get a warning even on
-                        * kernels without the proc flag.
-                        */
-                       set_tainted(fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
-                       break;
-               default:
-                       set_tainted(fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
-                       break;
-       }
-
-       if (flag_force_load)
-               set_tainted(fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
-
-       if (fd >= 0)
-               close(fd);
-}
-#else /* FEATURE_CHECK_TAINTED_MODULE */
-#define check_tainted_module(x, y) do { } while (0);
-#endif /* FEATURE_CHECK_TAINTED_MODULE */
-
-#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
-/* 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.
- */
-#if !ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-#define get_module_version(f, str) get_module_version(str)
-#endif
-static int
-get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
-{
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-       return new_get_module_version(f, str);
-#else  /* FEATURE_INSMOD_VERSION_CHECKING */
-       strncpy(str, "???", sizeof(str));
-       return -1;
-#endif /* FEATURE_INSMOD_VERSION_CHECKING */
-}
-
-/* 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)
-{
-       static const char symprefix[] ALIGN1 = "__insmod_";
-       static const char section_names[][8] = {
-               ".text",
-               ".rodata",
-               ".data",
-               ".bss",
-               ".sbss"
-       };
-
-       struct obj_section *sec;
-       struct obj_symbol *sym;
-       char *name, *absolute_filename;
-       char str[STRVERSIONLEN];
-       unsigned i;
-       int l, lm_name, lfilename, use_ksymtab, version;
-       struct stat statbuf;
-
-       /* WARNING: was using realpath, but replaced by readlink to stop using
-        * lots of stack. But here it seems to be able to cause problems? */
-       absolute_filename = xmalloc_readlink(filename);
-       if (!absolute_filename)
-               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_noexport;
-
-       sec = obj_find_section(f, ".this");
-       if (sec) {
-               /* 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,
-                               (int)(2 * sizeof(statbuf.st_mtime)), statbuf.st_mtime,
-                               version);
-               sym = obj_add_symbol(f, name, -1,
-                               ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
-                               sec->idx, sec->header.sh_addr, 0);
-               if (use_ksymtab)
-                       new_add_ksymtab(f, sym);
-       }
-       free(absolute_filename);
-#ifdef _NOT_SUPPORTED_
-       /* 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, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
-                               sec->idx, sec->header.sh_addr, 0);
-               if (use_ksymtab)
-                       new_add_ksymtab(f, sym);
-       }
-#endif /* _NOT_SUPPORTED_ */
-       /* tag the desired sections if size is non-zero */
-
-       for (i = 0; i < ARRAY_SIZE(section_names); ++i) {
-               sec = obj_find_section(f, section_names[i]);
-               if (sec && 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, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
-                                       sec->idx, sec->header.sh_addr, 0);
-                       if (use_ksymtab)
-                               new_add_ksymtab(f, sym);
-               }
-       }
-}
-#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
-
-#if ENABLE_FEATURE_INSMOD_LOAD_MAP
-static void print_load_map(struct obj_file *f)
-{
-       struct obj_section *sec;
-#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
-       struct obj_symbol **all, **p;
-       int i, nsyms, *loaded;
-       struct obj_symbol *sym;
-#endif
-       /* Report on the section layout.  */
-
-       printf("Sections:       Size      %-*s  Align\n",
-                       (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;
-
-               printf("%-15s %08lx  %0*lx  2**%d\n",
-                               sec->name,
-                               (long)sec->header.sh_size,
-                               (int) (2 * sizeof(void *)),
-                               (long)sec->header.sh_addr,
-                               a);
-       }
-#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
-       /* Quick reference which section indices are loaded.  */
-
-       i = f->header.e_shnum;
-       loaded = alloca(sizeof(int) * i);
-       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;
-                       }
-
-       /* And list them.  */
-       printf("\nSymbols:\n");
-       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 {
-                       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 (ELF_ST_BIND(sym->info) == STB_LOCAL)
-                       type = tolower(type);
-
-               printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
-                               type, sym->name);
-       }
-#endif
-}
-#else /* !FEATURE_INSMOD_LOAD_MAP */
-void print_load_map(struct obj_file *f);
-#endif
-
-int insmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int insmod_main(int argc, char **argv)
-{
-       char *opt_o, *arg1;
-       int len;
-       int k_crcs;
-       char *tmp, *tmp1;
-       unsigned long m_size;
-       ElfW(Addr) m_addr;
-       struct obj_file *f;
-       struct stat st;
-       char *m_name = NULL;
-       int exit_status = EXIT_FAILURE;
-       int m_has_modinfo;
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-       struct utsname uts_info;
-       char m_strversion[STRVERSIONLEN];
-       int m_version, m_crcs;
-#endif
-#if ENABLE_FEATURE_CLEAN_UP
-       FILE *fp = NULL;
-#else
-       FILE *fp;
-#endif
-       int k_version = 0;
-       struct utsname myuname;
-
-       /* Parse any options */
-       getopt32(argv, OPTION_STR, &opt_o);
-       arg1 = argv[optind];
-       if (option_mask32 & OPT_o) { // -o /* name the output module */
-               free(m_name);
-               m_name = xstrdup(opt_o);
-       }
-
-       if (arg1 == NULL) {
-               bb_show_usage();
-       }
-
-       /* Grab the module name */
-       tmp1 = xstrdup(arg1);
-       tmp = basename(tmp1);
-       len = strlen(tmp);
-
-       if (uname(&myuname) == 0) {
-               if (myuname.release[0] == '2') {
-                       k_version = myuname.release[2] - '0';
-               }
-       }
-
-#if ENABLE_FEATURE_2_6_MODULES
-       if (k_version > 4 && len > 3 && tmp[len - 3] == '.'
-        && tmp[len - 2] == 'k' && tmp[len - 1] == 'o'
-       ) {
-               len -= 3;
-               tmp[len] = '\0';
-       } else
-#endif
-               if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') {
-                       len -= 2;
-                       tmp[len] = '\0';
-               }
-
-
-#if ENABLE_FEATURE_2_6_MODULES
-       if (k_version > 4)
-               m_fullName = xasprintf("%s.ko", tmp);
-       else
-#endif
-               m_fullName = xasprintf("%s.o", tmp);
-
-       if (!m_name) {
-               m_name = tmp;
-       } else {
-               free(tmp1);
-               tmp1 = NULL;       /* flag for free(m_name) before exit() */
-       }
-
-       /* Get a filedesc for the module.  Check that we have a complete path */
-       if (stat(arg1, &st) < 0 || !S_ISREG(st.st_mode)
-        || (fp = fopen_for_read(arg1)) == NULL
-       ) {
-               /* Hmm.  Could not open it.  First search under /lib/modules/`uname -r`,
-                * but do not error out yet if we fail to find it... */
-               if (k_version) {        /* uname succeedd */
-                       char *module_dir;
-                       char *tmdn;
-
-                       tmdn = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, myuname.release);
-                       /* Jump through hoops in case /lib/modules/`uname -r`
-                        * is a symlink.  We do not want recursive_action to
-                        * follow symlinks, but we do want to follow the
-                        * /lib/modules/`uname -r` dir, So resolve it ourselves
-                        * if it is a link... */
-                       module_dir = xmalloc_readlink(tmdn);
-                       if (!module_dir)
-                               module_dir = xstrdup(tmdn);
-                       recursive_action(module_dir, ACTION_RECURSE,
-                                       check_module_name_match, NULL, m_fullName, 0);
-                       free(module_dir);
-                       free(tmdn);
-               }
-
-               /* Check if we have found anything yet */
-               if (!m_filename || ((fp = fopen_for_read(m_filename)) == NULL)) {
-                       int r;
-                       char *module_dir;
-
-                       free(m_filename);
-                       m_filename = NULL;
-                       module_dir = xmalloc_readlink(CONFIG_DEFAULT_MODULES_DIR);
-                       if (!module_dir)
-                               module_dir = xstrdup(CONFIG_DEFAULT_MODULES_DIR);
-                       /* No module found under /lib/modules/`uname -r`, this
-                        * time cast the net a bit wider.  Search /lib/modules/ */
-                       r = recursive_action(module_dir, ACTION_RECURSE,
-                                       check_module_name_match, NULL, m_fullName, 0);
-                       if (r)
-                               bb_error_msg_and_die("%s: module not found", m_fullName);
-                       free(module_dir);
-                       if (m_filename == NULL
-                        || ((fp = fopen_for_read(m_filename)) == NULL)
-                       ) {
-                               bb_error_msg_and_die("%s: module not found", m_fullName);
-                       }
-               }
-       } else
-               m_filename = xstrdup(arg1);
-
-       if (flag_verbose)
-               printf("Using %s\n", m_filename);
-
-#if ENABLE_FEATURE_2_6_MODULES
-       if (k_version > 4) {
-               argv[optind] = m_filename;
-               optind--;
-               return insmod_ng_main(argc - optind, argv + optind);
-       }
-#endif
-
-       f = obj_load(fp, LOADBITS);
-
-       if (get_modinfo_value(f, "kernel_version") == NULL)
-               m_has_modinfo = 0;
-       else
-               m_has_modinfo = 1;
-
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-       /* Version correspondence?  */
-       if (!flag_quiet) {
-               if (uname(&uts_info) < 0)
-                       uts_info.release[0] = '\0';
-               if (m_has_modinfo) {
-                       m_version = new_get_module_version(f, m_strversion);
-                       if (m_version == -1) {
-                               bb_error_msg_and_die("cannot find the kernel version the module was "
-                                               "compiled for");
-                       }
-               }
-
-               if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) {
-                       bb_error_msg("%skernel-module version mismatch\n"
-                               "\t%s was compiled for kernel version %s\n"
-                               "\twhile this kernel is version %s",
-                               flag_force_load ? "warning: " : "",
-                               m_filename, m_strversion, uts_info.release);
-                       if (!flag_force_load)
-                               goto out;
-               }
-       }
-       k_crcs = 0;
-#endif /* FEATURE_INSMOD_VERSION_CHECKING */
-
-       if (query_module(NULL, 0, NULL, 0, NULL))
-               bb_error_msg_and_die("not configured to support old kernels");
-       new_get_kernel_symbols();
-       k_crcs = new_is_kernel_checksummed();
-
-#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
-       m_crcs = 0;
-       if (m_has_modinfo)
-               m_crcs = new_is_module_checksummed(f);
-
-       if (m_crcs != k_crcs)
-               obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
-#endif /* FEATURE_INSMOD_VERSION_CHECKING */
-
-       /* Let the module know about the kernel symbols.  */
-       add_kernel_symbols(f);
-
-       /* Allocate common symbols, symbol tables, and string tables.  */
-
-       new_create_this_module(f, m_name);
-       obj_check_undefineds(f);
-       obj_allocate_commons(f);
-       check_tainted_module(f, m_name);
-
-       /* done with the module name, on to the optional var=value arguments */
-       ++optind;
-       if (optind < argc) {
-               new_process_module_arguments(f, argc - optind, argv + optind);
-       }
-
-       arch_create_got(f);
-       hide_special_symbols(f);
-
-#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
-       add_ksymoops_symbols(f, m_filename, m_name);
-#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
-
-       new_create_module_ksymtab(f);
-
-       /* Find current size of the module */
-       m_size = obj_load_size(f);
-
-       m_addr = create_module(m_name, m_size);
-       if (m_addr == (ElfW(Addr))(-1)) switch (errno) {
-               case EEXIST:
-                       bb_error_msg_and_die("a module named %s already exists", m_name);
-               case ENOMEM:
-                       bb_error_msg_and_die("can't allocate kernel memory for module; needed %lu bytes",
-                                       m_size);
-               default:
-                       bb_perror_msg_and_die("create_module: %s", m_name);
-       }
-
-#if !LOADBITS
-       /*
-        * the PROGBITS section was not loaded by the obj_load
-        * now we can load them directly into the kernel memory
-        */
-       if (!obj_load_progbits(fp, f, (char*)m_addr)) {
-               delete_module(m_name, 0);
-               goto out;
-       }
-#endif
-
-       if (!obj_relocate(f, m_addr)) {
-               delete_module(m_name, 0);
-               goto out;
-       }
-
-       if (!new_init_module(m_name, f, m_size)) {
-               delete_module(m_name, 0);
-               goto out;
-       }
-
-       if (flag_print_load_map)
-               print_load_map(f);
-
-       exit_status = EXIT_SUCCESS;
-
- out:
-#if ENABLE_FEATURE_CLEAN_UP
-       if (fp)
-               fclose(fp);
-       free(tmp1);
-       if (!tmp1)
-               free(m_name);
-       free(m_filename);
-#endif
-       return exit_status;
-}
-
-#endif /* ENABLE_FEATURE_2_4_MODULES */
-/*
- * End of big piece of 2.4-specific code
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
+#include "libbb.h"
+#include "modutils.h"
 
-#if ENABLE_FEATURE_2_6_MODULES
-
-#include <sys/mman.h>
-
-#if defined __UCLIBC__ && !ENABLE_FEATURE_2_4_MODULES
-/* big time suckage. The old prototype above renders our nice fwd-decl wrong */
-extern int init_module(void *module, unsigned long len, const char *options);
-#else
-#include <asm/unistd.h>
-#include <sys/syscall.h>
-#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
-#endif
-
-/* We use error numbers in a loose translation... */
-static const char *moderror(int err)
-{
-       switch (err) {
-       case ENOEXEC:
-               return "invalid module format";
-       case ENOENT:
-               return "unknown symbol in module";
-       case ESRCH:
-               return "module has wrong symbol version";
-       case EINVAL:
-               return "invalid parameters";
-       default:
-               return strerror(err);
-       }
-}
-
-#if !ENABLE_FEATURE_2_4_MODULES
 int insmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int insmod_main(int argc UNUSED_PARAM, char **argv)
-#else
-static int insmod_ng_main(int argc UNUSED_PARAM, char **argv)
-#endif
 {
-       size_t len;
-       int optlen;
-       void *map;
-       char *filename, *options;
+       char *filename;
+       int rc;
+
+       USE_FEATURE_2_4_MODULES(
+               getopt32(argv, INSMOD_OPTS INSMOD_ARGS);
+               argv += optind-1;
+       );
 
        filename = *++argv;
        if (!filename)
                bb_show_usage();
 
-       /* Rest is options */
-       options = xzalloc(1);
-       optlen = 0;
-       while (*++argv) {
-               options = xrealloc(options, optlen + 2 + strlen(*argv) + 2);
-               /* Spaces handled by "" pairs, but no way of escaping quotes */
-               optlen += sprintf(options + optlen, (strchr(*argv,' ') ? "\"%s\" " : "%s "), *argv);
-       }
-
-#if 0
-       /* Any special reason why mmap? It isn't performance critical. -vda */
-       /* Yes, xmalloc'ing can use *alot* of RAM. Don't forget that there are
-        * modules out there that are half a megabyte! mmap()ing is way nicer
-        * for small mem boxes, i guess. */
-       /* But after load, these modules will take up that 0.5mb in kernel
-        * anyway. Using malloc here causes only a transient spike to 1mb,
-        * after module is loaded, we go back to normal 0.5mb usage
-        * (in kernel). Also, mmap isn't magic - when we touch mapped data,
-        * we use memory. -vda */
-       int fd;
-       struct stat st;
-       unsigned long len;
-       fd = xopen(filename, O_RDONLY);
-       fstat(fd, &st);
-       len = st.st_size;
-       map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (map == MAP_FAILED) {
-               bb_perror_msg_and_die("cannot mmap '%s'", filename);
-       }
+       rc = bb_init_module(filename, parse_cmdline_module_options(argv));
+       if (rc)
+               bb_error_msg("cannot insert '%s': %s", filename, moderror(rc));
 
-       /* map == NULL on Blackfin, probably on other MMU-less systems too. Workaround. */
-       if (map == NULL) {
-               map = xmalloc(len);
-               xread(fd, map, len);
-       }
-#else
-       len = MAXINT(ssize_t);
-       map = xmalloc_xopen_read_close(filename, &len);
-#endif
-
-       if (init_module(map, len, options) != 0)
-               bb_error_msg_and_die("cannot insert '%s': %s",
-                               filename, moderror(errno));
-       return 0;
+       return rc;
 }
-
-#endif
index 3f237039e9c2af2d8f8cac41752bdae5cd903f71..b6656369306b3e44b56775046e27d36250810db6 100644 (file)
  * Mini lsmod implementation for busybox
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
- *
- * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
- * Nicolas Ferre <nicolas.ferre@alcove.fr> to support pre 2.1 kernels
- * (which lack the query_module() interface).
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
 #include "libbb.h"
 
-
-#if !ENABLE_FEATURE_CHECK_TAINTED_MODULE
-static void check_tainted(void) { bb_putchar('\n'); }
-#else
-#define TAINT_FILENAME                  "/proc/sys/kernel/tainted"
-#define TAINT_PROPRIETORY_MODULE        (1<<0)
-#define TAINT_FORCED_MODULE             (1<<1)
-#define TAINT_UNSAFE_SMP                (1<<2)
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+enum {
+       TAINT_PROPRIETORY_MODULE = (1 << 0),
+       TAINT_FORCED_MODULE      = (1 << 1),
+       TAINT_UNSAFE_SMP         = (1 << 2),
+};
 
 static void check_tainted(void)
 {
-       int tainted;
-       FILE *f;
-
-       tainted = 0;
-       f = fopen_for_read(TAINT_FILENAME);
-       if (f) {
-               fscanf(f, "%d", &tainted);
-               fclose(f);
+       int tainted = 0;
+       char *buf = xmalloc_open_read_close("/proc/sys/kernel/tainted", NULL);
+       if (buf) {
+               tainted = atoi(buf);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       free(buf);
        }
+
        if (tainted) {
                printf("    Tainted: %c%c%c\n",
                                tainted & TAINT_PROPRIETORY_MODULE      ? 'P' : 'G',
                                tainted & TAINT_FORCED_MODULE           ? 'F' : ' ',
                                tainted & TAINT_UNSAFE_SMP              ? 'S' : ' ');
        } else {
-               printf("    Not tainted\n");
+               puts("    Not tainted");
        }
 }
+#else
+static void check_tainted(void) { putchar('\n'); }
 #endif
 
-#if ENABLE_FEATURE_QUERY_MODULE_INTERFACE
-
-struct module_info
-{
-       unsigned long addr;
-       unsigned long size;
-       unsigned long flags;
-       long usecount;
-};
-
-
-int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
-
-enum {
-/* Values for query_module's which.  */
-       QM_MODULES = 1,
-       QM_DEPS = 2,
-       QM_REFS = 3,
-       QM_SYMBOLS = 4,
-       QM_INFO = 5,
-
-/* Bits of module.flags.  */
-       NEW_MOD_RUNNING = 1,
-       NEW_MOD_DELETED = 2,
-       NEW_MOD_AUTOCLEAN = 4,
-       NEW_MOD_VISITED = 8,
-       NEW_MOD_USED_ONCE = 16,
-       NEW_MOD_INITIALIZING = 64
-};
-
 int lsmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int lsmod_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 {
-       struct module_info info;
-       char *module_names, *mn, *deps, *dn;
-       size_t bufsize, depsize, nmod, count, i, j;
-
-       module_names = deps = NULL;
-       bufsize = depsize = 0;
-       while (query_module(NULL, QM_MODULES, module_names, bufsize, &nmod)) {
-               if (errno != ENOSPC) bb_perror_msg_and_die("QM_MODULES");
-               module_names = xmalloc(bufsize = nmod);
-       }
-
-       deps = xmalloc(depsize = 256);
-       printf("Module\t\t\tSize  Used by");
+#if ENABLE_FEATURE_LSMOD_PRETTY_2_6_OUTPUT
+       char *token[4];
+       parser_t *parser = config_open("/proc/modules");
+       printf("Module                  Size  Used by"); //vda!
        check_tainted();
 
-       for (i = 0, mn = module_names; i < nmod; mn += strlen(mn) + 1, i++) {
-               if (query_module(mn, QM_INFO, &info, sizeof(info), &count)) {
-                       if (errno == ENOENT) {
-                               /* The module was removed out from underneath us. */
-                               continue;
-                       }
-                       /* else choke */
-                       bb_perror_msg_and_die("module %s: QM_INFO", mn);
+       if (ENABLE_FEATURE_2_4_MODULES
+        && get_linux_version_code() < KERNEL_VERSION(2,6,0)
+       ) {
+               while (config_read(parser, token, 4, 3, "# \t", PARSE_NORMAL)) {
+                       if (token[3] != NULL && token[3][0] == '[') {
+                               token[3]++;
+                               token[3][strlen(token[3])-1] = '\0';
+                       } else
+                               token[3] = (char *) "";
+                       printf("%-19s %8s %2s %s\n", token[0], token[1], token[2], token[3]);
                }
-               while (query_module(mn, QM_REFS, deps, depsize, &count)) {
-                       if (errno == ENOENT) {
-                               /* The module was removed out from underneath us. */
-                               continue;
-                       } else if (errno != ENOSPC)
-                               bb_perror_msg_and_die("module %s: QM_REFS", mn);
-                       deps = xrealloc(deps, count);
-               }
-               printf("%-20s%8lu%4ld", mn, info.size, info.usecount);
-               if (info.flags & NEW_MOD_DELETED)
-                       printf(" (deleted)");
-               else if (info.flags & NEW_MOD_INITIALIZING)
-                       printf(" (initializing)");
-               else if (!(info.flags & NEW_MOD_RUNNING))
-                       printf(" (uninitialized)");
-               else {
-                       if (info.flags & NEW_MOD_AUTOCLEAN)
-                               printf(" (autoclean) ");
-                       if (!(info.flags & NEW_MOD_USED_ONCE))
-                               printf(" (unused)");
-               }
-               if (count)
-                       printf(" [");
-               for (j = 0, dn = deps; j < count; dn += strlen(dn) + 1, j++) {
-                       printf("%s%s", dn, (j==count-1)? "":" ");
-               }
-               if (count)
-                       bb_putchar(']');
-
-               bb_putchar('\n');
-       }
-
-#if ENABLE_FEATURE_CLEAN_UP
-       free(module_names);
-#endif
-
-       return 0;
-}
-
-#else /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
-
-int lsmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int lsmod_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
-{
-       FILE *file = xfopen_for_read("/proc/modules");
-
-       printf("Module                  Size  Used by");
-       check_tainted();
-#if ENABLE_FEATURE_LSMOD_PRETTY_2_6_OUTPUT
-       {
-               char *line;
-               while ((line = xmalloc_fgets(file)) != NULL) {
-                       char *tok;
-
-                       tok = strtok(line, " \t");
-                       printf("%-19s", tok);
-                       tok = strtok(NULL, " \t\n");
-                       printf(" %8s", tok);
-                       tok = strtok(NULL, " \t\n");
-                       /* Null if no module unloading support. */
-                       if (tok) {
-                               printf("  %s", tok);
-                               tok = strtok(NULL, "\n");
-                               if (!tok)
-                                       tok = (char*)"";
-                               /* New-style has commas, or -.  If so,
-                               truncate (other fields might follow). */
-                               else if (strchr(tok, ',')) {
-                                       tok = strtok(tok, "\t ");
-                                       /* Strip trailing comma. */
-                                       if (tok[strlen(tok)-1] == ',')
-                                               tok[strlen(tok)-1] = '\0';
-                               } else if (tok[0] == '-'
-                                && (tok[1] == '\0' || isspace(tok[1]))
-                               ) {
-                                       tok = (char*)"";
-                               }
-                               printf(" %s", tok);
-                       }
-                       bb_putchar('\n');
-                       free(line);
+       } else {
+               while (config_read(parser, token, 4, 4, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
+                       // N.B. token[3] is either '-' (module is not used by others)
+                       // or comma-separated list ended by comma
+                       // so trimming the trailing char is just what we need!
+                       token[3][strlen(token[3])-1] = '\0';
+                       printf("%-19s %8s %2s %s\n", token[0], token[1], token[2], token[3]);
                }
-               fclose(file);
        }
+       if (ENABLE_FEATURE_CLEAN_UP)
+               config_close(parser);
 #else
-       xprint_and_close_file(file);
-#endif  /*  CONFIG_FEATURE_2_6_MODULES  */
+       check_tainted();
+       xprint_and_close_file(xfopen_for_read("/proc/modules"));
+#endif
        return EXIT_SUCCESS;
 }
-
-#endif /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
index 412e71d16b7535809432c37b90186f7552679ae4..20b9a74aabcb6dbe4ad5479909a17d2df0c72651 100644 (file)
 /*
  * Modprobe written from scratch for BusyBox
  *
- * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
- * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
- * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com
- *
- * Portions Copyright (c) 2005 by Yann E. MORIN, yann.morin.1998@anciens.enib.fr
+ * Copyright (c) 2008 Timo Teras <timo.teras@iki.fi>
+ * Copyright (c) 2008 Vladimir Dronnikov
  *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
-*/
+ */
 
 #include "libbb.h"
+#include "modutils.h"
 #include <sys/utsname.h>
 #include <fnmatch.h>
 
-#define line_buffer bb_common_bufsiz1
-
-struct mod_opt_t {     /* one-way list of options to pass to a module */
-       char *  m_opt_val;
-       struct mod_opt_t * m_next;
-};
-
-struct dep_t { /* one-way list of dependency rules */
-       /* a dependency rule */
-       char *  m_name;                     /* the module name*/
-       char *  m_path;                     /* the module file path */
-       struct mod_opt_t *  m_options;      /* the module options */
-
-       unsigned int m_isalias      :1;     /* the module is an alias */
-       unsigned int m_isblacklisted:1;     /* the module is blacklisted */
-       unsigned int m_reserved     :14;    /* stuffin' */
-
-       unsigned int m_depcnt       :16;    /* the number of dependable module(s) */
-       char ** m_deparr;                   /* the list of dependable module(s) */
-
-       struct dep_t * m_next;              /* the next dependency rule */
+struct modprobe_option {
+       char *module;
+       char *option;
 };
 
-struct mod_list_t {    /* two-way list of modules to process */
-       /* a module description */
-       const char * m_name;
-       char * m_path;
-       struct mod_opt_t * m_options;
-
-       struct mod_list_t * m_prev;
-       struct mod_list_t * m_next;
+struct modprobe_conf {
+       char probename[MODULE_NAME_LEN];
+       llist_t *options;
+       llist_t *aliases;
+#if ENABLE_FEATURE_MODPROBE_BLACKLIST
+#define add_to_blacklist(conf, name) llist_add_to(&conf->blacklist, name)
+#define check_blacklist(conf, name) (llist_find(conf->blacklist, name) == NULL)
+       llist_t *blacklist;
+#else
+#define add_to_blacklist(conf, name) do {} while (0)
+#define check_blacklist(conf, name) (1)
+#endif
 };
 
-struct include_conf_t {
-       struct dep_t *first;
-       struct dep_t *current;
+#define MODPROBE_OPTS  "acdlnrt:VC:" USE_FEATURE_MODPROBE_BLACKLIST("b")
+enum {
+       MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */
+       MODPROBE_OPT_DUMP_ONLY  = (INSMOD_OPT_UNUSED << 1), /* c */
+       MODPROBE_OPT_D          = (INSMOD_OPT_UNUSED << 2), /* d */
+       MODPROBE_OPT_LIST_ONLY  = (INSMOD_OPT_UNUSED << 3), /* l */
+       MODPROBE_OPT_SHOW_ONLY  = (INSMOD_OPT_UNUSED << 4), /* n */
+       MODPROBE_OPT_REMOVE     = (INSMOD_OPT_UNUSED << 5), /* r */
+       MODPROBE_OPT_RESTRICT   = (INSMOD_OPT_UNUSED << 6), /* t */
+       MODPROBE_OPT_VERONLY    = (INSMOD_OPT_UNUSED << 7), /* V */
+       MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */
+       MODPROBE_OPT_BLACKLIST  = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST,
 };
 
-static struct dep_t *depend;
-
-#define MAIN_OPT_STR "acdklnqrst:vVC:"
-#define INSERT_ALL     1        /* a */
-#define DUMP_CONF_EXIT 2        /* c */
-#define D_OPT_IGNORED  4        /* d */
-#define AUTOCLEAN_FLG  8        /* k */
-#define LIST_ALL       16       /* l */
-#define SHOW_ONLY      32       /* n */
-#define QUIET          64       /* q */
-#define REMOVE_OPT     128      /* r */
-#define DO_SYSLOG      256      /* s */
-#define RESTRICT_DIR   512      /* t */
-#define VERBOSE        1024     /* v */
-#define VERSION_ONLY   2048     /* V */
-#define CONFIG_FILE    4096     /* C */
-
-#define autoclean       (option_mask32 & AUTOCLEAN_FLG)
-#define show_only       (option_mask32 & SHOW_ONLY)
-#define quiet           (option_mask32 & QUIET)
-#define remove_opt      (option_mask32 & REMOVE_OPT)
-#define do_syslog       (option_mask32 & DO_SYSLOG)
-#define verbose         (option_mask32 & VERBOSE)
-
-static int parse_tag_value(char *buffer, char **ptag, char **pvalue)
-{
-       char *tag, *value;
-
-       buffer = skip_whitespace(buffer);
-       tag = value = buffer;
-       while (!isspace(*value)) {
-               if (!*value)
-                       return 0;
-               value++;
-       }
-       *value++ = '\0';
-       value = skip_whitespace(value);
-       if (!*value)
-               return 0;
-
-       *ptag = tag;
-       *pvalue = value;
-
-       return 1;
-}
-
-/*
- * This function appends an option to a list
- */
-static struct mod_opt_t *append_option(struct mod_opt_t *opt_list, char *opt)
-{
-       struct mod_opt_t *ol = opt_list;
-
-       if (ol) {
-               while (ol->m_next) {
-                       ol = ol->m_next;
-               }
-               ol->m_next = xzalloc(sizeof(struct mod_opt_t));
-               ol = ol->m_next;
-       } else {
-               ol = opt_list = xzalloc(sizeof(struct mod_opt_t));
-       }
-
-       ol->m_opt_val = xstrdup(opt);
-       /*ol->m_next = NULL; - done by xzalloc*/
-
-       return opt_list;
-}
-
-#if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS
-/* static char* parse_command_string(char* src, char **dst);
- *   src: pointer to string containing argument
- *   dst: pointer to where to store the parsed argument
- *   return value: the pointer to the first char after the parsed argument,
- *                 NULL if there was no argument parsed (only trailing spaces).
- *   Note that memory is allocated with xstrdup when a new argument was
- *   parsed. Don't forget to free it!
- */
-#define ARG_EMPTY      0x00
-#define ARG_IN_DQUOTES 0x01
-#define ARG_IN_SQUOTES 0x02
-static char *parse_command_string(char *src, char **dst)
-{
-       int opt_status = ARG_EMPTY;
-       char* tmp_str;
-
-       /* Dumb you, I have nothing to do... */
-       if (src == NULL) return src;
-
-       /* Skip leading spaces */
-       while (*src == ' ') {
-               src++;
-       }
-       /* Is the end of string reached? */
-       if (*src == '\0') {
-               return NULL;
-       }
-       /* Reached the start of an argument
-        * By the way, we duplicate a little too much
-        * here but what is too much is freed later. */
-       *dst = tmp_str = xstrdup(src);
-       /* Get to the end of that argument */
-       while (*tmp_str != '\0'
-        && (*tmp_str != ' ' || (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)))
-       ) {
-               switch (*tmp_str) {
-               case '\'':
-                       if (opt_status & ARG_IN_DQUOTES) {
-                               /* Already in double quotes, keep current char as is */
-                       } else {
-                               /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
-                               memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
-                               /* mark me: we enter or leave single quotes */
-                               opt_status ^= ARG_IN_SQUOTES;
-                               /* Back one char, as we need to re-scan the new char there. */
-                               tmp_str--;
-                       }
-                       break;
-               case '"':
-                       if (opt_status & ARG_IN_SQUOTES) {
-                               /* Already in single quotes, keep current char as is */
-                       } else {
-                               /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
-                               memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
-                               /* mark me: we enter or leave double quotes */
-                               opt_status ^= ARG_IN_DQUOTES;
-                               /* Back one char, as we need to re-scan the new char there. */
-                               tmp_str--;
-                       }
-                       break;
-               case '\\':
-                       if (opt_status & ARG_IN_SQUOTES) {
-                               /* Between single quotes: keep as is. */
-                       } else {
-                               switch (*(tmp_str+1)) {
-                               case 'a':
-                               case 'b':
-                               case 't':
-                               case 'n':
-                               case 'v':
-                               case 'f':
-                               case 'r':
-                               case '0':
-                                       /* We escaped a special character. For now, keep
-                                        * both the back-slash and the following char. */
-                                       tmp_str++;
-                                       src++;
-                                       break;
-                               default:
-                                       /* We escaped a space or a single or double quote,
-                                        * or a back-slash, or a non-escapable char. Remove
-                                        * the '\' and keep the new current char as is. */
-                                       memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
-                                       break;
-                               }
-                       }
-                       break;
-               /* Any other char that is special shall appear here.
-                * Example: $ starts a variable
-               case '$':
-                       do_variable_expansion();
-                       break;
-                * */
-               default:
-                       /* any other char is kept as is. */
-                       break;
-               }
-               tmp_str++; /* Go to next char */
-               src++; /* Go to next char to find the end of the argument. */
-       }
-       /* End of string, but still no ending quote */
-       if (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)) {
-               bb_error_msg_and_die("unterminated (single or double) quote in options list: %s", src);
-       }
-       *tmp_str++ = '\0';
-       *dst = xrealloc(*dst, (tmp_str - *dst));
-       return src;
-}
-#else
-#define parse_command_string(src, dst) (0)
-#endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */
-
-static int is_conf_command(char *buffer, const char *command)
-{
-       int len = strlen(command);
-       return ((strstr(buffer, command) == buffer) &&
-                       isspace(buffer[len]));
-}
-
-/*
- * This function reads aliases and default module options from a configuration file
- * (/etc/modprobe.conf syntax). It supports includes (only files, no directories).
- */
+static llist_t *loaded;
 
-static int FAST_FUNC include_conf_file_act(const char *filename,
-                                          struct stat *statbuf UNUSED_PARAM,
-                                          void *userdata,
-                                          int depth UNUSED_PARAM);
+static int read_config(struct modprobe_conf *conf, const char *path);
 
-static int FAST_FUNC include_conf_dir_act(const char *filename UNUSED_PARAM,
-                                         struct stat *statbuf UNUSED_PARAM,
-                                         void *userdata UNUSED_PARAM,
-                                         int depth)
+static void add_option(llist_t **all_opts, const char *module, const char *opts)
 {
-       if (depth > 1)
-               return SKIP;
+       struct modprobe_option *o;
 
-       return TRUE;
+       o = xzalloc(sizeof(struct modprobe_option));
+       if (module)
+               o->module = filename2modname(module, NULL);
+       o->option = xstrdup(opts);
+       llist_add_to(all_opts, o);
 }
 
-static int include_conf_recursive(struct include_conf_t *conf, const char *filename)
+static int FAST_FUNC config_file_action(const char *filename,
+                                       struct stat *statbuf UNUSED_PARAM,
+                                       void *userdata,
+                                       int depth UNUSED_PARAM)
 {
-       return recursive_action(filename, ACTION_RECURSE,
-                               include_conf_file_act,
-                               include_conf_dir_act,
-                               conf, 1);
-}
-
-static int FAST_FUNC include_conf_file_act(const char *filename,
-                                          struct stat *statbuf UNUSED_PARAM,
-                                          void *userdata,
-                                          int depth UNUSED_PARAM)
-{
-       struct include_conf_t *conf = (struct include_conf_t *) userdata;
-       struct dep_t **first = &conf->first;
-       struct dep_t **current = &conf->current;
-       int continuation_line = 0;
-       FILE *f;
+       struct modprobe_conf *conf = (struct modprobe_conf *) userdata;
+       RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN);
+       char *tokens[3];
+       parser_t *p;
+       int rc = TRUE;
 
        if (bb_basename(filename)[0] == '.')
-               return TRUE;
-
-       f = fopen_for_read(filename);
-       if (f == NULL)
-               return FALSE;
-
-       // alias parsing is not 100% correct (no correct handling of continuation lines within an alias)!
-
-       while (fgets(line_buffer, sizeof(line_buffer), f)) {
-               int l;
+               goto error;
 
-               *strchrnul(line_buffer, '#') = '\0';
-
-               l = strlen(line_buffer);
-
-               while (l && isspace(line_buffer[l-1])) {
-                       line_buffer[l-1] = '\0';
-                       l--;
-               }
-
-               if (l == 0) {
-                       continuation_line = 0;
-                       continue;
-               }
-
-               if (continuation_line)
-                       continue;
-
-               if (is_conf_command(line_buffer, "alias")) {
-                       char *alias, *mod;
-
-                       if (parse_tag_value(line_buffer + 6, &alias, &mod)) {
-                               /* handle alias as a module dependent on the aliased module */
-                               if (!*current) {
-                                       (*first) = (*current) = xzalloc(sizeof(struct dep_t));
-                               } else {
-                                       (*current)->m_next = xzalloc(sizeof(struct dep_t));
-                                       (*current) = (*current)->m_next;
-                               }
-                               (*current)->m_name = xstrdup(alias);
-                               (*current)->m_isalias = 1;
-
-                               if ((strcmp(mod, "off") == 0) || (strcmp(mod, "null") == 0)) {
-                                       /*(*current)->m_depcnt = 0; - done by xzalloc */
-                                       /*(*current)->m_deparr = 0;*/
-                               } else {
-                                       (*current)->m_depcnt = 1;
-                                       (*current)->m_deparr = xmalloc(sizeof(char *));
-                                       (*current)->m_deparr[0] = xstrdup(mod);
-                               }
-                               /*(*current)->m_next = NULL; - done by xzalloc */
-                       }
-               } else if (is_conf_command(line_buffer, "options")) {
-                       char *mod, *opt;
-
-                       /* split the line in the module/alias name, and options */
-                       if (parse_tag_value(line_buffer + 8, &mod, &opt)) {
-                               struct dep_t *dt;
-
-                               /* find the corresponding module */
-                               for (dt = *first; dt; dt = dt->m_next) {
-                                       if (strcmp(dt->m_name, mod) == 0)
-                                               break;
-                               }
-                               if (dt) {
-                                       if (ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS) {
-                                               char* new_opt = NULL;
-                                               while ((opt = parse_command_string(opt, &new_opt))) {
-                                                       dt->m_options = append_option(dt->m_options, new_opt);
-                                               }
-                                       } else {
-                                               dt->m_options = append_option(dt->m_options, opt);
-                                       }
-                               }
-                       }
-               } else if (is_conf_command(line_buffer, "include")) {
-                       char *includefile;
+       p = config_open2(filename, fopen_for_read);
+       if (p == NULL) {
+               rc = FALSE;
+               goto error;
+       }
 
-                       includefile = skip_whitespace(line_buffer + 8);
-                       include_conf_recursive(conf, includefile);
+       while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) {
+               if (strcmp(tokens[0], "alias") == 0) {
+                       filename2modname(tokens[1], modname);
+                       if (tokens[2] &&
+                           fnmatch(modname, conf->probename, 0) == 0)
+                               llist_add_to(&conf->aliases,
+                                       filename2modname(tokens[2], NULL));
+               } else if (strcmp(tokens[0], "options") == 0) {
+                       if (tokens[2])
+                               add_option(&conf->options, tokens[1], tokens[2]);
+               } else if (strcmp(tokens[0], "include") == 0) {
+                       read_config(conf, tokens[1]);
                } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST &&
-                               (is_conf_command(line_buffer, "blacklist"))) {
-                       char *mod;
-                       struct dep_t *dt;
-
-                       mod = skip_whitespace(line_buffer + 10);
-                       for (dt = *first; dt; dt = dt->m_next) {
-                               if (strcmp(dt->m_name, mod) == 0)
-                                       break;
-                       }
-                       if (dt)
-                               dt->m_isblacklisted = 1;
+                          strcmp(tokens[0], "blacklist") == 0) {
+                       add_to_blacklist(conf, xstrdup(tokens[1]));
                }
-       } /* while (fgets(...)) */
-
-       fclose(f);
-       return TRUE;
-}
-
-static int include_conf_file(struct include_conf_t *conf,
-                            const char *filename)
-{
-       return include_conf_file_act(filename, NULL, conf, 0);
-}
-
-static int include_conf_file2(struct include_conf_t *conf,
-                             const char *filename, const char *oldname)
-{
-       if (include_conf_file(conf, filename) == TRUE)
-               return TRUE;
-       return include_conf_file(conf, oldname);
-}
-
-/*
- * This function builds a list of dependency rules from /lib/modules/`uname -r`/modules.dep.
- * It then fills every modules and aliases with their default options, found by parsing
- * modprobe.conf (or modules.conf, or conf.modules).
- */
-static struct dep_t *build_dep(void)
-{
-       FILE *f;
-       struct utsname un;
-       struct include_conf_t conf = { NULL, NULL };
-       char *filename;
-       int continuation_line = 0;
-       int k_version;
-
-       uname(&un); /* never fails */
-
-       k_version = 0;
-       if (un.release[0] == '2') {
-               k_version = un.release[2] - '0';
        }
-
-       filename = xasprintf(CONFIG_DEFAULT_MODULES_DIR"/%s/"CONFIG_DEFAULT_DEPMOD_FILE, un.release);
-       f = fopen_for_read(filename);
+       config_close(p);
+error:
        if (ENABLE_FEATURE_CLEAN_UP)
-               free(filename);
-       if (f == NULL) {
-               /* Ok, that didn't work.  Fall back to looking in /lib/modules */
-               f = fopen_for_read(CONFIG_DEFAULT_MODULES_DIR"/"CONFIG_DEFAULT_DEPMOD_FILE);
-               if (f == NULL) {
-                       bb_error_msg_and_die("cannot parse "CONFIG_DEFAULT_DEPMOD_FILE);
-               }
-       }
-
-       while (fgets(line_buffer, sizeof(line_buffer), f)) {
-               int l = strlen(line_buffer);
-               char *p = NULL;
-
-               while (l > 0 && isspace(line_buffer[l-1])) {
-                       line_buffer[l-1] = '\0';
-                       l--;
-               }
-
-               if (l == 0) {
-                       continuation_line = 0;
-                       continue;
-               }
-
-               /* Is this a new module dep description? */
-               if (!continuation_line) {
-                       /* find the dep beginning */
-                       char *col = strchr(line_buffer, ':');
-                       char *dot = col;
-
-                       if (col) {
-                               /* This line is a dep description */
-                               const char *mods;
-                               char *modpath;
-                               char *mod;
-
-                               /* Find the beginning of the module file name */
-                               *col = '\0';
-                               mods = bb_basename(line_buffer);
-
-                               /* find the path of the module */
-                               modpath = strchr(line_buffer, '/'); /* ... and this is the path */
-                               if (!modpath)
-                                       modpath = line_buffer; /* module with no path */
-                               /* find the end of the module name in the file name */
-                               if (ENABLE_FEATURE_2_6_MODULES &&
-                                   (k_version > 4) && (col[-3] == '.') &&
-                                   (col[-2] == 'k') && (col[-1] == 'o'))
-                                       dot = col - 3;
-                               else if ((col[-2] == '.') && (col[-1] == 'o'))
-                                       dot = col - 2;
-
-                               mod = xstrndup(mods, dot - mods);
-
-                               /* enqueue new module */
-                               if (!conf.current) {
-                                       conf.first = conf.current = xzalloc(sizeof(struct dep_t));
-                               } else {
-                                       conf.current->m_next = xzalloc(sizeof(struct dep_t));
-                                       conf.current = conf.current->m_next;
-                               }
-                               conf.current->m_name = mod;
-                               conf.current->m_path = xstrdup(modpath);
-                               /*current->m_options = NULL; - xzalloc did it*/
-                               /*current->m_isalias = 0;*/
-                               /*current->m_depcnt = 0;*/
-                               /*current->m_deparr = 0;*/
-                               /*current->m_next = 0;*/
-
-                               p = col + 1;
-                       } else
-                               /* this line is not a dep description */
-                               p = NULL;
-               } else
-                       /* It's a dep description continuation */
-                       p = line_buffer;
-
-               /* p points to the first dependable module; if NULL, no dependable module */
-               if (p && (p = skip_whitespace(p))[0] != '\0') {
-                       char *end = &line_buffer[l-1];
-                       const char *deps;
-                       char *dep;
-                       char *next;
-                       int ext = 0;
-
-                       while (isblank(*end) || (*end == '\\'))
-                               end--;
-
-                       do {
-                               /* search the end of the dependency */
-                               next = strchr(p, ' ');
-                               if (next) {
-                                       *next = '\0';
-                                       next--;
-                               } else
-                                       next = end;
-
-                               /* find the beginning of the module file name */
-                               deps = bb_basename(p);
-                               if (deps == p)
-                                       deps = skip_whitespace(deps);
-
-                               /* find the end of the module name in the file name */
-                               if (ENABLE_FEATURE_2_6_MODULES
-                                && (k_version > 4) && (next[-2] == '.')
-                                && (next[-1] == 'k') && (next[0] == 'o'))
-                                       ext = 3;
-                               else if ((next[-1] == '.') && (next[0] == 'o'))
-                                       ext = 2;
-
-                               /* Cope with blank lines */
-                               if ((next - deps - ext + 1) <= 0)
-                                       continue;
-                               dep = xstrndup(deps, next - deps - ext + 1);
-
-                               /* Add the new dependable module name */
-                               conf.current->m_deparr = xrealloc_vector(conf.current->m_deparr, 2, conf.current->m_depcnt);
-                               conf.current->m_deparr[conf.current->m_depcnt++] = dep;
-
-                               p = next + 2;
-                       } while (next < end);
-               }
-
-               /* is there other dependable module(s) ? */
-               continuation_line = (line_buffer[l-1] == '\\');
-       } /* while (fgets(...)) */
-       fclose(f);
-
-       /*
-        * First parse system-specific options and aliases
-        * as they take precedence over the kernel ones.
-        * >=2.6: we only care about modprobe.conf
-        * <=2.4: we care about modules.conf and conf.modules
-        */
-       {
-               int r = FALSE;
-
-               if (ENABLE_FEATURE_2_6_MODULES) {
-                       if (include_conf_file(&conf, "/etc/modprobe.conf"))
-                               r = TRUE;
-                       if (include_conf_recursive(&conf, "/etc/modprobe.d"))
-                               r = TRUE;
-               }
-               if (ENABLE_FEATURE_2_4_MODULES && !r)
-                       include_conf_file2(&conf,
-                                          "/etc/modules.conf",
-                                          "/etc/conf.modules");
-       }
-
-       /* Only 2.6 has a modules.alias file */
-       if (ENABLE_FEATURE_2_6_MODULES) {
-               /* Parse kernel-declared module aliases */
-               filename = xasprintf(CONFIG_DEFAULT_MODULES_DIR"/%s/modules.alias", un.release);
-               include_conf_file2(&conf,
-                                  filename,
-                                  CONFIG_DEFAULT_MODULES_DIR"/modules.alias");
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       free(filename);
-
-               /* Parse kernel-declared symbol aliases */
-               filename = xasprintf(CONFIG_DEFAULT_MODULES_DIR"/%s/modules.symbols", un.release);
-               include_conf_file2(&conf,
-                                  filename,
-                                  CONFIG_DEFAULT_MODULES_DIR"/modules.symbols");
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       free(filename);
-       }
-
-       return conf.first;
-}
-
-/* return 1 = loaded, 0 = not loaded, -1 = can't tell */
-static int already_loaded(const char *name)
-{
-       FILE *f;
-       int ret;
-
-       f = fopen_for_read("/proc/modules");
-       if (f == NULL)
-               return -1;
-
-       ret = 0;
-       while (fgets(line_buffer, sizeof(line_buffer), f)) {
-               char *p = line_buffer;
-               const char *n = name;
-
-               while (1) {
-                       char cn = *n;
-                       char cp = *p;
-                       if (cp == ' ' || cp == '\0') {
-                               if (cn == '\0') {
-                                       ret = 1; /* match! */
-                                       goto done;
-                               }
-                               break; /* no match on this line, take next one */
-                       }
-                       if (cn == '-') cn = '_';
-                       if (cp == '-') cp = '_';
-                       if (cp != cn)
-                               break; /* no match on this line, take next one */
-                       n++;
-                       p++;
-               }
-       }
- done:
-       fclose(f);
-       return ret;
+               RELEASE_CONFIG_BUFFER(modname);
+       return rc;
 }
 
-static int mod_process(const struct mod_list_t *list, int do_insert)
+static int read_config(struct modprobe_conf *conf, const char *path)
 {
-       int rc = 0;
-       char **argv = NULL;
-       struct mod_opt_t *opts;
-       int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */
-       int argc;
-
-       while (list) {
-               argc = 0;
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       argc_malloc = 0;
-               /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory
-                * each time we allocate memory for argv.
-                * But it is (quite) small amounts of memory that leak each
-                * time a module is loaded,  and it is reclaimed when modprobe
-                * exits anyway (even when standalone shell? Yes --vda).
-                * This could become a problem when loading a module with LOTS of
-                * dependencies, with LOTS of options for each dependencies, with
-                * very little memory on the target... But in that case, the module
-                * would not load because there is no more memory, so there's no
-                * problem. */
-               /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */
-               argv = xmalloc(6 * sizeof(char*));
-               if (do_insert) {
-                       if (already_loaded(list->m_name) != 1) {
-                               argv[argc++] = (char*)"insmod";
-                               if (ENABLE_FEATURE_2_4_MODULES) {
-                                       if (do_syslog)
-                                               argv[argc++] = (char*)"-s";
-                                       if (autoclean)
-                                               argv[argc++] = (char*)"-k";
-                                       if (quiet)
-                                               argv[argc++] = (char*)"-q";
-                                       else if (verbose) /* verbose and quiet are mutually exclusive */
-                                               argv[argc++] = (char*)"-v";
-                               }
-                               argv[argc++] = list->m_path;
-                               if (ENABLE_FEATURE_CLEAN_UP)
-                                       argc_malloc = argc;
-                               opts = list->m_options;
-                               while (opts) {
-                                       /* Add one more option */
-                                       argc++;
-                                       argv = xrealloc(argv, (argc + 1) * sizeof(char*));
-                                       argv[argc-1] = opts->m_opt_val;
-                                       opts = opts->m_next;
-                               }
-                       }
-               } else {
-                       /* modutils uses short name for removal */
-                       if (already_loaded(list->m_name) != 0) {
-                               argv[argc++] = (char*)"rmmod";
-                               if (do_syslog)
-                                       argv[argc++] = (char*)"-s";
-                               argv[argc++] = (char*)list->m_name;
-                               if (ENABLE_FEATURE_CLEAN_UP)
-                                       argc_malloc = argc;
-                       }
-               }
-               argv[argc] = NULL;
-
-               if (argc) {
-                       if (verbose) {
-                               printf("%s module %s\n", do_insert?"Loading":"Unloading", list->m_name);
-                       }
-                       if (!show_only) {
-                               int rc2 = wait4pid(spawn(argv));
-
-                               if (do_insert) {
-                                       rc = rc2; /* only last module matters */
-                               } else if (!rc2) {
-                                       rc = 0; /* success if remove any mod */
-                               }
-                       }
-                       if (ENABLE_FEATURE_CLEAN_UP) {
-                               /* the last value in the array has index == argc, but
-                                * it is the terminating NULL, so we must not free it. */
-                               while (argc_malloc < argc) {
-                                       free(argv[argc_malloc++]);
-                               }
-                       }
-               }
-               if (ENABLE_FEATURE_CLEAN_UP) {
-                       free(argv);
-                       argv = NULL;
-               }
-               list = do_insert ? list->m_prev : list->m_next;
-       }
-       return (show_only) ? 0 : rc;
+       return recursive_action(path, ACTION_RECURSE | ACTION_QUIET,
+                               config_file_action, NULL, conf, 1);
 }
 
-/*
- * Check the matching between a pattern and a module name.
- * We need this as *_* is equivalent to *-*, even in pattern matching.
- */
-static int check_pattern(const char* pat_src, const char* mod_src)
+static char *gather_options(llist_t *first, const char *module, int usecmdline)
 {
-       int ret;
-
-       if (ENABLE_FEATURE_MODPROBE_FANCY_ALIAS) {
-               char* pat;
-               char* mod;
-               char* p;
+       struct modprobe_option *opt;
+       llist_t *n;
+       char *opts = xstrdup("");
+       int optlen = 0;
 
-               pat = xstrdup(pat_src);
-               mod = xstrdup(mod_src);
+       for (n = first; n != NULL; n = n->link) {
+               opt = (struct modprobe_option *) n->data;
 
-               for (p = pat; (p = strchr(p, '-')); *p++ = '_');
-               for (p = mod; (p = strchr(p, '-')); *p++ = '_');
-
-               ret = fnmatch(pat, mod, 0);
-
-               if (ENABLE_FEATURE_CLEAN_UP) {
-                       free(pat);
-                       free(mod);
-               }
+               if (opt->module == NULL && !usecmdline)
+                       continue;
+               if (opt->module != NULL && strcmp(opt->module, module) != 0)
+                       continue;
 
-               return ret;
+               opts = xrealloc(opts, optlen + strlen(opt->option) + 2);
+               optlen += sprintf(opts + optlen, "%s ", opt->option);
        }
-       return fnmatch(pat_src, mod_src, 0);
+       return opts;
 }
 
-/*
- * Builds the dependency list (aka stack) of a module.
- * head: the highest module in the stack (last to insmod, first to rmmod)
- * tail: the lowest module in the stack (first to insmod, last to rmmod)
- */
-static void check_dep(char *mod, struct mod_list_t **head, struct mod_list_t **tail)
+static int do_modprobe(struct modprobe_conf *conf, const char *module)
 {
-       struct mod_list_t *find;
-       struct dep_t *dt;
-       struct mod_opt_t *opt = NULL;
-       char *path = NULL;
-
-       /* Search for the given module name amongst all dependency rules.
-        * The module name in a dependency rule can be a shell pattern,
-        * so try to match the given module name against such a pattern.
-        * Of course if the name in the dependency rule is a plain string,
-        * then we consider it a pattern, and matching will still work. */
-       for (dt = depend; dt; dt = dt->m_next) {
-               if (check_pattern(dt->m_name, mod) == 0) {
-                       break;
-               }
-       }
-
-       if (!dt) {
-               bb_error_msg("module %s not found", mod);
-               return;
-       }
+       RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN);
+       llist_t *deps = NULL;
+       char *fn, *options, *colon = NULL, *tokens[2];
+       parser_t *p;
+       int rc = -1;
+
+       p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, fopen_for_read);
+       if (p == NULL)
+               goto error;
+
+       while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
+               colon = last_char_is(tokens[0], ':');
+               if (colon == NULL)
+                       continue;
 
-       // resolve alias names
-       while (dt->m_isalias) {
-               if (dt->m_depcnt == 1) {
-                       struct dep_t *adt;
+               filename2modname(tokens[0], modname);
+               if (strcmp(modname, module) == 0)
+                       break;
 
-                       for (adt = depend; adt; adt = adt->m_next) {
-                               if (check_pattern(adt->m_name, dt->m_deparr[0]) == 0 &&
-                                               !(ENABLE_FEATURE_MODPROBE_BLACKLIST &&
-                                                       adt->m_isblacklisted))
-                                       break;
-                       }
-                       if (adt) {
-                               /* This is the module we are aliased to */
-                               struct mod_opt_t *opts = dt->m_options;
-                               /* Option of the alias are appended to the options of the module */
-                               while (opts) {
-                                       adt->m_options = append_option(adt->m_options, opts->m_opt_val);
-                                       opts = opts->m_next;
-                               }
-                               dt = adt;
-                       } else {
-                               bb_error_msg("module %s not found", mod);
-                               return;
-                       }
-               } else {
-                       bb_error_msg("bad alias %s", dt->m_name);
-                       return;
-               }
+               colon = NULL;
        }
-
-       mod = dt->m_name;
-       path = dt->m_path;
-       opt = dt->m_options;
-
-       // search for duplicates
-       for (find = *head; find; find = find->m_next) {
-               if (strcmp(mod, find->m_name) == 0) {
-                       // found -> dequeue it
-
-                       if (find->m_prev)
-                               find->m_prev->m_next = find->m_next;
-                       else
-                               *head = find->m_next;
-
-                       if (find->m_next)
-                               find->m_next->m_prev = find->m_prev;
-                       else
-                               *tail = find->m_prev;
-
-                       break; // there can be only one duplicate
+       if (colon == NULL)
+               goto error_not_found;
+
+       colon[0] = '\0';
+       llist_add_to(&deps, xstrdup(tokens[0]));
+       if (tokens[1])
+               string_to_llist(tokens[1], &deps, " ");
+
+       if (!(option_mask32 & MODPROBE_OPT_REMOVE))
+               deps = llist_rev(deps);
+
+       rc = 0;
+       while (deps && rc == 0) {
+               fn = llist_pop(&deps);
+               filename2modname(fn, modname);
+               if (option_mask32 & MODPROBE_OPT_REMOVE) {
+                       if (bb_delete_module(modname, O_EXCL) != 0)
+                               rc = errno;
+               } else if (llist_find(loaded, modname) == NULL) {
+                       options = gather_options(conf->options, modname,
+                                                strcmp(modname, module) == 0);
+                       rc = bb_init_module(fn, options);
+                       if (rc == 0)
+                               llist_add_to(&loaded, xstrdup(modname));
+                       if (ENABLE_FEATURE_CLEAN_UP)
+                               free(options);
                }
-       }
-
-       if (!find) { // did not find a duplicate
-               find = xzalloc(sizeof(struct mod_list_t));
-               find->m_name = mod;
-               find->m_path = path;
-               find->m_options = opt;
-       }
-
-       // enqueue at tail
-       if (*tail)
-               (*tail)->m_next = find;
-       find->m_prev = *tail;
-       find->m_next = NULL; /* possibly NOT done by xzalloc! */
-
-       if (!*head)
-               *head = find;
-       *tail = find;
-
-       if (dt) {
-               int i;
 
-               /* Add all dependable module for that new module */
-               for (i = 0; i < dt->m_depcnt; i++)
-                       check_dep(dt->m_deparr[i], head, tail);
-       }
-}
-
-static int mod_insert(char **argv)
-{
-       struct mod_list_t *tail = NULL;
-       struct mod_list_t *head = NULL;
-       char *modname = *argv++;
-       int rc;
-
-       // get dep list for module mod
-       check_dep(modname, &head, &tail);
-
-       rc = 1;
-       if (head && tail) {
-               while (*argv)
-                       head->m_options = append_option(head->m_options, *argv++);
-
-               // process tail ---> head
-               rc = mod_process(tail, 1);
-               if (rc) {
-                       /*
-                        * In case of using udev, multiple instances of modprobe can be
-                        * spawned to load the same module (think of two same usb devices,
-                        * for example; or cold-plugging at boot time). Thus we shouldn't
-                        * fail if the module was loaded, and not by us.
-                        */
-                       if (already_loaded(modname))
-                               rc = 0;
-               }
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       free(fn);
        }
-       return rc;
-}
-
-static int mod_remove(char *modname)
-{
-       static const struct mod_list_t rm_a_dummy = { "-a", NULL, NULL, NULL, NULL };
-
-       int rc;
-       struct mod_list_t *head = NULL;
-       struct mod_list_t *tail = NULL;
 
-       if (modname)
-               check_dep(modname, &head, &tail);
-       else  // autoclean
-               head = tail = (struct mod_list_t*) &rm_a_dummy;
-
-       rc = 1;
-       if (head && tail)
-               rc = mod_process(head, 0);  // process head ---> tail
+error_not_found:
+       config_close(p);
+error:
+       if (ENABLE_FEATURE_CLEAN_UP)
+               RELEASE_CONFIG_BUFFER(modname);
+       if (rc > 0 && !(option_mask32 & INSMOD_OPT_SILENT))
+               bb_error_msg("Failed to %sload module %s: %s.",
+                            (option_mask32 & MODPROBE_OPT_REMOVE) ? "un" : "",
+                            module, moderror(rc));
        return rc;
 }
 
 int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int modprobe_main(int argc UNUSED_PARAM, char **argv)
 {
-       int rc = EXIT_SUCCESS;
-       unsigned opt;
-       char *unused;
+       struct utsname uts;
+       int num_modules, i, rc;
+       llist_t *options = NULL;
+       parser_t *parser;
 
        opt_complementary = "q-v:v-q";
-       opt = getopt32(argv, MAIN_OPT_STR, &unused, &unused);
+       getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS,
+                NULL, NULL);
        argv += optind;
-
-       if (opt & (DUMP_CONF_EXIT | LIST_ALL))
-               return EXIT_SUCCESS;
-       if (opt & (RESTRICT_DIR | CONFIG_FILE))
-               bb_error_msg_and_die("-t and -C not supported");
-
-       depend = build_dep();
-
-       if (!depend)
-               bb_error_msg_and_die("cannot parse "CONFIG_DEFAULT_DEPMOD_FILE);
-
-       if (remove_opt) {
-               do {
-                       /* (*argv) can be NULL here */
-                       if (mod_remove(*argv)) {
-                               bb_perror_msg("failed to %s module %s", "remove",
-                                               *argv);
-                               rc = EXIT_FAILURE;
-                       }
-               } while (*argv && *++argv);
+       argc -= optind;
+
+       if (option_mask32 & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY |
+                            MODPROBE_OPT_SHOW_ONLY))
+               bb_error_msg_and_die("not supported");
+
+       /* goto modules location */
+       xchdir(CONFIG_DEFAULT_MODULES_DIR);
+       uname(&uts);
+       xchdir(uts.release);
+
+       if (option_mask32 & (MODPROBE_OPT_REMOVE | MODPROBE_OPT_INSERT_ALL)) {
+               /* each parameter is a module name */
+               num_modules = argc;
+               if (num_modules == 0) {
+                       if (bb_delete_module(NULL, O_NONBLOCK|O_EXCL) != 0)
+                               bb_perror_msg_and_die("rmmod");
+                       return EXIT_SUCCESS;
+               }
        } else {
-               if (!*argv)
-                       bb_error_msg_and_die("no module or pattern provided");
+               /* the only module, the rest of parameters are options */
+               num_modules = 1;
+               add_option(&options, NULL, parse_cmdline_module_options(argv));
+       }
 
-               if (mod_insert(argv))
-                       bb_perror_msg_and_die("failed to %s module %s", "load", *argv);
+       /* cache modules */
+       parser = config_open2("/proc/modules", fopen_for_read);
+       if (parser) {
+               char *s;
+               while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY))
+                       llist_add_to(&loaded, xstrdup(s));
+               config_close(parser);
        }
 
-       /* Here would be a good place to free up memory allocated during the dependencies build. */
+       for (i = 0; i < num_modules; i++) {
+               struct modprobe_conf *conf;
+
+               conf = xzalloc(sizeof(struct modprobe_conf));
+               conf->options = options;
+               filename2modname(argv[i], conf->probename);
+               read_config(conf, "/etc/modprobe.conf");
+               read_config(conf, "/etc/modprobe.d");
+               if (ENABLE_FEATURE_MODUTILS_SYMBOLS &&
+                   conf->aliases == NULL && strncmp(argv[i], "symbol:", 7) == 0)
+                       read_config(conf, "modules.symbols");
+
+               if (ENABLE_FEATURE_MODUTILS_ALIAS && conf->aliases == NULL)
+                       read_config(conf, "modules.alias");
+
+               if (conf->aliases == NULL) {
+                       /* Try if module by literal name is found; literal
+                        * names are blacklist only if '-b' is given. */
+                       if (!(option_mask32 & MODPROBE_OPT_BLACKLIST) ||
+                           check_blacklist(conf, conf->probename)) {
+                               rc = do_modprobe(conf, conf->probename);
+                               if (rc < 0 && !(option_mask32 & INSMOD_OPT_SILENT))
+                                       bb_error_msg("Module %s not found.", argv[i]);
+                       }
+               } else {
+                       /* Probe all aliases */
+                       while (conf->aliases != NULL) {
+                               char *realname = llist_pop(&conf->aliases);
+                               if (check_blacklist(conf, realname))
+                                       do_modprobe(conf, realname);
+                               if (ENABLE_FEATURE_CLEAN_UP)
+                                       free(realname);
+                       }
+               }
+       }
 
-       return rc;
+       return EXIT_SUCCESS;
 }
diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c
new file mode 100644 (file)
index 0000000..2bc4bda
--- /dev/null
@@ -0,0 +1,3936 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini insmod implementation for busybox
+ *
+ * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64,
+ * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * and Ron Alder <alder@lineo.com>
+ *
+ * Rodney Radford <rradford@mindspring.com> 17-Aug-2004.
+ *   Added x86_64 support.
+ *
+ * Miles Bader <miles@gnu.org> added NEC V850E support.
+ *
+ * Modified by Bryan Rittmeyer <bryan@ixiacom.com> to support SH4
+ * and (theoretically) SH3. I have only tested SH4 in little endian mode.
+ *
+ * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
+ * Nicolas Ferre <nicolas.ferre@alcove.fr> to support ARM7TDMI.  Only
+ * very minor changes required to also work with StrongArm and presumably
+ * all ARM based systems.
+ *
+ * Yoshinori Sato <ysato@users.sourceforge.jp> 19-May-2004.
+ *   added Renesas H8/300 support.
+ *
+ * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
+ *   Integrated support for sh64 (SH-5), from preliminary modutils
+ *   patches from Benedict Gaster <benedict.gaster@superh.com>.
+ *   Currently limited to support for 32bit ABI.
+ *
+ * Magnus Damm <damm@opensource.se> 22-May-2002.
+ *   The plt and got code are now using the same structs.
+ *   Added generic linked list code to fully support PowerPC.
+ *   Replaced the mess in arch_apply_relocation() with architecture blocks.
+ *   The arch_create_got() function got cleaned up with architecture blocks.
+ *   These blocks should be easy maintain and sync with obj_xxx.c in modutils.
+ *
+ * Magnus Damm <damm@opensource.se> added PowerPC support 20-Feb-2001.
+ *   PowerPC specific code stolen from modutils-2.3.16,
+ *   written by Paul Mackerras, Copyright 1996, 1997 Linux International.
+ *   I've only tested the code on mpc8xx platforms in big-endian mode.
+ *   Did some cleanup and added USE_xxx_ENTRIES...
+ *
+ * Quinn Jensen <jensenq@lineo.com> added MIPS support 23-Feb-2001.
+ *   based on modutils-2.4.2
+ *   MIPS specific support for Elf loading and relocation.
+ *   Copyright 1996, 1997 Linux International.
+ *   Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>
+ *
+ * Based almost entirely on the Linux modutils-2.3.11 implementation.
+ *   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
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "modutils.h"
+#include <libgen.h>
+#include <sys/utsname.h>
+
+#if ENABLE_FEATURE_INSMOD_LOADINKMEM
+#define LOADBITS 0
+#else
+#define LOADBITS 1
+#endif
+
+/* Alpha */
+#if defined(__alpha__)
+#define MATCH_MACHINE(x) (x == EM_ALPHA)
+#define SHT_RELM       SHT_RELA
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+/* ARM support */
+#if defined(__arm__)
+#define MATCH_MACHINE(x) (x == EM_ARM)
+#define SHT_RELM       SHT_REL
+#define Elf32_RelM     Elf32_Rel
+#define ELFCLASSM      ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#endif
+
+/* blackfin */
+#if defined(BFIN)
+#define MATCH_MACHINE(x) (x == EM_BLACKFIN)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+
+/* CRIS */
+#if defined(__cris__)
+#define MATCH_MACHINE(x) (x == EM_CRIS)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#ifndef EM_CRIS
+#define EM_CRIS 76
+#define R_CRIS_NONE 0
+#define R_CRIS_32   3
+#endif
+#endif
+
+/* H8/300 */
+#if defined(__H8300H__) || defined(__H8300S__)
+#define MATCH_MACHINE(x) (x == EM_H8_300)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define USE_SINGLE
+#define SYMBOL_PREFIX  "_"
+#endif
+
+/* PA-RISC / HP-PA */
+#if defined(__hppa__)
+#define MATCH_MACHINE(x) (x == EM_PARISC)
+#define SHT_RELM       SHT_RELA
+#if defined(__LP64__)
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#else
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+#endif
+
+/* x86 */
+#if defined(__i386__)
+#ifndef EM_486
+#define MATCH_MACHINE(x) (x == EM_386)
+#else
+#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486)
+#endif
+#define SHT_RELM       SHT_REL
+#define Elf32_RelM     Elf32_Rel
+#define ELFCLASSM      ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+#endif
+
+/* IA64, aka Itanium */
+#if defined(__ia64__)
+#define MATCH_MACHINE(x) (x == EM_IA_64)
+#define SHT_RELM       SHT_RELA
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+/* m68k */
+#if defined(__mc68000__)
+#define MATCH_MACHINE(x) (x == EM_68K)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+#endif
+
+/* Microblaze */
+#if defined(__microblaze__)
+#define USE_SINGLE
+#include <linux/elf-em.h>
+#define MATCH_MACHINE(x) (x == EM_XILINX_MICROBLAZE)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+
+/* MIPS */
+#if defined(__mips__)
+#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE)
+#define SHT_RELM       SHT_REL
+#define Elf32_RelM     Elf32_Rel
+#define ELFCLASSM      ELFCLASS32
+/* Account for ELF spec changes.  */
+#ifndef EM_MIPS_RS3_LE
+#ifdef EM_MIPS_RS4_BE
+#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE
+#else
+#define EM_MIPS_RS3_LE 10
+#endif
+#endif /* !EM_MIPS_RS3_LE */
+#define ARCHDATAM       "__dbe_table"
+#endif
+
+/* Nios II */
+#if defined(__nios2__)
+#define MATCH_MACHINE(x) (x == EM_ALTERA_NIOS2)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+
+/* PowerPC */
+#if defined(__powerpc64__)
+#define MATCH_MACHINE(x) (x == EM_PPC64)
+#define SHT_RELM       SHT_RELA
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#elif defined(__powerpc__)
+#define MATCH_MACHINE(x) (x == EM_PPC)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 16
+#define USE_PLT_LIST
+#define LIST_ARCHTYPE ElfW(Addr)
+#define USE_LIST
+#define ARCHDATAM       "__ftr_fixup"
+#endif
+
+/* S390 */
+#if defined(__s390__)
+#define MATCH_MACHINE(x) (x == EM_S390)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#endif
+
+/* SuperH */
+#if defined(__sh__)
+#define MATCH_MACHINE(x) (x == EM_SH)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+/* the SH changes have only been tested in =little endian= mode */
+/* I'm not sure about big endian, so let's warn: */
+#if defined(__sh__) && BB_BIG_ENDIAN
+# error insmod.c may require changes for use on big endian SH
+#endif
+/* it may or may not work on the SH1/SH2... Error on those also */
+#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__))
+#error insmod.c may require changes for SH1 or SH2 use
+#endif
+#endif
+
+/* Sparc */
+#if defined(__sparc__)
+#define MATCH_MACHINE(x) (x == EM_SPARC)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+
+/* v850e */
+#if defined(__v850e__)
+#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_SINGLE
+#ifndef EM_CYGNUS_V850 /* grumble */
+#define EM_CYGNUS_V850 0x9080
+#endif
+#define SYMBOL_PREFIX  "_"
+#endif
+
+/* X86_64  */
+#if defined(__x86_64__)
+#define MATCH_MACHINE(x) (x == EM_X86_64)
+#define SHT_RELM       SHT_RELA
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+#ifndef SHT_RELM
+#error Sorry, but insmod.c does not yet support this architecture...
+#endif
+
+
+//----------------------------------------------------------------------------
+//--------modutils module.h, lines 45-242
+//----------------------------------------------------------------------------
+
+/* Definitions for the Linux module syscall interface.
+   Copyright 1996, 1997 Linux International.
+
+   Contributed by Richard Henderson <rth@tamu.edu>
+
+   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.  */
+
+
+#ifndef MODUTILS_MODULE_H
+
+/*======================================================================*/
+/* For sizeof() which are related to the module platform and not to the
+   environment isnmod is running in, use sizeof_xx instead of sizeof(xx).  */
+
+#define tgt_sizeof_char                sizeof(char)
+#define tgt_sizeof_short       sizeof(short)
+#define tgt_sizeof_int         sizeof(int)
+#define tgt_sizeof_long                sizeof(long)
+#define tgt_sizeof_char_p      sizeof(char *)
+#define tgt_sizeof_void_p      sizeof(void *)
+#define tgt_long               long
+
+#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64)
+#undef tgt_sizeof_long
+#undef tgt_sizeof_char_p
+#undef tgt_sizeof_void_p
+#undef tgt_long
+enum {
+       tgt_sizeof_long = 8,
+       tgt_sizeof_char_p = 8,
+       tgt_sizeof_void_p = 8
+};
+#define tgt_long               long long
+#endif
+
+/*======================================================================*/
+/* The structures used in Linux 2.1.  */
+
+/* Note: new_module_symbol does not use tgt_long intentionally */
+struct new_module_symbol {
+       unsigned long value;
+       unsigned long name;
+};
+
+struct new_module_persist;
+
+struct new_module_ref {
+       unsigned tgt_long dep;          /* kernel addresses */
+       unsigned tgt_long ref;
+       unsigned tgt_long next_ref;
+};
+
+struct new_module {
+       unsigned tgt_long size_of_struct;       /* == sizeof(module) */
+       unsigned tgt_long next;
+       unsigned tgt_long name;
+       unsigned tgt_long size;
+
+       tgt_long usecount;
+       unsigned tgt_long flags;                /* AUTOCLEAN et al */
+
+       unsigned nsyms;
+       unsigned ndeps;
+
+       unsigned tgt_long syms;
+       unsigned tgt_long deps;
+       unsigned tgt_long refs;
+       unsigned tgt_long init;
+       unsigned tgt_long cleanup;
+       unsigned tgt_long ex_table_start;
+       unsigned tgt_long ex_table_end;
+#ifdef __alpha__
+       unsigned tgt_long gp;
+#endif
+       /* Everything after here is extension.  */
+       unsigned tgt_long persist_start;
+       unsigned tgt_long persist_end;
+       unsigned tgt_long can_unload;
+       unsigned tgt_long runsize;
+       const char *kallsyms_start;     /* All symbols for kernel debugging */
+       const char *kallsyms_end;
+       const char *archdata_start;     /* arch specific data for module */
+       const char *archdata_end;
+       const char *kernel_data;        /* Reserved for kernel internal use */
+};
+
+#ifdef ARCHDATAM
+#define ARCHDATA_SEC_NAME ARCHDATAM
+#else
+#define ARCHDATA_SEC_NAME "__archdata"
+#endif
+#define KALLSYMS_SEC_NAME "__kallsyms"
+
+
+struct new_module_info {
+       unsigned long addr;
+       unsigned long size;
+       unsigned long flags;
+       long usecount;
+};
+
+/* Bits of module.flags.  */
+enum {
+       NEW_MOD_RUNNING = 1,
+       NEW_MOD_DELETED = 2,
+       NEW_MOD_AUTOCLEAN = 4,
+       NEW_MOD_VISITED = 8,
+       NEW_MOD_USED_ONCE = 16
+};
+
+int init_module(const char *name, const struct new_module *);
+int query_module(const char *name, int which, void *buf,
+               size_t bufsize, size_t *ret);
+
+/* Values for query_module's which.  */
+enum {
+       QM_MODULES = 1,
+       QM_DEPS = 2,
+       QM_REFS = 3,
+       QM_SYMBOLS = 4,
+       QM_INFO = 5
+};
+
+/*======================================================================*/
+/* The system calls unchanged between 2.0 and 2.1.  */
+
+unsigned long create_module(const char *, size_t);
+int delete_module(const char *module, unsigned int flags);
+
+
+#endif /* module.h */
+
+//----------------------------------------------------------------------------
+//--------end of modutils module.h
+//----------------------------------------------------------------------------
+
+
+
+//----------------------------------------------------------------------------
+//--------modutils obj.h, lines 253-462
+//----------------------------------------------------------------------------
+
+/* Elf object file loading and relocation routines.
+   Copyright 1996, 1997 Linux International.
+
+   Contributed by Richard Henderson <rth@tamu.edu>
+
+   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.  */
+
+
+#ifndef MODUTILS_OBJ_H
+
+/* The relocatable object is manipulated using elfin types.  */
+
+#include <elf.h>
+#include <endian.h>
+
+#ifndef ElfW
+# if ELFCLASSM == ELFCLASS32
+#  define ElfW(x)  Elf32_ ## x
+#  define ELFW(x)  ELF32_ ## x
+# else
+#  define ElfW(x)  Elf64_ ## x
+#  define ELFW(x)  ELF64_ ## x
+# endif
+#endif
+
+/* For some reason this is missing from some ancient C libraries....  */
+#ifndef ELF32_ST_INFO
+# define ELF32_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#ifndef ELF64_ST_INFO
+# define ELF64_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#define ELF_ST_BIND(info) ELFW(ST_BIND)(info)
+#define ELF_ST_TYPE(info) ELFW(ST_TYPE)(info)
+#define ELF_ST_INFO(bind, type) ELFW(ST_INFO)(bind, type)
+#define ELF_R_TYPE(val) ELFW(R_TYPE)(val)
+#define ELF_R_SYM(val) ELFW(R_SYM)(val)
+
+struct obj_string_patch;
+struct obj_symbol_patch;
+
+struct obj_section
+{
+       ElfW(Shdr) header;
+       const char *name;
+       char *contents;
+       struct obj_section *load_next;
+       int idx;
+};
+
+struct obj_symbol
+{
+       struct obj_symbol *next;        /* hash table link */
+       const char *name;
+       unsigned long value;
+       unsigned long size;
+       int secidx;                     /* the defining section index/module */
+       int info;
+       int ksymidx;                    /* for export to the kernel symtab */
+       int referenced;         /* actually used in the link */
+};
+
+/* Hardcode the hash table size.  We shouldn't be needing so many
+   symbols that we begin to degrade performance, and we get a big win
+   by giving the compiler a constant divisor.  */
+
+#define HASH_BUCKETS  521
+
+struct obj_file {
+       ElfW(Ehdr) header;
+       ElfW(Addr) baseaddr;
+       struct obj_section **sections;
+       struct obj_section *load_order;
+       struct obj_section **load_order_search_start;
+       struct obj_string_patch *string_patches;
+       struct obj_symbol_patch *symbol_patches;
+       int (*symbol_cmp)(const char *, const char *);
+       unsigned long (*symbol_hash)(const char *);
+       unsigned long local_symtab_size;
+       struct obj_symbol **local_symtab;
+       struct obj_symbol *symtab[HASH_BUCKETS];
+};
+
+enum obj_reloc {
+       obj_reloc_ok,
+       obj_reloc_overflow,
+       obj_reloc_dangerous,
+       obj_reloc_unhandled
+};
+
+struct obj_string_patch {
+       struct obj_string_patch *next;
+       int reloc_secidx;
+       ElfW(Addr) reloc_offset;
+       ElfW(Addr) string_offset;
+};
+
+struct obj_symbol_patch {
+       struct obj_symbol_patch *next;
+       int reloc_secidx;
+       ElfW(Addr) reloc_offset;
+       struct obj_symbol *sym;
+};
+
+
+/* Generic object manipulation routines.  */
+
+static unsigned long obj_elf_hash(const char *);
+
+static unsigned long obj_elf_hash_n(const char *, unsigned long len);
+
+static struct obj_symbol *obj_find_symbol(struct obj_file *f,
+                                        const char *name);
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file *f,
+                                 struct obj_symbol *sym);
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+static void obj_set_symbol_compare(struct obj_file *f,
+                           int (*cmp)(const char *, const char *),
+                           unsigned long (*hash)(const char *));
+#endif
+
+static struct obj_section *obj_find_section(struct obj_file *f,
+                                          const char *name);
+
+static void obj_insert_section_load_order(struct obj_file *f,
+                                   struct obj_section *sec);
+
+static struct obj_section *obj_create_alloced_section(struct obj_file *f,
+                                               const char *name,
+                                               unsigned long align,
+                                               unsigned long size);
+
+static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
+                                                     const char *name,
+                                                     unsigned long align,
+                                                     unsigned long size);
+
+static void *obj_extend_section(struct obj_section *sec, unsigned long more);
+
+static void obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                    const char *string);
+
+static void obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                    struct obj_symbol *sym);
+
+static void obj_check_undefineds(struct obj_file *f);
+
+static void obj_allocate_commons(struct obj_file *f);
+
+static unsigned long obj_load_size(struct obj_file *f);
+
+static int obj_relocate(struct obj_file *f, ElfW(Addr) base);
+
+static struct obj_file *obj_load(FILE *f, int loadprogbits);
+
+static int obj_create_image(struct obj_file *f, char *image);
+
+/* Architecture specific manipulation routines.  */
+
+static struct obj_file *arch_new_file(void);
+
+static struct obj_section *arch_new_section(void);
+
+static struct obj_symbol *arch_new_symbol(void);
+
+static enum obj_reloc arch_apply_relocation(struct obj_file *f,
+                                     struct obj_section *targsec,
+                                     /*struct obj_section *symsec,*/
+                                     struct obj_symbol *sym,
+                                     ElfW(RelM) *rel, ElfW(Addr) value);
+
+static void arch_create_got(struct obj_file *f);
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license);
+#endif /* FEATURE_CHECK_TAINTED_MODULE */
+#endif /* obj.h */
+//----------------------------------------------------------------------------
+//--------end of modutils obj.h
+//----------------------------------------------------------------------------
+
+
+/* SPFX is always a string, so it can be concatenated to string constants.  */
+#ifdef SYMBOL_PREFIX
+#define SPFX   SYMBOL_PREFIX
+#else
+#define SPFX   ""
+#endif
+
+enum { STRVERSIONLEN = 64 };
+
+/*======================================================================*/
+
+#define flag_force_load (option_mask32 & INSMOD_OPT_FORCE)
+#define flag_autoclean (option_mask32 & INSMOD_OPT_KERNELD)
+#define flag_verbose (option_mask32 & INSMOD_OPT_VERBOSE)
+#define flag_quiet (option_mask32 & INSMOD_OPT_SILENT)
+#define flag_noexport (option_mask32 & INSMOD_OPT_NO_EXPORT)
+#define flag_print_load_map (option_mask32 & INSMOD_OPT_PRINT_MAP)
+
+/*======================================================================*/
+
+#if defined(USE_LIST)
+
+struct arch_list_entry
+{
+       struct arch_list_entry *next;
+       LIST_ARCHTYPE addend;
+       int offset;
+       int inited : 1;
+};
+
+#endif
+
+#if defined(USE_SINGLE)
+
+struct arch_single_entry
+{
+       int offset;
+       int inited : 1;
+       int allocated : 1;
+};
+
+#endif
+
+#if defined(__mips__)
+struct mips_hi16
+{
+       struct mips_hi16 *next;
+       ElfW(Addr) *addr;
+       ElfW(Addr) value;
+};
+#endif
+
+struct arch_file {
+       struct obj_file root;
+#if defined(USE_PLT_ENTRIES)
+       struct obj_section *plt;
+#endif
+#if defined(USE_GOT_ENTRIES)
+       struct obj_section *got;
+#endif
+#if defined(__mips__)
+       struct mips_hi16 *mips_hi16_list;
+#endif
+};
+
+struct arch_symbol {
+       struct obj_symbol root;
+#if defined(USE_PLT_ENTRIES)
+#if defined(USE_PLT_LIST)
+       struct arch_list_entry *pltent;
+#else
+       struct arch_single_entry pltent;
+#endif
+#endif
+#if defined(USE_GOT_ENTRIES)
+       struct arch_single_entry gotent;
+#endif
+};
+
+
+struct external_module {
+       const char *name;
+       ElfW(Addr) addr;
+       int used;
+       size_t nsyms;
+       struct new_module_symbol *syms;
+};
+
+static struct new_module_symbol *ksyms;
+static size_t nksyms;
+
+static struct external_module *ext_modules;
+static int n_ext_modules;
+static int n_ext_modules_used;
+
+/*======================================================================*/
+
+
+static struct obj_file *arch_new_file(void)
+{
+       struct arch_file *f;
+       f = xzalloc(sizeof(*f));
+       return &f->root; /* it's a first member */
+}
+
+static struct obj_section *arch_new_section(void)
+{
+       return xzalloc(sizeof(struct obj_section));
+}
+
+static struct obj_symbol *arch_new_symbol(void)
+{
+       struct arch_symbol *sym;
+       sym = xzalloc(sizeof(*sym));
+       return &sym->root;
+}
+
+static enum obj_reloc
+arch_apply_relocation(struct obj_file *f,
+                               struct obj_section *targsec,
+                               /*struct obj_section *symsec,*/
+                               struct obj_symbol *sym,
+                               ElfW(RelM) *rel, ElfW(Addr) v)
+{
+#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \
+ || defined(__sh__) || defined(__s390__) || defined(__x86_64__) \
+ || defined(__powerpc__) || defined(__mips__)
+       struct arch_file *ifile = (struct arch_file *) f;
+#endif
+       enum obj_reloc ret = obj_reloc_ok;
+       ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset);
+#if defined(__arm__) || defined(__H8300H__) || defined(__H8300S__) \
+ || defined(__i386__) || defined(__mc68000__) || defined(__microblaze__) \
+ || defined(__mips__) || defined(__nios2__) || defined(__powerpc__) \
+ || defined(__s390__) || defined(__sh__) || defined(__x86_64__)
+       ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset;
+#endif
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+       struct arch_symbol *isym = (struct arch_symbol *) sym;
+#endif
+#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \
+ || defined(__sh__) || defined(__s390__)
+#if defined(USE_GOT_ENTRIES)
+       ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0;
+#endif
+#endif
+#if defined(USE_PLT_ENTRIES)
+       ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0;
+       unsigned long *ip;
+# if defined(USE_PLT_LIST)
+       struct arch_list_entry *pe;
+# else
+       struct arch_single_entry *pe;
+# endif
+#endif
+
+       switch (ELF_R_TYPE(rel->r_info)) {
+
+#if defined(__arm__)
+
+               case R_ARM_NONE:
+                       break;
+
+               case R_ARM_ABS32:
+                       *loc += v;
+                       break;
+
+               case R_ARM_GOT32:
+                       goto bb_use_got;
+
+               case R_ARM_GOTPC:
+                       /* relative reloc, always to _GLOBAL_OFFSET_TABLE_
+                        * (which is .got) similar to branch,
+                        * but is full 32 bits relative */
+
+                       *loc += got - dot;
+                       break;
+
+               case R_ARM_PC24:
+               case R_ARM_PLT32:
+                       goto bb_use_plt;
+
+               case R_ARM_GOTOFF: /* address relative to the got */
+                       *loc += v - got;
+                       break;
+
+#elif defined(__cris__)
+
+               case R_CRIS_NONE:
+                       break;
+
+               case R_CRIS_32:
+                       /* CRIS keeps the relocation value in the r_addend field and
+                        * should not use whats in *loc at all
+                        */
+                       *loc = v;
+                       break;
+
+#elif defined(__H8300H__) || defined(__H8300S__)
+
+               case R_H8_DIR24R8:
+                       loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1);
+                       *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v);
+                       break;
+               case R_H8_DIR24A8:
+                       *loc += v;
+                       break;
+               case R_H8_DIR32:
+               case R_H8_DIR32A16:
+                       *loc += v;
+                       break;
+               case R_H8_PCREL16:
+                       v -= dot + 2;
+                       if ((ElfW(Sword))v > 0x7fff ||
+                           (ElfW(Sword))v < -(ElfW(Sword))0x8000)
+                               ret = obj_reloc_overflow;
+                       else
+                               *(unsigned short *)loc = v;
+                       break;
+               case R_H8_PCREL8:
+                       v -= dot + 1;
+                       if ((ElfW(Sword))v > 0x7f ||
+                           (ElfW(Sword))v < -(ElfW(Sword))0x80)
+                               ret = obj_reloc_overflow;
+                       else
+                               *(unsigned char *)loc = v;
+                       break;
+
+#elif defined(__i386__)
+
+               case R_386_NONE:
+                       break;
+
+               case R_386_32:
+                       *loc += v;
+                       break;
+
+               case R_386_PLT32:
+               case R_386_PC32:
+               case R_386_GOTOFF:
+                       *loc += v - dot;
+                       break;
+
+               case R_386_GLOB_DAT:
+               case R_386_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_386_RELATIVE:
+                       *loc += f->baseaddr;
+                       break;
+
+               case R_386_GOTPC:
+                       *loc += got - dot;
+                       break;
+
+               case R_386_GOT32:
+                       goto bb_use_got;
+                       break;
+
+#elif defined(__microblaze__)
+               case R_MICROBLAZE_NONE:
+               case R_MICROBLAZE_64_NONE:
+               case R_MICROBLAZE_32_SYM_OP_SYM:
+               case R_MICROBLAZE_32_PCREL:
+                       break;
+
+               case R_MICROBLAZE_64_PCREL: {
+                       /* dot is the address of the current instruction.
+                        * v is the target symbol address.
+                        * So we need to extract the offset in the code,
+                        * adding v, then subtrating the current address
+                        * of this instruction.
+                        * Ex: "IMM 0xFFFE  bralid 0x0000" = "bralid 0xFFFE0000"
+                        */
+
+                       /* Get split offset stored in code */
+                       unsigned int temp = (loc[0] & 0xFFFF) << 16 |
+                                               (loc[1] & 0xFFFF);
+
+                       /* Adjust relative offset. -4 adjustment required
+                        * because dot points to the IMM insn, but branch
+                        * is computed relative to the branch instruction itself.
+                        */
+                       temp += v - dot - 4;
+
+                       /* Store back into code */
+                       loc[0] = (loc[0] & 0xFFFF0000) | temp >> 16;
+                       loc[1] = (loc[1] & 0xFFFF0000) | (temp & 0xFFFF);
+
+                       break;
+               }
+
+               case R_MICROBLAZE_32:
+                       *loc += v;
+                       break;
+
+               case R_MICROBLAZE_64: {
+                       /* Get split pointer stored in code */
+                       unsigned int temp1 = (loc[0] & 0xFFFF) << 16 |
+                                               (loc[1] & 0xFFFF);
+
+                       /* Add reloc offset */
+                       temp1+=v;
+
+                       /* Store back into code */
+                       loc[0] = (loc[0] & 0xFFFF0000) | temp1 >> 16;
+                       loc[1] = (loc[1] & 0xFFFF0000) | (temp1 & 0xFFFF);
+
+                       break;
+               }
+
+               case R_MICROBLAZE_32_PCREL_LO:
+               case R_MICROBLAZE_32_LO:
+               case R_MICROBLAZE_SRO32:
+               case R_MICROBLAZE_SRW32:
+                       ret = obj_reloc_unhandled;
+                       break;
+
+#elif defined(__mc68000__)
+
+               case R_68K_NONE:
+                       break;
+
+               case R_68K_32:
+                       *loc += v;
+                       break;
+
+               case R_68K_8:
+                       if (v > 0xff) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(char *)loc = v;
+                       break;
+
+               case R_68K_16:
+                       if (v > 0xffff) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(short *)loc = v;
+                       break;
+
+               case R_68K_PC8:
+                       v -= dot;
+                       if ((ElfW(Sword))v > 0x7f ||
+                                       (ElfW(Sword))v < -(ElfW(Sword))0x80) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(char *)loc = v;
+                       break;
+
+               case R_68K_PC16:
+                       v -= dot;
+                       if ((ElfW(Sword))v > 0x7fff ||
+                                       (ElfW(Sword))v < -(ElfW(Sword))0x8000) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(short *)loc = v;
+                       break;
+
+               case R_68K_PC32:
+                       *(int *)loc = v - dot;
+                       break;
+
+               case R_68K_GLOB_DAT:
+               case R_68K_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_68K_RELATIVE:
+                       *(int *)loc += f->baseaddr;
+                       break;
+
+               case R_68K_GOT32:
+                       goto bb_use_got;
+
+# ifdef R_68K_GOTOFF
+               case R_68K_GOTOFF:
+                       *loc += v - got;
+                       break;
+# endif
+
+#elif defined(__mips__)
+
+               case R_MIPS_NONE:
+                       break;
+
+               case R_MIPS_32:
+                       *loc += v;
+                       break;
+
+               case R_MIPS_26:
+                       if (v % 4)
+                               ret = obj_reloc_dangerous;
+                       if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000))
+                               ret = obj_reloc_overflow;
+                       *loc =
+                               (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) &
+                                                                               0x03ffffff);
+                       break;
+
+               case R_MIPS_HI16:
+                       {
+                               struct mips_hi16 *n;
+
+                               /* We cannot relocate this one now because we don't know the value
+                                  of the carry we need to add.  Save the information, and let LO16
+                                  do the actual relocation.  */
+                               n = xmalloc(sizeof *n);
+                               n->addr = loc;
+                               n->value = v;
+                               n->next = ifile->mips_hi16_list;
+                               ifile->mips_hi16_list = n;
+                               break;
+                       }
+
+               case R_MIPS_LO16:
+                       {
+                               unsigned long insnlo = *loc;
+                               ElfW(Addr) val, vallo;
+
+                               /* Sign extend the addend we extract from the lo insn.  */
+                               vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
+
+                               if (ifile->mips_hi16_list != NULL) {
+                                       struct mips_hi16 *l;
+
+                                       l = ifile->mips_hi16_list;
+                                       while (l != NULL) {
+                                               struct mips_hi16 *next;
+                                               unsigned long insn;
+
+                                               /* Do the HI16 relocation.  Note that we actually don't
+                                                  need to know anything about the LO16 itself, except where
+                                                  to find the low 16 bits of the addend needed by the LO16.  */
+                                               insn = *l->addr;
+                                               val =
+                                                       ((insn & 0xffff) << 16) +
+                                                       vallo;
+                                               val += v;
+
+                                               /* Account for the sign extension that will happen in the
+                                                  low bits.  */
+                                               val =
+                                                       ((val >> 16) +
+                                                        ((val & 0x8000) !=
+                                                         0)) & 0xffff;
+
+                                               insn = (insn & ~0xffff) | val;
+                                               *l->addr = insn;
+
+                                               next = l->next;
+                                               free(l);
+                                               l = next;
+                                       }
+
+                                       ifile->mips_hi16_list = NULL;
+                               }
+
+                               /* Ok, we're done with the HI16 relocs.  Now deal with the LO16.  */
+                               val = v + vallo;
+                               insnlo = (insnlo & ~0xffff) | (val & 0xffff);
+                               *loc = insnlo;
+                               break;
+                       }
+
+#elif defined(__nios2__)
+
+               case R_NIOS2_NONE:
+                       break;
+
+               case R_NIOS2_BFD_RELOC_32:
+                       *loc += v;
+                       break;
+
+               case R_NIOS2_BFD_RELOC_16:
+                       if (v > 0xffff) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(short *)loc = v;
+                       break;
+
+               case R_NIOS2_BFD_RELOC_8:
+                       if (v > 0xff) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(char *)loc = v;
+                       break;
+
+               case R_NIOS2_S16:
+                       {
+                               Elf32_Addr word;
+
+                               if ((Elf32_Sword)v > 0x7fff ||
+                                   (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc;
+                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+                                      (word & 0x3f);
+                       }
+                       break;
+
+               case R_NIOS2_U16:
+                       {
+                               Elf32_Addr word;
+
+                               if (v > 0xffff) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc;
+                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+                                      (word & 0x3f);
+                       }
+                       break;
+
+               case R_NIOS2_PCREL16:
+                       {
+                               Elf32_Addr word;
+
+                               v -= dot + 4;
+                               if ((Elf32_Sword)v > 0x7fff ||
+                                   (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc;
+                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
+                       }
+                       break;
+
+               case R_NIOS2_GPREL:
+                       {
+                               Elf32_Addr word, gp;
+                               /* get _gp */
+                               gp = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "_gp"));
+                               v-=gp;
+                               if ((Elf32_Sword)v > 0x7fff ||
+                                               (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc;
+                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
+                       }
+                       break;
+
+               case R_NIOS2_CALL26:
+                       if (v & 3)
+                               ret = obj_reloc_dangerous;
+                       if ((v >> 28) != (dot >> 28))
+                               ret = obj_reloc_overflow;
+                       *loc = (*loc & 0x3f) | ((v >> 2) << 6);
+                       break;
+
+               case R_NIOS2_IMM5:
+                       {
+                               Elf32_Addr word;
+
+                               if (v > 0x1f) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc & ~0x7c0;
+                               *loc = word | ((v & 0x1f) << 6);
+                       }
+                       break;
+
+               case R_NIOS2_IMM6:
+                       {
+                               Elf32_Addr word;
+
+                               if (v > 0x3f) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc & ~0xfc0;
+                               *loc = word | ((v & 0x3f) << 6);
+                       }
+                       break;
+
+               case R_NIOS2_IMM8:
+                       {
+                               Elf32_Addr word;
+
+                               if (v > 0xff) {
+                                       ret = obj_reloc_overflow;
+                               }
+
+                               word = *loc & ~0x3fc0;
+                               *loc = word | ((v & 0xff) << 6);
+                       }
+                       break;
+
+               case R_NIOS2_HI16:
+                       {
+                               Elf32_Addr word;
+
+                               word = *loc;
+                               *loc = ((((word >> 22) << 16) | ((v >>16) & 0xffff)) << 6) |
+                                      (word & 0x3f);
+                       }
+                       break;
+
+               case R_NIOS2_LO16:
+                       {
+                               Elf32_Addr word;
+
+                               word = *loc;
+                               *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+                                      (word & 0x3f);
+                       }
+                       break;
+
+               case R_NIOS2_HIADJ16:
+                       {
+                               Elf32_Addr word1, word2;
+
+                               word1 = *loc;
+                               word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff;
+                               *loc = ((((word1 >> 22) << 16) | word2) << 6) |
+                                      (word1 & 0x3f);
+                       }
+                       break;
+
+#elif defined(__powerpc64__)
+               /* PPC64 needs a 2.6 kernel, 2.4 module relocation irrelevant */
+
+#elif defined(__powerpc__)
+
+               case R_PPC_ADDR16_HA:
+                       *(unsigned short *)loc = (v + 0x8000) >> 16;
+                       break;
+
+               case R_PPC_ADDR16_HI:
+                       *(unsigned short *)loc = v >> 16;
+                       break;
+
+               case R_PPC_ADDR16_LO:
+                       *(unsigned short *)loc = v;
+                       break;
+
+               case R_PPC_REL24:
+                       goto bb_use_plt;
+
+               case R_PPC_REL32:
+                       *loc = v - dot;
+                       break;
+
+               case R_PPC_ADDR32:
+                       *loc = v;
+                       break;
+
+#elif defined(__s390__)
+
+               case R_390_32:
+                       *(unsigned int *) loc += v;
+                       break;
+               case R_390_16:
+                       *(unsigned short *) loc += v;
+                       break;
+               case R_390_8:
+                       *(unsigned char *) loc += v;
+                       break;
+
+               case R_390_PC32:
+                       *(unsigned int *) loc += v - dot;
+                       break;
+               case R_390_PC16DBL:
+                       *(unsigned short *) loc += (v - dot) >> 1;
+                       break;
+               case R_390_PC16:
+                       *(unsigned short *) loc += v - dot;
+                       break;
+
+               case R_390_PLT32:
+               case R_390_PLT16DBL:
+                       /* find the plt entry and initialize it.  */
+                       pe = (struct arch_single_entry *) &isym->pltent;
+                       if (pe->inited == 0) {
+                               ip = (unsigned long *)(ifile->plt->contents + pe->offset);
+                               ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
+                               ip[1] = 0x100607f1;
+                               if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+                                       ip[2] = v - 2;
+                               else
+                                       ip[2] = v;
+                               pe->inited = 1;
+                       }
+
+                       /* Insert relative distance to target.  */
+                       v = plt + pe->offset - dot;
+                       if (ELF_R_TYPE(rel->r_info) == R_390_PLT32)
+                               *(unsigned int *) loc = (unsigned int) v;
+                       else if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+                               *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
+                       break;
+
+               case R_390_GLOB_DAT:
+               case R_390_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_390_RELATIVE:
+                       *loc += f->baseaddr;
+                       break;
+
+               case R_390_GOTPC:
+                       *(unsigned long *) loc += got - dot;
+                       break;
+
+               case R_390_GOT12:
+               case R_390_GOT16:
+               case R_390_GOT32:
+                       if (!isym->gotent.inited)
+                       {
+                               isym->gotent.inited = 1;
+                               *(ElfW(Addr) *)(ifile->got->contents + isym->gotent.offset) = v;
+                       }
+                       if (ELF_R_TYPE(rel->r_info) == R_390_GOT12)
+                               *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
+                       else if (ELF_R_TYPE(rel->r_info) == R_390_GOT16)
+                               *(unsigned short *) loc += isym->gotent.offset;
+                       else if (ELF_R_TYPE(rel->r_info) == R_390_GOT32)
+                               *(unsigned int *) loc += isym->gotent.offset;
+                       break;
+
+# ifndef R_390_GOTOFF32
+#  define R_390_GOTOFF32 R_390_GOTOFF
+# endif
+               case R_390_GOTOFF32:
+                       *loc += v - got;
+                       break;
+
+#elif defined(__sh__)
+
+               case R_SH_NONE:
+                       break;
+
+               case R_SH_DIR32:
+                       *loc += v;
+                       break;
+
+               case R_SH_REL32:
+                       *loc += v - dot;
+                       break;
+
+               case R_SH_PLT32:
+                       *loc = v - dot;
+                       break;
+
+               case R_SH_GLOB_DAT:
+               case R_SH_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_SH_RELATIVE:
+                       *loc = f->baseaddr + rel->r_addend;
+                       break;
+
+               case R_SH_GOTPC:
+                       *loc = got - dot + rel->r_addend;
+                       break;
+
+               case R_SH_GOT32:
+                       goto bb_use_got;
+
+               case R_SH_GOTOFF:
+                       *loc = v - got;
+                       break;
+
+# if defined(__SH5__)
+               case R_SH_IMM_MEDLOW16:
+               case R_SH_IMM_LOW16:
+                       {
+                               ElfW(Addr) word;
+
+                               if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
+                                       v >>= 16;
+
+                               /*
+                                *  movi and shori have the format:
+                                *
+                                *  |  op  | imm  | reg | reserved |
+                                *   31..26 25..10 9.. 4 3   ..   0
+                                *
+                                * so we simply mask and or in imm.
+                                */
+                               word = *loc & ~0x3fffc00;
+                               word |= (v & 0xffff) << 10;
+
+                               *loc = word;
+
+                               break;
+                       }
+
+               case R_SH_IMM_MEDLOW16_PCREL:
+               case R_SH_IMM_LOW16_PCREL:
+                       {
+                               ElfW(Addr) word;
+
+                               word = *loc & ~0x3fffc00;
+
+                               v -= dot;
+
+                               if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
+                                       v >>= 16;
+
+                               word |= (v & 0xffff) << 10;
+
+                               *loc = word;
+
+                               break;
+                       }
+# endif /* __SH5__ */
+
+#elif defined(__v850e__)
+
+               case R_V850_NONE:
+                       break;
+
+               case R_V850_32:
+                       /* We write two shorts instead of a long because even
+                          32-bit insns only need half-word alignment, but
+                          32-bit data needs to be long-word aligned.  */
+                       v += ((unsigned short *)loc)[0];
+                       v += ((unsigned short *)loc)[1] << 16;
+                       ((unsigned short *)loc)[0] = v & 0xffff;
+                       ((unsigned short *)loc)[1] = (v >> 16) & 0xffff;
+                       break;
+
+               case R_V850_22_PCREL:
+                       goto bb_use_plt;
+
+#elif defined(__x86_64__)
+
+               case R_X86_64_NONE:
+                       break;
+
+               case R_X86_64_64:
+                       *loc += v;
+                       break;
+
+               case R_X86_64_32:
+                       *(unsigned int *) loc += v;
+                       if (v > 0xffffffff)
+                       {
+                               ret = obj_reloc_overflow; /* Kernel module compiled without -mcmodel=kernel. */
+                               /* error("Possibly is module compiled without -mcmodel=kernel!"); */
+                       }
+                       break;
+
+               case R_X86_64_32S:
+                       *(signed int *) loc += v;
+                       break;
+
+               case R_X86_64_16:
+                       *(unsigned short *) loc += v;
+                       break;
+
+               case R_X86_64_8:
+                       *(unsigned char *) loc += v;
+                       break;
+
+               case R_X86_64_PC32:
+                       *(unsigned int *) loc += v - dot;
+                       break;
+
+               case R_X86_64_PC16:
+                       *(unsigned short *) loc += v - dot;
+                       break;
+
+               case R_X86_64_PC8:
+                       *(unsigned char *) loc += v - dot;
+                       break;
+
+               case R_X86_64_GLOB_DAT:
+               case R_X86_64_JUMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_X86_64_RELATIVE:
+                       *loc += f->baseaddr;
+                       break;
+
+               case R_X86_64_GOT32:
+               case R_X86_64_GOTPCREL:
+                       goto bb_use_got;
+# if 0
+                       if (!isym->gotent.reloc_done)
+                       {
+                               isym->gotent.reloc_done = 1;
+                               *(Elf64_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
+                       }
+                       /* XXX are these really correct?  */
+                       if (ELF64_R_TYPE(rel->r_info) == R_X86_64_GOTPCREL)
+                               *(unsigned int *) loc += v + isym->gotent.offset;
+                       else
+                               *loc += isym->gotent.offset;
+                       break;
+# endif
+
+#else
+# warning "no idea how to handle relocations on your arch"
+#endif
+
+               default:
+                       printf("Warning: unhandled reloc %d\n",(int)ELF_R_TYPE(rel->r_info));
+                       ret = obj_reloc_unhandled;
+                       break;
+
+#if defined(USE_PLT_ENTRIES)
+
+bb_use_plt:
+
+                       /* find the plt entry and initialize it if necessary */
+
+#if defined(USE_PLT_LIST)
+                       for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;)
+                               pe = pe->next;
+#else
+                       pe = &isym->pltent;
+#endif
+
+                       if (! pe->inited) {
+                               ip = (unsigned long *) (ifile->plt->contents + pe->offset);
+
+                               /* generate some machine code */
+
+#if defined(__arm__)
+                               ip[0] = 0xe51ff004;                     /* ldr pc,[pc,#-4] */
+                               ip[1] = v;                              /* sym@ */
+#endif
+#if defined(__powerpc__)
+                               ip[0] = 0x3d600000 + ((v + 0x8000) >> 16);  /* lis r11,sym@ha */
+                               ip[1] = 0x396b0000 + (v & 0xffff);          /* addi r11,r11,sym@l */
+                               ip[2] = 0x7d6903a6;                           /* mtctr r11 */
+                               ip[3] = 0x4e800420;                           /* bctr */
+#endif
+#if defined(__v850e__)
+                               /* We have to trash a register, so we assume that any control
+                                  transfer more than 21-bits away must be a function call
+                                  (so we can use a call-clobbered register).  */
+                               ip[0] = 0x0621 + ((v & 0xffff) << 16);   /* mov sym, r1 ... */
+                               ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
+#endif
+                               pe->inited = 1;
+                       }
+
+                       /* relative distance to target */
+                       v -= dot;
+                       /* if the target is too far away.... */
+#if defined(__arm__) || defined(__powerpc__)
+                       if ((int)v < -0x02000000 || (int)v >= 0x02000000)
+#elif defined(__v850e__)
+                               if ((ElfW(Sword))v > 0x1fffff || (ElfW(Sword))v < (ElfW(Sword))-0x200000)
+#endif
+                                       /* go via the plt */
+                                       v = plt + pe->offset - dot;
+
+#if defined(__v850e__)
+                       if (v & 1)
+#else
+                               if (v & 3)
+#endif
+                                       ret = obj_reloc_dangerous;
+
+                       /* merge the offset into the instruction. */
+#if defined(__arm__)
+                       /* Convert to words. */
+                       v >>= 2;
+
+                       *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff);
+#endif
+#if defined(__powerpc__)
+                       *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
+#endif
+#if defined(__v850e__)
+                       /* We write two shorts instead of a long because even 32-bit insns
+                          only need half-word alignment, but the 32-bit data write needs
+                          to be long-word aligned.  */
+                       ((unsigned short *)loc)[0] =
+                               (*(unsigned short *)loc & 0xffc0) /* opcode + reg */
+                               | ((v >> 16) & 0x3f);             /* offs high part */
+                       ((unsigned short *)loc)[1] =
+                               (v & 0xffff);                    /* offs low part */
+#endif
+                       break;
+#endif /* USE_PLT_ENTRIES */
+
+#if defined(USE_GOT_ENTRIES)
+bb_use_got:
+
+                       /* needs an entry in the .got: set it, once */
+                       if (!isym->gotent.inited) {
+                               isym->gotent.inited = 1;
+                               *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v;
+                       }
+                       /* make the reloc with_respect_to_.got */
+#if defined(__sh__)
+                       *loc += isym->gotent.offset + rel->r_addend;
+#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__)
+                       *loc += isym->gotent.offset;
+#endif
+                       break;
+
+#endif /* USE_GOT_ENTRIES */
+       }
+
+       return ret;
+}
+
+
+#if defined(USE_LIST)
+
+static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list,
+                         int offset, int size)
+{
+       struct arch_list_entry *pe;
+
+       for (pe = *list; pe != NULL; pe = pe->next) {
+               if (pe->addend == rel->r_addend) {
+                       break;
+               }
+       }
+
+       if (pe == NULL) {
+               pe = xmalloc(sizeof(struct arch_list_entry));
+               pe->next = *list;
+               pe->addend = rel->r_addend;
+               pe->offset = offset;
+               pe->inited = 0;
+               *list = pe;
+               return size;
+       }
+       return 0;
+}
+
+#endif
+
+#if defined(USE_SINGLE)
+
+static int arch_single_init(/*ElfW(RelM) *rel,*/ struct arch_single_entry *single,
+                            int offset, int size)
+{
+       if (single->allocated == 0) {
+               single->allocated = 1;
+               single->offset = offset;
+               single->inited = 0;
+               return size;
+       }
+       return 0;
+}
+
+#endif
+
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+
+static struct obj_section *arch_xsect_init(struct obj_file *f, const char *name,
+                                          int offset, int size)
+{
+       struct obj_section *myrelsec = obj_find_section(f, name);
+
+       if (offset == 0) {
+               offset += size;
+       }
+
+       if (myrelsec) {
+               obj_extend_section(myrelsec, offset);
+       } else {
+               myrelsec = obj_create_alloced_section(f, name,
+                               size, offset);
+       }
+
+       return myrelsec;
+}
+
+#endif
+
+static void arch_create_got(struct obj_file *f)
+{
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+       struct arch_file *ifile = (struct arch_file *) f;
+       int i;
+#if defined(USE_GOT_ENTRIES)
+       int got_offset = 0, got_needed = 0, got_allocate;
+#endif
+#if defined(USE_PLT_ENTRIES)
+       int plt_offset = 0, plt_needed = 0, plt_allocate;
+#endif
+       struct obj_section *relsec, *symsec, *strsec;
+       ElfW(RelM) *rel, *relend;
+       ElfW(Sym) *symtab, *extsym;
+       const char *strtab, *name;
+       struct arch_symbol *intsym;
+
+       for (i = 0; i < f->header.e_shnum; ++i) {
+               relsec = f->sections[i];
+               if (relsec->header.sh_type != SHT_RELM)
+                       continue;
+
+               symsec = f->sections[relsec->header.sh_link];
+               strsec = f->sections[symsec->header.sh_link];
+
+               rel = (ElfW(RelM) *) relsec->contents;
+               relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+               symtab = (ElfW(Sym) *) symsec->contents;
+               strtab = (const char *) strsec->contents;
+
+               for (; rel < relend; ++rel) {
+                       extsym = &symtab[ELF_R_SYM(rel->r_info)];
+
+#if defined(USE_GOT_ENTRIES)
+                       got_allocate = 0;
+#endif
+#if defined(USE_PLT_ENTRIES)
+                       plt_allocate = 0;
+#endif
+
+                       switch (ELF_R_TYPE(rel->r_info)) {
+#if defined(__arm__)
+                       case R_ARM_PC24:
+                       case R_ARM_PLT32:
+                               plt_allocate = 1;
+                               break;
+
+                       case R_ARM_GOTOFF:
+                       case R_ARM_GOTPC:
+                               got_needed = 1;
+                               continue;
+
+                       case R_ARM_GOT32:
+                               got_allocate = 1;
+                               break;
+
+#elif defined(__i386__)
+                       case R_386_GOTPC:
+                       case R_386_GOTOFF:
+                               got_needed = 1;
+                               continue;
+
+                       case R_386_GOT32:
+                               got_allocate = 1;
+                               break;
+
+#elif defined(__powerpc__)
+                       case R_PPC_REL24:
+                               plt_allocate = 1;
+                               break;
+
+#elif defined(__mc68000__)
+                       case R_68K_GOT32:
+                               got_allocate = 1;
+                               break;
+
+#ifdef R_68K_GOTOFF
+                       case R_68K_GOTOFF:
+                               got_needed = 1;
+                               continue;
+#endif
+
+#elif defined(__sh__)
+                       case R_SH_GOT32:
+                               got_allocate = 1;
+                               break;
+
+                       case R_SH_GOTPC:
+                       case R_SH_GOTOFF:
+                               got_needed = 1;
+                               continue;
+
+#elif defined(__v850e__)
+                       case R_V850_22_PCREL:
+                               plt_needed = 1;
+                               break;
+
+#endif
+                       default:
+                               continue;
+                       }
+
+                       if (extsym->st_name != 0) {
+                               name = strtab + extsym->st_name;
+                       } else {
+                               name = f->sections[extsym->st_shndx]->name;
+                       }
+                       intsym = (struct arch_symbol *) obj_find_symbol(f, name);
+#if defined(USE_GOT_ENTRIES)
+                       if (got_allocate) {
+                               got_offset += arch_single_init(
+                                               /*rel,*/ &intsym->gotent,
+                                               got_offset, GOT_ENTRY_SIZE);
+
+                               got_needed = 1;
+                       }
+#endif
+#if defined(USE_PLT_ENTRIES)
+                       if (plt_allocate) {
+#if defined(USE_PLT_LIST)
+                               plt_offset += arch_list_add(
+                                               rel, &intsym->pltent,
+                                               plt_offset, PLT_ENTRY_SIZE);
+#else
+                               plt_offset += arch_single_init(
+                                               /*rel,*/ &intsym->pltent,
+                                               plt_offset, PLT_ENTRY_SIZE);
+#endif
+                               plt_needed = 1;
+                       }
+#endif
+               }
+       }
+
+#if defined(USE_GOT_ENTRIES)
+       if (got_needed) {
+               ifile->got = arch_xsect_init(f, ".got", got_offset,
+                               GOT_ENTRY_SIZE);
+       }
+#endif
+
+#if defined(USE_PLT_ENTRIES)
+       if (plt_needed) {
+               ifile->plt = arch_xsect_init(f, ".plt", plt_offset,
+                               PLT_ENTRY_SIZE);
+       }
+#endif
+
+#endif /* defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) */
+}
+
+/*======================================================================*/
+
+/* Standard ELF hash function.  */
+static unsigned long obj_elf_hash_n(const char *name, unsigned long n)
+{
+       unsigned long h = 0;
+       unsigned long g;
+       unsigned char ch;
+
+       while (n > 0) {
+               ch = *name++;
+               h = (h << 4) + ch;
+               g = (h & 0xf0000000);
+               if (g != 0) {
+                       h ^= g >> 24;
+                       h &= ~g;
+               }
+               n--;
+       }
+       return h;
+}
+
+static unsigned long obj_elf_hash(const char *name)
+{
+       return obj_elf_hash_n(name, strlen(name));
+}
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+/* String comparison for non-co-versioned kernel and module.  */
+
+static int ncv_strcmp(const char *a, const char *b)
+{
+       size_t alen = strlen(a), blen = strlen(b);
+
+       if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R')
+               return strncmp(a, b, alen);
+       else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R')
+               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 && str[len - 10] == '_' && str[len - 9] == 'R')
+               len -= 10;
+       return obj_elf_hash_n(str, len);
+}
+
+static void
+obj_set_symbol_compare(struct obj_file *f,
+                                          int (*cmp) (const char *, const char *),
+                                          unsigned long (*hash) (const char *))
+{
+       if (cmp)
+               f->symbol_cmp = cmp;
+       if (hash) {
+               struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
+               int i;
+
+               f->symbol_hash = hash;
+
+               memcpy(tmptab, f->symtab, sizeof(tmptab));
+               memset(f->symtab, 0, sizeof(f->symtab));
+
+               for (i = 0; i < HASH_BUCKETS; ++i)
+                       for (sym = tmptab[i]; sym; sym = next) {
+                               unsigned long h = hash(sym->name) % HASH_BUCKETS;
+                               next = sym->next;
+                               sym->next = f->symtab[h];
+                               f->symtab[h] = sym;
+                       }
+       }
+}
+
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+static struct obj_symbol *
+obj_add_symbol(struct obj_file *f, const char *name,
+                               unsigned long symidx, int info,
+                               int secidx, ElfW(Addr) value,
+                               unsigned long size)
+{
+       struct obj_symbol *sym;
+       unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+       int n_type = ELF_ST_TYPE(info);
+       int n_binding = ELF_ST_BIND(info);
+
+       for (sym = f->symtab[hash]; sym; sym = sym->next) {
+               if (f->symbol_cmp(sym->name, name) == 0) {
+                       int o_secidx = sym->secidx;
+                       int o_info = sym->info;
+                       int o_type = ELF_ST_TYPE(o_info);
+                       int o_binding = ELF_ST_BIND(o_info);
+
+                       /* A redefinition!  Is it legal?  */
+
+                       if (secidx == SHN_UNDEF)
+                               return sym;
+                       else if (o_secidx == SHN_UNDEF)
+                               goto found;
+                       else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) {
+                               /* Cope with local and global symbols of the same name
+                                  in the same object file, as might have been created
+                                  by ld -r.  The only reason locals are now seen at this
+                                  level at all is so that we can do semi-sensible things
+                                  with parameters.  */
+
+                               struct obj_symbol *nsym, **p;
+
+                               nsym = arch_new_symbol();
+                               nsym->next = sym->next;
+                               nsym->ksymidx = -1;
+
+                               /* Excise the old (local) symbol from the hash chain.  */
+                               for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
+                                       continue;
+                               *p = sym = nsym;
+                               goto found;
+                       } else if (n_binding == STB_LOCAL) {
+                               /* Another symbol of the same name has already been defined.
+                                  Just add this to the local table.  */
+                               sym = arch_new_symbol();
+                               sym->next = NULL;
+                               sym->ksymidx = -1;
+                               f->local_symtab[symidx] = sym;
+                               goto found;
+                       } else if (n_binding == STB_WEAK)
+                               return sym;
+                       else if (o_binding == STB_WEAK)
+                               goto found;
+                       /* Don't unify COMMON symbols with object types the programmer
+                          doesn't expect.  */
+                       else if (secidx == SHN_COMMON
+                                       && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
+                               return sym;
+                       else if (o_secidx == SHN_COMMON
+                                       && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
+                               goto found;
+                       else {
+                               /* Don't report an error if the symbol is coming from
+                                  the kernel or some external module.  */
+                               if (secidx <= SHN_HIRESERVE)
+                                       bb_error_msg("%s multiply defined", name);
+                               return sym;
+                       }
+               }
+       }
+
+       /* Completely new symbol.  */
+       sym = arch_new_symbol();
+       sym->next = f->symtab[hash];
+       f->symtab[hash] = sym;
+       sym->ksymidx = -1;
+       if (ELF_ST_BIND(info) == STB_LOCAL && symidx != (unsigned long)(-1)) {
+               if (symidx >= f->local_symtab_size)
+                       bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
+                                       name, (long) symidx, (long) f->local_symtab_size);
+               else
+                       f->local_symtab[symidx] = sym;
+       }
+
+found:
+       sym->name = name;
+       sym->value = value;
+       sym->size = size;
+       sym->secidx = secidx;
+       sym->info = info;
+
+       return sym;
+}
+
+static struct obj_symbol *
+obj_find_symbol(struct obj_file *f, const char *name)
+{
+       struct obj_symbol *sym;
+       unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+
+       for (sym = f->symtab[hash]; sym; sym = sym->next)
+               if (f->symbol_cmp(sym->name, name) == 0)
+                       return sym;
+
+       return NULL;
+}
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym)
+{
+       if (sym) {
+               if (sym->secidx >= SHN_LORESERVE)
+                       return sym->value;
+
+               return sym->value + f->sections[sym->secidx]->header.sh_addr;
+       } else {
+               /* As a special case, a NULL sym has value zero.  */
+               return 0;
+       }
+}
+
+static struct obj_section *obj_find_section(struct obj_file *f, const char *name)
+{
+       int i, n = f->header.e_shnum;
+
+       for (i = 0; i < n; ++i)
+               if (strcmp(f->sections[i]->name, name) == 0)
+                       return f->sections[i];
+
+       return NULL;
+}
+
+static int obj_load_order_prio(struct obj_section *a)
+{
+       unsigned long af, ac;
+
+       af = a->header.sh_flags;
+
+       ac = 0;
+       if (a->name[0] != '.' || strlen(a->name) != 10 ||
+                       strcmp(a->name + 5, ".init"))
+               ac |= 32;
+       if (af & SHF_ALLOC)
+               ac |= 16;
+       if (!(af & SHF_WRITE))
+               ac |= 8;
+       if (af & SHF_EXECINSTR)
+               ac |= 4;
+       if (a->header.sh_type != SHT_NOBITS)
+               ac |= 2;
+
+       return ac;
+}
+
+static void
+obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec)
+{
+       struct obj_section **p;
+       int prio = obj_load_order_prio(sec);
+       for (p = f->load_order_search_start; *p; p = &(*p)->load_next)
+               if (obj_load_order_prio(*p) < prio)
+                       break;
+       sec->load_next = *p;
+       *p = sec;
+}
+
+static struct obj_section *obj_create_alloced_section(struct obj_file *f,
+                               const char *name,
+                               unsigned long align,
+                               unsigned long size)
+{
+       int newidx = f->header.e_shnum++;
+       struct obj_section *sec;
+
+       f->sections = xrealloc_vector(f->sections, 2, newidx);
+       f->sections[newidx] = sec = arch_new_section();
+
+       sec->header.sh_type = SHT_PROGBITS;
+       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+       sec->header.sh_size = size;
+       sec->header.sh_addralign = align;
+       sec->name = name;
+       sec->idx = newidx;
+       if (size)
+               sec->contents = xmalloc(size);
+
+       obj_insert_section_load_order(f, sec);
+
+       return sec;
+}
+
+static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
+                               const char *name,
+                               unsigned long align,
+                               unsigned long size)
+{
+       int newidx = f->header.e_shnum++;
+       struct obj_section *sec;
+
+       f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
+       f->sections[newidx] = sec = arch_new_section();
+
+       sec->header.sh_type = SHT_PROGBITS;
+       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+       sec->header.sh_size = size;
+       sec->header.sh_addralign = align;
+       sec->name = name;
+       sec->idx = newidx;
+       if (size)
+               sec->contents = xmalloc(size);
+
+       sec->load_next = f->load_order;
+       f->load_order = sec;
+       if (f->load_order_search_start == &f->load_order)
+               f->load_order_search_start = &sec->load_next;
+
+       return sec;
+}
+
+static void *obj_extend_section(struct obj_section *sec, unsigned long more)
+{
+       unsigned long oldsize = sec->header.sh_size;
+       if (more) {
+               sec->header.sh_size += more;
+               sec->contents = xrealloc(sec->contents, sec->header.sh_size);
+       }
+       return sec->contents + oldsize;
+}
+
+
+/* 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 new_module_symbol *syms, size_t nsyms)
+{
+       struct new_module_symbol *s;
+       size_t i;
+       int used = 0;
+#ifdef SYMBOL_PREFIX
+       char *name_buf = 0;
+       size_t name_alloced_size = 0;
+#endif
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+       int gpl;
+
+       gpl = obj_gpl_license(f, NULL) == 0;
+#endif
+       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;
+               char *name;
+
+               /* GPL licensed modules can use symbols exported with
+                * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
+                * exported names.  Non-GPL modules never see any GPLONLY_
+                * symbols so they cannot fudge it by adding the prefix on
+                * their references.
+                */
+               if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+                       if (gpl)
+                               s->name += 8;
+                       else
+#endif
+                               continue;
+               }
+               name = (char *)s->name;
+
+#ifdef SYMBOL_PREFIX
+               /* Prepend SYMBOL_PREFIX to the symbol's name (the
+                  kernel exports `C names', but module object files
+                  reference `linker names').  */
+               size_t extra = sizeof SYMBOL_PREFIX;
+               size_t name_size = strlen(name) + extra;
+               if (name_size > name_alloced_size) {
+                       name_alloced_size = name_size * 2;
+                       name_buf = alloca(name_alloced_size);
+               }
+               strcpy(name_buf, SYMBOL_PREFIX);
+               strcpy(name_buf + extra - 1, name);
+               name = name_buf;
+#endif /* SYMBOL_PREFIX */
+
+               sym = obj_find_symbol(f, name);
+               if (sym && !(ELF_ST_BIND(sym->info) == STB_LOCAL)) {
+#ifdef SYMBOL_PREFIX
+                       /* Put NAME_BUF into more permanent storage.  */
+                       name = xmalloc(name_size);
+                       strcpy(name, name_buf);
+#endif
+                       sym = obj_add_symbol(f, name, -1,
+                                       ELF_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 external_module *m;
+       int i, nused = 0;
+
+       /* Add module symbols first.  */
+
+       for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m) {
+               if (m->nsyms
+                && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms)
+               ) {
+                       m->used = 1;
+                       ++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 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 (p + klen == v && strncmp(p, key, klen) == 0)
+                               return v + 1;
+               } else {
+                       if (p + klen == n && strcmp(p, key) == 0)
+                               return n;
+               }
+               p = n + 1;
+       }
+
+       return NULL;
+}
+
+
+/*======================================================================*/
+/* Functions relating to module loading after 2.1.18.  */
+
+/* From Linux-2.6 sources */
+/* You can use " around spaces, but can't escape ". */
+/* Hyphens and underscores equivalent in parameter names. */
+static char *next_arg(char *args, char **param, char **val)
+{
+       unsigned int i, equals = 0;
+       int in_quote = 0, quoted = 0;
+       char *next;
+
+       if (*args == '"') {
+               args++;
+               in_quote = 1;
+               quoted = 1;
+       }
+
+       for (i = 0; args[i]; i++) {
+               if (args[i] == ' ' && !in_quote)
+                       break;
+               if (equals == 0) {
+                       if (args[i] == '=')
+                               equals = i;
+               }
+               if (args[i] == '"')
+                       in_quote = !in_quote;
+       }
+
+       *param = args;
+       if (!equals)
+               *val = NULL;
+       else {
+               args[equals] = '\0';
+               *val = args + equals + 1;
+
+               /* Don't include quotes in value. */
+               if (**val == '"') {
+                       (*val)++;
+                       if (args[i-1] == '"')
+                               args[i-1] = '\0';
+               }
+               if (quoted && args[i-1] == '"')
+                       args[i-1] = '\0';
+       }
+
+       if (args[i]) {
+               args[i] = '\0';
+               next = args + i + 1;
+       } else
+               next = args + i;
+
+       /* Chew up trailing spaces. */
+       return skip_whitespace(next);
+}
+
+static void
+new_process_module_arguments(struct obj_file *f, const char *options)
+{
+       char *xoptions, *pos;
+       char *param, *val;
+
+       xoptions = pos = xstrdup(skip_whitespace(options));
+       while (*pos) {
+               unsigned long charssize = 0;
+               char *tmp, *contents, *loc, *pinfo, *p;
+               struct obj_symbol *sym;
+               int min, max, n, len;
+
+               pos = next_arg(pos, &param, &val);
+
+               tmp = xasprintf("parm_%s", param);
+               pinfo = get_modinfo_value(f, tmp);
+               free(tmp);
+               if (pinfo == NULL)
+                       bb_error_msg_and_die("invalid parameter %s", param);
+
+#ifdef SYMBOL_PREFIX
+               tmp = xasprintf(SYMBOL_PREFIX "%s", param);
+               sym = obj_find_symbol(f, tmp);
+               free(tmp);
+#else
+               sym = obj_find_symbol(f, param);
+#endif
+
+               /* Also check that the parameter was not resolved from the kernel.  */
+               if (sym == NULL || sym->secidx > SHN_HIRESERVE)
+                       bb_error_msg_and_die("symbol for parameter %s not found", param);
+
+               /* Number of parameters */
+               if (isdigit(*pinfo)) {
+                       min = strtoul(pinfo, &pinfo, 10);
+                       if (*pinfo == '-')
+                               max = strtoul(pinfo + 1, &pinfo, 10);
+                       else
+                               max = min;
+               } else
+                       min = max = 1;
+
+               contents = f->sections[sym->secidx]->contents;
+               loc = contents + sym->value;
+
+               if (*pinfo == 'c') {
+                       if (!isdigit(*(pinfo + 1))) {
+                               bb_error_msg_and_die("parameter type 'c' for %s must be followed by"
+                                                    " the maximum size", param);
+                       }
+                       charssize = strtoul(pinfo + 1, (char **) NULL, 10);
+               }
+
+               if (val == NULL) {
+                       if (*pinfo != 'b')
+                               bb_error_msg_and_die("argument expected for parameter %s", param);
+                       val = (char *) "1";
+               }
+
+               /* Parse parameter values */
+               n = 0;
+               p = val;
+               while (*p != 0) {
+                       if (++n > max)
+                               bb_error_msg_and_die("too many values for %s (max %d)", param, max);
+
+                       switch (*pinfo) {
+                       case 's':
+                               len = strcspn(p, ",");
+                               p[len] = 0;
+                               obj_string_patch(f, sym->secidx,
+                                                loc - contents, p);
+                               loc += tgt_sizeof_char_p;
+                               p += len;
+                               break;
+                       case 'c':
+                               len = strcspn(p, ",");
+                               p[len] = 0;
+                               if (len >= charssize)
+                                       bb_error_msg_and_die("string too long for %s (max %ld)", param,
+                                                            charssize - 1);
+                               strcpy((char *) loc, p);
+                               loc += charssize;
+                               p += len;
+                               break;
+                       case 'b':
+                               *loc++ = strtoul(p, &p, 0);
+                               break;
+                       case 'h':
+                               *(short *) loc = strtoul(p, &p, 0);
+                               loc += tgt_sizeof_short;
+                               break;
+                       case 'i':
+                               *(int *) loc = strtoul(p, &p, 0);
+                               loc += tgt_sizeof_int;
+                               break;
+                       case 'l':
+                               *(long *) loc = strtoul(p, &p, 0);
+                               loc += tgt_sizeof_long;
+                               break;
+                       default:
+                               bb_error_msg_and_die("unknown parameter type '%c' for %s",
+                                                    *pinfo, param);
+                       }
+
+                       p = skip_whitespace(p);
+                       if (*p != ',')
+                               break;
+                       p = skip_whitespace(p + 1);
+               }
+
+               if (n < min)
+                       bb_error_msg_and_die("parameter %s requires at least %d arguments", param, min);
+               if (*p != '\0')
+                       bb_error_msg_and_die("invalid argument syntax for %s", param);
+       }
+
+       free(xoptions);
+}
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+static int new_is_module_checksummed(struct obj_file *f)
+{
+       const char *p = get_modinfo_value(f, "using_checksums");
+       if (p)
+               return xatoi(p);
+       return 0;
+}
+
+/* Get the module's kernel version in the canonical integer form.  */
+
+static int
+new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+       char *p, *q;
+       int a, b, c;
+
+       p = get_modinfo_value(f, "kernel_version");
+       if (p == NULL)
+               return -1;
+       safe_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;
+}
+
+#endif   /* FEATURE_INSMOD_VERSION_CHECKING */
+
+
+/* Fetch the loaded modules, and all currently exported symbols.  */
+
+static void new_get_kernel_symbols(void)
+{
+       char *module_names, *mn;
+       struct external_module *modules, *m;
+       struct new_module_symbol *syms, *s;
+       size_t ret, bufsize, nmod, nsyms, i, j;
+
+       /* Collect the loaded modules.  */
+
+       bufsize = 256;
+       module_names = xmalloc(bufsize);
+
+ retry_modules_load:
+       if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
+               if (errno == ENOSPC && bufsize < ret) {
+                       bufsize = ret;
+                       module_names = xrealloc(module_names, bufsize);
+                       goto retry_modules_load;
+               }
+               bb_perror_msg_and_die("QM_MODULES");
+       }
+
+       n_ext_modules = nmod = ret;
+
+       /* Collect the modules' symbols.  */
+
+       if (nmod) {
+               ext_modules = modules = xmalloc(nmod * sizeof(*modules));
+               memset(modules, 0, nmod * sizeof(*modules));
+               for (i = 0, mn = module_names, m = modules;
+                               i < nmod; ++i, ++m, mn += strlen(mn) + 1) {
+                       struct new_module_info info;
+
+                       if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) {
+                               if (errno == ENOENT) {
+                                       /* The module was removed out from underneath us.  */
+                                       continue;
+                               }
+                               bb_perror_msg_and_die("query_module: QM_INFO: %s", mn);
+                       }
+
+                       bufsize = 1024;
+                       syms = xmalloc(bufsize);
+ retry_mod_sym_load:
+                       if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) {
+                               switch (errno) {
+                                       case ENOSPC:
+                                               bufsize = ret;
+                                               syms = xrealloc(syms, bufsize);
+                                               goto retry_mod_sym_load;
+                                       case ENOENT:
+                                               /* The module was removed out from underneath us.  */
+                                               continue;
+                                       default:
+                                               bb_perror_msg_and_die("query_module: QM_SYMBOLS: %s", mn);
+                               }
+                       }
+                       nsyms = ret;
+
+                       m->name = mn;
+                       m->addr = info.addr;
+                       m->nsyms = nsyms;
+                       m->syms = syms;
+
+                       for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+                               s->name += (unsigned long) syms;
+                       }
+               }
+       }
+
+       /* Collect the kernel's symbols.  */
+
+       syms = xmalloc(bufsize = 16 * 1024);
+ retry_kern_sym_load:
+       if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) {
+               if (errno == ENOSPC && bufsize < ret) {
+                       bufsize = ret;
+                       syms = xrealloc(syms, bufsize);
+                       goto retry_kern_sym_load;
+               }
+               bb_perror_msg_and_die("kernel: QM_SYMBOLS");
+       }
+       nksyms = nsyms = ret;
+       ksyms = syms;
+
+       for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+               s->name += (unsigned long) syms;
+       }
+}
+
+
+/* Return the kernel symbol checksum version, or zero if not used.  */
+
+static int new_is_kernel_checksummed(void)
+{
+       struct new_module_symbol *s;
+       size_t i;
+
+       /* Using_Versions is not 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 void  new_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 new_module));
+       memset(sec->contents, 0, sizeof(struct new_module));
+
+       obj_add_symbol(f, SPFX "__this_module", -1,
+                       ELF_ST_INFO(STB_LOCAL, STT_OBJECT), sec->idx, 0,
+                       sizeof(struct new_module));
+
+       obj_string_patch(f, sec->idx, offsetof(struct new_module, name),
+                       m_name);
+}
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add an entry to the __ksymtab section, creating it if necessary */
+static void new_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;
+       /* Empty section might be byte-aligned */
+       sec->header.sh_addralign = tgt_sizeof_void_p;
+       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);
+}
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+static int new_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 new_module_ref *dep;
+               struct obj_symbol *tm;
+
+               sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
+                               (sizeof(struct new_module_ref)
+                                * n_ext_modules_used));
+               if (!sec)
+                       return 0;
+
+               tm = obj_find_symbol(f, SPFX "__this_module");
+               dep = (struct new_module_ref *) sec->contents;
+               for (i = 0; i < n_ext_modules; ++i)
+                       if (ext_modules[i].used) {
+                               dep->dep = ext_modules[i].addr;
+                               obj_symbol_patch(f, sec->idx,
+                                               (char *) &dep->ref - sec->contents, tm);
+                               dep->next_ref = 0;
+                               ++dep;
+                       }
+       }
+
+       if (!flag_noexport && !obj_find_section(f, "__ksymtab")) {
+               size_t nsyms;
+               int *loaded;
+
+               sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);
+
+               /* 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 (nsyms = i = 0; i < HASH_BUCKETS; ++i) {
+                       struct obj_symbol *sym;
+                       for (sym = f->symtab[i]; sym; sym = sym->next)
+                               if (ELF_ST_BIND(sym->info) != STB_LOCAL
+                                               && sym->secidx <= SHN_HIRESERVE
+                                               && (sym->secidx >= SHN_LORESERVE
+                                                       || loaded[sym->secidx])) {
+                                       ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p;
+
+                                       obj_symbol_patch(f, sec->idx, ofs, sym);
+                                       obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p,
+                                                       sym->name);
+
+                                       nsyms++;
+                               }
+               }
+
+               obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p);
+       }
+
+       return 1;
+}
+
+
+static int
+new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
+{
+       struct new_module *module;
+       struct obj_section *sec;
+       void *image;
+       int ret;
+       tgt_long m_addr;
+
+       sec = obj_find_section(f, ".this");
+       if (!sec || !sec->contents) {
+               bb_perror_msg_and_die("corrupt module %s?", m_name);
+       }
+       module = (struct new_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, SPFX "init_module"));
+       module->cleanup =
+               obj_symbol_final_value(f, obj_find_symbol(f, SPFX "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 = (void*)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 = (void*)sec->header.sh_addr;
+               module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
+       }
+
+       /* 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);
+
+       ret = init_module(m_name, (struct new_module *) image);
+       if (ret)
+               bb_perror_msg("init_module: %s", m_name);
+
+       free(image);
+
+       return ret == 0;
+}
+
+
+/*======================================================================*/
+
+static void
+obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                                const char *string)
+{
+       struct obj_string_patch *p;
+       struct obj_section *strsec;
+       size_t len = strlen(string) + 1;
+       char *loc;
+
+       p = xmalloc(sizeof(*p));
+       p->next = f->string_patches;
+       p->reloc_secidx = secidx;
+       p->reloc_offset = offset;
+       f->string_patches = p;
+
+       strsec = obj_find_section(f, ".kstrtab");
+       if (strsec == NULL) {
+               strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);
+               p->string_offset = 0;
+               loc = strsec->contents;
+       } else {
+               p->string_offset = strsec->header.sh_size;
+               loc = obj_extend_section(strsec, len);
+       }
+       memcpy(loc, string, len);
+}
+
+static void
+obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                                struct obj_symbol *sym)
+{
+       struct obj_symbol_patch *p;
+
+       p = xmalloc(sizeof(*p));
+       p->next = f->symbol_patches;
+       p->reloc_secidx = secidx;
+       p->reloc_offset = offset;
+       p->sym = sym;
+       f->symbol_patches = p;
+}
+
+static void obj_check_undefineds(struct obj_file *f)
+{
+       unsigned i;
+
+       for (i = 0; i < HASH_BUCKETS; ++i) {
+               struct obj_symbol *sym;
+               for (sym = f->symtab[i]; sym; sym = sym->next)
+                       if (sym->secidx == SHN_UNDEF) {
+                               if (ELF_ST_BIND(sym->info) == STB_WEAK) {
+                                       sym->secidx = SHN_ABS;
+                                       sym->value = 0;
+                               } else {
+                                       if (!flag_quiet)
+                                               bb_error_msg_and_die("unresolved symbol %s", sym->name);
+                               }
+                       }
+       }
+}
+
+static void obj_allocate_commons(struct obj_file *f)
+{
+       struct common_entry {
+               struct common_entry *next;
+               struct obj_symbol *sym;
+       } *common_head = NULL;
+
+       unsigned long i;
+
+       for (i = 0; i < HASH_BUCKETS; ++i) {
+               struct obj_symbol *sym;
+               for (sym = f->symtab[i]; sym; sym = sym->next)
+                       if (sym->secidx == SHN_COMMON) {
+                               /* Collect all COMMON symbols and sort them by size so as to
+                                  minimize space wasted by alignment requirements.  */
+                               {
+                                       struct common_entry **p, *n;
+                                       for (p = &common_head; *p; p = &(*p)->next)
+                                               if (sym->size <= (*p)->sym->size)
+                                                       break;
+
+                                       n = alloca(sizeof(*n));
+                                       n->next = *p;
+                                       n->sym = sym;
+                                       *p = n;
+                               }
+                       }
+       }
+
+       for (i = 1; i < f->local_symtab_size; ++i) {
+               struct obj_symbol *sym = f->local_symtab[i];
+               if (sym && sym->secidx == SHN_COMMON) {
+                       struct common_entry **p, *n;
+                       for (p = &common_head; *p; p = &(*p)->next)
+                               if (sym == (*p)->sym)
+                                       break;
+                               else if (sym->size < (*p)->sym->size) {
+                                       n = alloca(sizeof(*n));
+                                       n->next = *p;
+                                       n->sym = sym;
+                                       *p = n;
+                                       break;
+                               }
+               }
+       }
+
+       if (common_head) {
+               /* Find the bss section.  */
+               for (i = 0; i < f->header.e_shnum; ++i)
+                       if (f->sections[i]->header.sh_type == SHT_NOBITS)
+                               break;
+
+               /* If for some reason there hadn't been one, create one.  */
+               if (i == f->header.e_shnum) {
+                       struct obj_section *sec;
+
+                       f->sections = xrealloc_vector(f->sections, 2, i);
+                       f->sections[i] = sec = arch_new_section();
+                       f->header.e_shnum = i + 1;
+
+                       sec->header.sh_type = SHT_PROGBITS;
+                       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+                       sec->name = ".bss";
+                       sec->idx = i;
+               }
+
+               /* Allocate the COMMONS.  */
+               {
+                       ElfW(Addr) bss_size = f->sections[i]->header.sh_size;
+                       ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;
+                       struct common_entry *c;
+
+                       for (c = common_head; c; c = c->next) {
+                               ElfW(Addr) align = c->sym->value;
+
+                               if (align > max_align)
+                                       max_align = align;
+                               if (bss_size & (align - 1))
+                                       bss_size = (bss_size | (align - 1)) + 1;
+
+                               c->sym->secidx = i;
+                               c->sym->value = bss_size;
+
+                               bss_size += c->sym->size;
+                       }
+
+                       f->sections[i]->header.sh_size = bss_size;
+                       f->sections[i]->header.sh_addralign = max_align;
+               }
+       }
+
+       /* For the sake of patch relocation and parameter initialization,
+          allocate zeroed data for NOBITS sections now.  Note that after
+          this we cannot assume NOBITS are really empty.  */
+       for (i = 0; i < f->header.e_shnum; ++i) {
+               struct obj_section *s = f->sections[i];
+               if (s->header.sh_type == SHT_NOBITS) {
+                       if (s->header.sh_size != 0)
+                               s->contents = memset(xmalloc(s->header.sh_size),
+                                               0, s->header.sh_size);
+                       else
+                               s->contents = NULL;
+
+                       s->header.sh_type = SHT_PROGBITS;
+               }
+       }
+}
+
+static unsigned long obj_load_size(struct obj_file *f)
+{
+       unsigned long dot = 0;
+       struct obj_section *sec;
+
+       /* Finalize the positions of the sections relative to one another.  */
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+               ElfW(Addr) align;
+
+               align = sec->header.sh_addralign;
+               if (align && (dot & (align - 1)))
+                       dot = (dot | (align - 1)) + 1;
+
+               sec->header.sh_addr = dot;
+               dot += sec->header.sh_size;
+       }
+
+       return dot;
+}
+
+static int obj_relocate(struct obj_file *f, ElfW(Addr) base)
+{
+       int i, n = f->header.e_shnum;
+       int ret = 1;
+
+       /* Finalize the addresses of the sections.  */
+
+       f->baseaddr = base;
+       for (i = 0; i < n; ++i)
+               f->sections[i]->header.sh_addr += base;
+
+       /* And iterate over all of the relocations.  */
+
+       for (i = 0; i < n; ++i) {
+               struct obj_section *relsec, *symsec, *targsec, *strsec;
+               ElfW(RelM) * rel, *relend;
+               ElfW(Sym) * symtab;
+               const char *strtab;
+
+               relsec = f->sections[i];
+               if (relsec->header.sh_type != SHT_RELM)
+                       continue;
+
+               symsec = f->sections[relsec->header.sh_link];
+               targsec = f->sections[relsec->header.sh_info];
+               strsec = f->sections[symsec->header.sh_link];
+
+               rel = (ElfW(RelM) *) relsec->contents;
+               relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+               symtab = (ElfW(Sym) *) symsec->contents;
+               strtab = (const char *) strsec->contents;
+
+               for (; rel < relend; ++rel) {
+                       ElfW(Addr) value = 0;
+                       struct obj_symbol *intsym = NULL;
+                       unsigned long symndx;
+                       ElfW(Sym) * extsym = 0;
+                       const char *errmsg;
+
+                       /* Attempt to find a value to use for this relocation.  */
+
+                       symndx = ELF_R_SYM(rel->r_info);
+                       if (symndx) {
+                               /* Note we've already checked for undefined symbols.  */
+
+                               extsym = &symtab[symndx];
+                               if (ELF_ST_BIND(extsym->st_info) == STB_LOCAL) {
+                                       /* Local symbols we look up in the local table to be sure
+                                          we get the one that is really intended.  */
+                                       intsym = f->local_symtab[symndx];
+                               } else {
+                                       /* Others we look up in the hash table.  */
+                                       const char *name;
+                                       if (extsym->st_name)
+                                               name = strtab + extsym->st_name;
+                                       else
+                                               name = f->sections[extsym->st_shndx]->name;
+                                       intsym = obj_find_symbol(f, name);
+                               }
+
+                               value = obj_symbol_final_value(f, intsym);
+                               intsym->referenced = 1;
+                       }
+#if SHT_RELM == SHT_RELA
+#if defined(__alpha__) && defined(AXP_BROKEN_GAS)
+                       /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9.  */
+                       if (!extsym || !extsym->st_name ||
+                                       ELF_ST_BIND(extsym->st_info) != STB_LOCAL)
+#endif
+                               value += rel->r_addend;
+#endif
+
+                       /* Do it! */
+                       switch (arch_apply_relocation
+                                       (f, targsec, /*symsec,*/ intsym, rel, value)
+                       ) {
+                       case obj_reloc_ok:
+                               break;
+
+                       case obj_reloc_overflow:
+                               errmsg = "Relocation overflow";
+                               goto bad_reloc;
+                       case obj_reloc_dangerous:
+                               errmsg = "Dangerous relocation";
+                               goto bad_reloc;
+                       case obj_reloc_unhandled:
+                               errmsg = "Unhandled relocation";
+bad_reloc:
+                               if (extsym) {
+                                       bb_error_msg("%s of type %ld for %s", errmsg,
+                                                       (long) ELF_R_TYPE(rel->r_info),
+                                                       strtab + extsym->st_name);
+                               } else {
+                                       bb_error_msg("%s of type %ld", errmsg,
+                                                       (long) ELF_R_TYPE(rel->r_info));
+                               }
+                               ret = 0;
+                               break;
+                       }
+               }
+       }
+
+       /* Finally, take care of the patches.  */
+
+       if (f->string_patches) {
+               struct obj_string_patch *p;
+               struct obj_section *strsec;
+               ElfW(Addr) strsec_base;
+               strsec = obj_find_section(f, ".kstrtab");
+               strsec_base = strsec->header.sh_addr;
+
+               for (p = f->string_patches; p; p = p->next) {
+                       struct obj_section *targsec = f->sections[p->reloc_secidx];
+                       *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+                               = strsec_base + p->string_offset;
+               }
+       }
+
+       if (f->symbol_patches) {
+               struct obj_symbol_patch *p;
+
+               for (p = f->symbol_patches; p; p = p->next) {
+                       struct obj_section *targsec = f->sections[p->reloc_secidx];
+                       *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+                               = obj_symbol_final_value(f, p->sym);
+               }
+       }
+
+       return ret;
+}
+
+static int obj_create_image(struct obj_file *f, char *image)
+{
+       struct obj_section *sec;
+       ElfW(Addr) base = f->baseaddr;
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+               char *secimg;
+
+               if (sec->contents == 0 || sec->header.sh_size == 0)
+                       continue;
+
+               secimg = image + (sec->header.sh_addr - base);
+
+               /* Note that we allocated data for NOBITS sections earlier.  */
+               memcpy(secimg, sec->contents, sec->header.sh_size);
+       }
+
+       return 1;
+}
+
+/*======================================================================*/
+
+static struct obj_file *obj_load(FILE *fp, int loadprogbits UNUSED_PARAM)
+{
+       struct obj_file *f;
+       ElfW(Shdr) * section_headers;
+       size_t shnum, i;
+       char *shstrtab;
+
+       /* Read the file header.  */
+
+       f = arch_new_file();
+       f->symbol_cmp = strcmp;
+       f->symbol_hash = obj_elf_hash;
+       f->load_order_search_start = &f->load_order;
+
+       fseek(fp, 0, SEEK_SET);
+       if (fread(&f->header, sizeof(f->header), 1, fp) != 1) {
+               bb_perror_msg_and_die("error reading ELF header");
+       }
+
+       if (f->header.e_ident[EI_MAG0] != ELFMAG0
+                       || f->header.e_ident[EI_MAG1] != ELFMAG1
+                       || f->header.e_ident[EI_MAG2] != ELFMAG2
+                       || f->header.e_ident[EI_MAG3] != ELFMAG3) {
+               bb_error_msg_and_die("not an ELF file");
+       }
+       if (f->header.e_ident[EI_CLASS] != ELFCLASSM
+                       || f->header.e_ident[EI_DATA] != (BB_BIG_ENDIAN
+                               ? ELFDATA2MSB : ELFDATA2LSB)
+                       || f->header.e_ident[EI_VERSION] != EV_CURRENT
+                       || !MATCH_MACHINE(f->header.e_machine)) {
+               bb_error_msg_and_die("ELF file not for this architecture");
+       }
+       if (f->header.e_type != ET_REL) {
+               bb_error_msg_and_die("ELF file not a relocatable object");
+       }
+
+       /* Read the section headers.  */
+
+       if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
+               bb_error_msg_and_die("section header size mismatch: %lu != %lu",
+                               (unsigned long) f->header.e_shentsize,
+                               (unsigned long) sizeof(ElfW(Shdr)));
+       }
+
+       shnum = f->header.e_shnum;
+       f->sections = xmalloc(sizeof(struct obj_section *) * shnum);
+       memset(f->sections, 0, sizeof(struct obj_section *) * shnum);
+
+       section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
+       fseek(fp, f->header.e_shoff, SEEK_SET);
+       if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) {
+               bb_perror_msg_and_die("error reading ELF section headers");
+       }
+
+       /* Read the section data.  */
+
+       for (i = 0; i < shnum; ++i) {
+               struct obj_section *sec;
+
+               f->sections[i] = sec = arch_new_section();
+
+               sec->header = section_headers[i];
+               sec->idx = i;
+
+               if (sec->header.sh_size) {
+                       switch (sec->header.sh_type) {
+                       case SHT_NULL:
+                       case SHT_NOTE:
+                       case SHT_NOBITS:
+                               /* ignore */
+                               break;
+
+                       case SHT_PROGBITS:
+#if LOADBITS
+                               if (!loadprogbits) {
+                                       sec->contents = NULL;
+                                       break;
+                               }
+#endif
+                       case SHT_SYMTAB:
+                       case SHT_STRTAB:
+                       case SHT_RELM:
+                               if (sec->header.sh_size > 0) {
+                                       sec->contents = xmalloc(sec->header.sh_size);
+                                       fseek(fp, sec->header.sh_offset, SEEK_SET);
+                                       if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
+                                               bb_perror_msg_and_die("error reading ELF section data");
+                                       }
+                               } else {
+                                       sec->contents = NULL;
+                               }
+                               break;
+
+#if SHT_RELM == SHT_REL
+                       case SHT_RELA:
+                               bb_error_msg_and_die("RELA relocations not supported on this architecture");
+#else
+                       case SHT_REL:
+                               bb_error_msg_and_die("REL relocations not supported on this architecture");
+#endif
+                       default:
+                               if (sec->header.sh_type >= SHT_LOPROC) {
+                                       /* Assume processor specific section types are debug
+                                          info and can safely be ignored.  If this is ever not
+                                          the case (Hello MIPS?), don't put ifdefs here but
+                                          create an arch_load_proc_section().  */
+                                       break;
+                               }
+
+                               bb_error_msg_and_die("can't handle sections of type %ld",
+                                               (long) sec->header.sh_type);
+                       }
+               }
+       }
+
+       /* Do what sort of interpretation as needed by each section.  */
+
+       shstrtab = f->sections[f->header.e_shstrndx]->contents;
+
+       for (i = 0; i < shnum; ++i) {
+               struct obj_section *sec = f->sections[i];
+               sec->name = shstrtab + sec->header.sh_name;
+       }
+
+       for (i = 0; i < shnum; ++i) {
+               struct obj_section *sec = f->sections[i];
+
+               /* .modinfo should be contents only but gcc has no attribute for that.
+                * The kernel may have marked .modinfo as ALLOC, ignore this bit.
+                */
+               if (strcmp(sec->name, ".modinfo") == 0)
+                       sec->header.sh_flags &= ~SHF_ALLOC;
+
+               if (sec->header.sh_flags & SHF_ALLOC)
+                       obj_insert_section_load_order(f, sec);
+
+               switch (sec->header.sh_type) {
+               case SHT_SYMTAB:
+                       {
+                               unsigned long nsym, j;
+                               char *strtab;
+                               ElfW(Sym) * sym;
+
+                               if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
+                                       bb_error_msg_and_die("symbol size mismatch: %lu != %lu",
+                                                       (unsigned long) sec->header.sh_entsize,
+                                                       (unsigned long) sizeof(ElfW(Sym)));
+                               }
+
+                               nsym = sec->header.sh_size / sizeof(ElfW(Sym));
+                               strtab = f->sections[sec->header.sh_link]->contents;
+                               sym = (ElfW(Sym) *) sec->contents;
+
+                               /* Allocate space for a table of local symbols.  */
+                               j = f->local_symtab_size = sec->header.sh_info;
+                               f->local_symtab = xzalloc(j * sizeof(struct obj_symbol *));
+
+                               /* Insert all symbols into the hash table.  */
+                               for (j = 1, ++sym; j < nsym; ++j, ++sym) {
+                                       ElfW(Addr) val = sym->st_value;
+                                       const char *name;
+                                       if (sym->st_name)
+                                               name = strtab + sym->st_name;
+                                       else if (sym->st_shndx < shnum)
+                                               name = f->sections[sym->st_shndx]->name;
+                                       else
+                                               continue;
+#if defined(__SH5__)
+                                       /*
+                                        * For sh64 it is possible that the target of a branch
+                                        * requires a mode switch (32 to 16 and back again).
+                                        *
+                                        * This is implied by the lsb being set in the target
+                                        * address for SHmedia mode and clear for SHcompact.
+                                        */
+                                       val |= sym->st_other & 4;
+#endif
+                                       obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
+                                                       val, sym->st_size);
+                               }
+                       }
+                       break;
+
+               case SHT_RELM:
+                       if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
+                               bb_error_msg_and_die("relocation entry size mismatch: %lu != %lu",
+                                               (unsigned long) sec->header.sh_entsize,
+                                               (unsigned long) sizeof(ElfW(RelM)));
+                       }
+                       break;
+                       /* XXX  Relocation code from modutils-2.3.19 is not here.
+                        * Why?  That's about 20 lines of code from obj/obj_load.c,
+                        * which gets done in a second pass through the sections.
+                        * This BusyBox insmod does similar work in obj_relocate(). */
+               }
+       }
+
+       return f;
+}
+
+#if ENABLE_FEATURE_INSMOD_LOADINKMEM
+/*
+ * load the unloaded sections directly into the memory allocated by
+ * kernel for the module
+ */
+
+static int obj_load_progbits(FILE *fp, struct obj_file *f, char *imagebase)
+{
+       ElfW(Addr) base = f->baseaddr;
+       struct obj_section* sec;
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+
+               /* section already loaded? */
+               if (sec->contents != NULL)
+                       continue;
+
+               if (sec->header.sh_size == 0)
+                       continue;
+
+               sec->contents = imagebase + (sec->header.sh_addr - base);
+               fseek(fp, sec->header.sh_offset, SEEK_SET);
+               if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
+                       bb_perror_msg("error reading ELF section data");
+                       return 0;
+               }
+
+       }
+       return 1;
+}
+#endif
+
+static void hide_special_symbols(struct obj_file *f)
+{
+       static const char *const specials[] = {
+               SPFX "cleanup_module",
+               SPFX "init_module",
+               SPFX "kernel_version",
+               NULL
+       };
+
+       struct obj_symbol *sym;
+       const char *const *p;
+
+       for (p = specials; *p; ++p) {
+               sym = obj_find_symbol(f, *p);
+               if (sym != NULL)
+                       sym->info = ELF_ST_INFO(STB_LOCAL, ELF_ST_TYPE(sym->info));
+       }
+}
+
+
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license)
+{
+       struct obj_section *sec;
+       /* This list must match *exactly* the list of allowable licenses in
+        * linux/include/linux/module.h.  Checking for leading "GPL" will not
+        * work, somebody will use "GPL sucks, this is proprietary".
+        */
+       static const char *const gpl_licenses[] = {
+               "GPL",
+               "GPL v2",
+               "GPL and additional rights",
+               "Dual BSD/GPL",
+               "Dual MPL/GPL"
+       };
+
+       sec = obj_find_section(f, ".modinfo");
+       if (sec) {
+               const char *value, *ptr, *endptr;
+               ptr = sec->contents;
+               endptr = ptr + sec->header.sh_size;
+               while (ptr < endptr) {
+                       value = strchr(ptr, '=');
+                       if (value && strncmp(ptr, "license", value-ptr) == 0) {
+                               unsigned i;
+                               if (license)
+                                       *license = value+1;
+                               for (i = 0; i < ARRAY_SIZE(gpl_licenses); ++i) {
+                                       if (strcmp(value+1, gpl_licenses[i]) == 0)
+                                               return 0;
+                               }
+                               return 2;
+                       }
+                       ptr = strchr(ptr, '\0');
+                       if (ptr)
+                               ptr++;
+                       else
+                               ptr = endptr;
+               }
+       }
+       return 1;
+}
+
+#define TAINT_FILENAME                  "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE        (1 << 0)
+#define TAINT_FORCED_MODULE             (1 << 1)
+#define TAINT_UNSAFE_SMP                (1 << 2)
+#define TAINT_URL                       "http://www.tux.org/lkml/#export-tainted"
+
+static void set_tainted(int fd, const char *m_name,
+               int kernel_has_tainted, int taint, const char *text1, const char *text2)
+{
+       static smallint printed_info;
+
+       char buf[80];
+       int oldval;
+
+       if (fd < 0 && !kernel_has_tainted)
+               return;         /* New modutils on old kernel */
+       printf("Warning: loading %s will taint the kernel: %s%s\n",
+                       m_name, text1, text2);
+       if (!printed_info) {
+               printf("  See %s for information about tainted modules\n", TAINT_URL);
+               printed_info = 1;
+       }
+       if (fd >= 0) {
+               read(fd, buf, sizeof(buf)-1);
+               buf[sizeof(buf)-1] = '\0';
+               oldval = strtoul(buf, NULL, 10);
+               sprintf(buf, "%d\n", oldval | taint);
+               write(fd, buf, strlen(buf));
+       }
+}
+
+/* Check if loading this module will taint the kernel. */
+static void check_tainted_module(struct obj_file *f, const char *m_name)
+{
+       static const char tainted_file[] ALIGN1 = TAINT_FILENAME;
+
+       int fd, kernel_has_tainted;
+       const char *ptr;
+
+       kernel_has_tainted = 1;
+       fd = open(tainted_file, O_RDWR);
+       if (fd < 0) {
+               if (errno == ENOENT)
+                       kernel_has_tainted = 0;
+               else if (errno == EACCES)
+                       kernel_has_tainted = 1;
+               else {
+                       perror(tainted_file);
+                       kernel_has_tainted = 0;
+               }
+       }
+
+       switch (obj_gpl_license(f, &ptr)) {
+               case 0:
+                       break;
+               case 1:
+                       set_tainted(fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
+                       break;
+               case 2:
+                       /* The module has a non-GPL license so we pretend that the
+                        * kernel always has a taint flag to get a warning even on
+                        * kernels without the proc flag.
+                        */
+                       set_tainted(fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
+                       break;
+               default:
+                       set_tainted(fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
+                       break;
+       }
+
+       if (flag_force_load)
+               set_tainted(fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
+
+       if (fd >= 0)
+               close(fd);
+}
+#else /* FEATURE_CHECK_TAINTED_MODULE */
+#define check_tainted_module(x, y) do { } while (0);
+#endif /* FEATURE_CHECK_TAINTED_MODULE */
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* 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.
+ */
+#if !ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+#define get_module_version(f, str) get_module_version(str)
+#endif
+static int
+get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+       return new_get_module_version(f, str);
+#else  /* FEATURE_INSMOD_VERSION_CHECKING */
+       strncpy(str, "???", sizeof(str));
+       return -1;
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+}
+
+/* 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)
+{
+       static const char symprefix[] ALIGN1 = "__insmod_";
+       static const char section_names[][8] = {
+               ".text",
+               ".rodata",
+               ".data",
+               ".bss",
+               ".sbss"
+       };
+
+       struct obj_section *sec;
+       struct obj_symbol *sym;
+       char *name, *absolute_filename;
+       char str[STRVERSIONLEN];
+       unsigned i;
+       int l, lm_name, lfilename, use_ksymtab, version;
+       struct stat statbuf;
+
+       /* WARNING: was using realpath, but replaced by readlink to stop using
+        * lots of stack. But here it seems to be able to cause problems? */
+       absolute_filename = xmalloc_readlink(filename);
+       if (!absolute_filename)
+               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_noexport;
+
+       sec = obj_find_section(f, ".this");
+       if (sec) {
+               /* 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,
+                               (int)(2 * sizeof(statbuf.st_mtime)), statbuf.st_mtime,
+                               version);
+               sym = obj_add_symbol(f, name, -1,
+                               ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+                               sec->idx, sec->header.sh_addr, 0);
+               if (use_ksymtab)
+                       new_add_ksymtab(f, sym);
+       }
+       free(absolute_filename);
+#ifdef _NOT_SUPPORTED_
+       /* 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, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+                               sec->idx, sec->header.sh_addr, 0);
+               if (use_ksymtab)
+                       new_add_ksymtab(f, sym);
+       }
+#endif /* _NOT_SUPPORTED_ */
+       /* tag the desired sections if size is non-zero */
+
+       for (i = 0; i < ARRAY_SIZE(section_names); ++i) {
+               sec = obj_find_section(f, section_names[i]);
+               if (sec && 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, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+                                       sec->idx, sec->header.sh_addr, 0);
+                       if (use_ksymtab)
+                               new_add_ksymtab(f, sym);
+               }
+       }
+}
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP
+static void print_load_map(struct obj_file *f)
+{
+       struct obj_section *sec;
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
+       struct obj_symbol **all, **p;
+       int i, nsyms, *loaded;
+       struct obj_symbol *sym;
+#endif
+       /* Report on the section layout.  */
+
+       printf("Sections:       Size      %-*s  Align\n",
+                       (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;
+
+               printf("%-15s %08lx  %0*lx  2**%d\n",
+                               sec->name,
+                               (long)sec->header.sh_size,
+                               (int) (2 * sizeof(void *)),
+                               (long)sec->header.sh_addr,
+                               a);
+       }
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
+       /* Quick reference which section indices are loaded.  */
+
+       i = f->header.e_shnum;
+       loaded = alloca(sizeof(int) * i);
+       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;
+
+       /* And list them.  */
+       printf("\nSymbols:\n");
+       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 {
+                       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 (ELF_ST_BIND(sym->info) == STB_LOCAL)
+                       type = tolower(type);
+
+               printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
+                               type, sym->name);
+       }
+#endif
+}
+#else /* !FEATURE_INSMOD_LOAD_MAP */
+static void print_load_map(struct obj_file *f UNUSED_PARAM)
+{
+}
+#endif
+
+int FAST_FUNC bb_init_module_24(const char *m_filename, const char *options UNUSED_PARAM)
+{
+       int k_crcs;
+       unsigned long m_size;
+       ElfW(Addr) m_addr;
+       struct obj_file *f;
+       struct utsname uts;
+       int exit_status = EXIT_FAILURE;
+       int m_has_modinfo;
+       char *m_name;
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+       char m_strversion[STRVERSIONLEN];
+       int m_version, m_crcs;
+#endif
+       FILE *fp;
+
+       uname(&uts);
+       fp = fopen_for_read(m_filename);
+       if (fp == NULL)
+               return EXIT_FAILURE;
+
+       m_name = xstrdup(bb_basename(m_filename));
+       *strrchr(m_name, '.') = 0;
+
+       f = obj_load(fp, LOADBITS);
+
+       if (get_modinfo_value(f, "kernel_version") == NULL)
+               m_has_modinfo = 0;
+       else
+               m_has_modinfo = 1;
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+       /* Version correspondence?  */
+       if (!flag_quiet) {
+               if (m_has_modinfo) {
+                       m_version = new_get_module_version(f, m_strversion);
+                       if (m_version == -1) {
+                               bb_error_msg_and_die("cannot find the kernel version the module was "
+                                               "compiled for");
+                       }
+               }
+
+               if (strncmp(uts.release, m_strversion, STRVERSIONLEN) != 0) {
+                       bb_error_msg("%skernel-module version mismatch\n"
+                               "\t%s was compiled for kernel version %s\n"
+                               "\twhile this kernel is version %s",
+                               flag_force_load ? "warning: " : "",
+                               m_name, m_strversion, uts.release);
+                       if (!flag_force_load)
+                               goto out;
+               }
+       }
+       k_crcs = 0;
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+       if (query_module(NULL, 0, NULL, 0, NULL))
+               bb_error_msg_and_die("not configured to support old kernels");
+       new_get_kernel_symbols();
+       k_crcs = new_is_kernel_checksummed();
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+       m_crcs = 0;
+       if (m_has_modinfo)
+               m_crcs = new_is_module_checksummed(f);
+
+       if (m_crcs != k_crcs)
+               obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+       /* Let the module know about the kernel symbols.  */
+       add_kernel_symbols(f);
+
+       /* Allocate common symbols, symbol tables, and string tables.  */
+
+       new_create_this_module(f, m_name);
+       obj_check_undefineds(f);
+       obj_allocate_commons(f);
+       check_tainted_module(f, m_name);
+
+       /* done with the module name, on to the optional var=value arguments */
+       new_process_module_arguments(f, options);
+
+       arch_create_got(f);
+       hide_special_symbols(f);
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+       add_ksymoops_symbols(f, m_filename, m_name);
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+       new_create_module_ksymtab(f);
+
+       /* Find current size of the module */
+       m_size = obj_load_size(f);
+
+       m_addr = create_module(m_name, m_size);
+       if (m_addr == (ElfW(Addr))(-1)) switch (errno) {
+               case EEXIST:
+                       bb_error_msg_and_die("a module named %s already exists", m_name);
+               case ENOMEM:
+                       bb_error_msg_and_die("can't allocate kernel memory for module; needed %lu bytes",
+                                       m_size);
+               default:
+                       bb_perror_msg_and_die("create_module: %s", m_name);
+       }
+
+#if !LOADBITS
+       /*
+        * the PROGBITS section was not loaded by the obj_load
+        * now we can load them directly into the kernel memory
+        */
+       if (!obj_load_progbits(fp, f, (char*)m_addr)) {
+               delete_module(m_name, 0);
+               goto out;
+       }
+#endif
+
+       if (!obj_relocate(f, m_addr)) {
+               delete_module(m_name, 0);
+               goto out;
+       }
+
+       if (!new_init_module(m_name, f, m_size)) {
+               delete_module(m_name, 0);
+               goto out;
+       }
+
+       if (flag_print_load_map)
+               print_load_map(f);
+
+       exit_status = EXIT_SUCCESS;
+
+ out:
+       if (fp)
+               fclose(fp);
+       free(m_name);
+
+       return exit_status;
+}
diff --git a/modutils/modutils.c b/modutils/modutils.c
new file mode 100644 (file)
index 0000000..18ea537
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Common modutils related functions for busybox
+ *
+ * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "modutils.h"
+
+#ifdef __UCLIBC__
+extern int init_module(void *module, unsigned long len, const char *options);
+extern int delete_module(const char *module, unsigned int flags);
+#else
+# include <sys/syscall.h>
+# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+#endif
+
+USE_FEATURE_2_4_MODULES(char *insmod_outputname);
+
+/*
+ a libbb candidate from ice age!
+*/
+llist_t FAST_FUNC *llist_find(llist_t *first, const char *str)
+{
+       while (first != NULL) {
+               if (strcmp(first->data, str) == 0)
+                       return first;
+               first = first->link;
+       }
+       return NULL;
+}
+
+void FAST_FUNC replace(char *s, char what, char with)
+{
+       while (*s) {
+               if (what == *s)
+                       *s = with;
+               ++s;
+       }
+}
+
+char * FAST_FUNC replace_underscores(char *s)
+{
+       replace(s, '-', '_');
+       return s;
+}
+
+int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
+{
+       char *tok;
+       int len = 0;
+
+       while ((tok = strsep(&string, delim)) != NULL) {
+               if (tok[0] == '\0')
+                       continue;
+               llist_add_to_end(llist, xstrdup(tok));
+               len += strlen(tok);
+       }
+       return len;
+}
+
+char * FAST_FUNC filename2modname(const char *filename, char *modname)
+{
+       int i;
+       char *from;
+
+       if (filename == NULL)
+               return NULL;
+       if (modname == NULL)
+               modname = xmalloc(MODULE_NAME_LEN);
+       from = bb_get_last_path_component_nostrip(filename);
+       for (i = 0; i < MODULE_NAME_LEN && from[i] != '\0' && from[i] != '.'; i++)
+               modname[i] = (from[i] == '-') ? '_' : from[i];
+       modname[i] = 0;
+
+       return modname;
+}
+
+const char * FAST_FUNC moderror(int err)
+{
+       switch (err) {
+       case -1:
+               return "no such module";
+       case ENOEXEC:
+               return "invalid module format";
+       case ENOENT:
+               return "unknown symbol in module, or unknown parameter";
+       case ESRCH:
+               return "module has wrong symbol version";
+       case ENOSYS:
+               return "kernel does not support requested operation";
+       default:
+               return strerror(err);
+       }
+}
+
+char * FAST_FUNC parse_cmdline_module_options(char **argv)
+{
+       char *options;
+       int optlen;
+
+       options = xzalloc(1);
+       optlen = 0;
+       while (*++argv) {
+               options = xrealloc(options, optlen + 2 + strlen(*argv) + 2);
+               /* Spaces handled by "" pairs, but no way of escaping quotes */
+               optlen += sprintf(options + optlen, (strchr(*argv,' ') ? "\"%s\" " : "%s "), *argv);
+       }
+       return options;
+}
+
+int FAST_FUNC bb_init_module(const char *filename, const char *options)
+{
+       size_t len = MAXINT(ssize_t);
+       char *image;
+       int rc = ENOENT;
+
+#if ENABLE_FEATURE_2_4_MODULES
+       if (get_linux_version_code() < KERNEL_VERSION(2,6,0))
+               return bb_init_module_24(filename, options);
+#endif
+
+       /* Use the 2.6 way */
+       image = (char *) xmalloc_open_zipped_read_close(filename, &len);
+       if (image) {
+               if (init_module(image, len, options) != 0)
+                       rc = errno;
+               else
+                       rc = 0;
+               free(image);
+       }
+
+       return rc;
+}
+
+int FAST_FUNC bb_delete_module(const char *module, unsigned int flags)
+{
+       return delete_module(module, flags);
+}
diff --git a/modutils/modutils.h b/modutils/modutils.h
new file mode 100644 (file)
index 0000000..32ee18b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Common modutils related functions for busybox
+ *
+ * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#ifndef __MODUTILS_H__
+#define __MODUTILS_H__
+
+#include "libbb.h"
+#include <stdio.h>
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility push(hidden)
+#endif
+
+/* As defined in linux/include/linux/module.h */
+#define MODULE_NAME_LEN 64
+
+const char *moderror(int err) FAST_FUNC;
+llist_t *llist_find(llist_t *first, const char *str) FAST_FUNC;
+void replace(char *s, char what, char with) FAST_FUNC;
+char *replace_underscores(char *s) FAST_FUNC;
+int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC ;
+char *filename2modname(const char *filename, char *modname) FAST_FUNC;
+char *parse_cmdline_module_options(char **argv) FAST_FUNC;
+
+#define INSMOD_OPTS "vq" USE_FEATURE_2_4_MODULES("sLo:fkx") \
+       USE_FEATURE_INSMOD_LOAD_MAP("m")
+#define INSMOD_ARGS USE_FEATURE_2_4_MODULES(, &insmod_outputname)
+
+enum {
+       INSMOD_OPT_VERBOSE      = 0x0001,
+       INSMOD_OPT_SILENT       = 0x0002,
+       INSMOD_OPT_SYSLOG       = 0x0004 * ENABLE_FEATURE_2_4_MODULES,
+       INSMOD_OPT_LOCK         = 0x0008 * ENABLE_FEATURE_2_4_MODULES,
+       INSMOD_OPT_OUTPUTNAME   = 0x0010 * ENABLE_FEATURE_2_4_MODULES,
+       INSMOD_OPT_FORCE        = 0x0020 * ENABLE_FEATURE_2_4_MODULES,
+       INSMOD_OPT_KERNELD      = 0x0040 * ENABLE_FEATURE_2_4_MODULES,
+       INSMOD_OPT_NO_EXPORT    = 0x0080 * ENABLE_FEATURE_2_4_MODULES,
+       INSMOD_OPT_PRINT_MAP    = 0x0100 * ENABLE_FEATURE_INSMOD_LOAD_MAP,
+#if ENABLE_FEATURE_2_4_MODULES
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP
+       INSMOD_OPT_UNUSED       = 0x0200,
+#else /* ENABLE_FEATURE_INSMOD_LOAD_MAP */
+       INSMOD_OPT_UNUSED       = 0x0100
+#endif
+#else /* ENABLE_FEATURE_2_4_MODULES */
+       INSMOD_OPT_UNUSED       = 0x0004
+#endif
+};
+
+int FAST_FUNC bb_init_module(const char *module, const char *options);
+int FAST_FUNC bb_delete_module(const char *module, unsigned int flags);
+
+#if ENABLE_FEATURE_2_4_MODULES
+extern char *insmod_outputname;
+
+int FAST_FUNC bb_init_module_24(const char *module, const char *options);
+#endif
+
+#if __GNUC_PREREQ(4,1)
+# pragma GCC visibility pop
+#endif
+
+#endif
index 5129b34958adb0da37bf13849da2a0d84f278f51..cdc690a6936ac6ea32540439f5679d19d4058209 100644 (file)
@@ -3,98 +3,45 @@
  * Mini rmmod implementation for busybox
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>
  *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
 #include "libbb.h"
-
-#ifdef __UCLIBC__
-extern int delete_module(const char *module, unsigned int flags);
-#else
-# include <sys/syscall.h>
-# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
-#endif
-
-#if ENABLE_FEATURE_2_6_MODULES
-static void filename2modname(char *modname, const char *afterslash)
-{
-       unsigned int i;
-       int kr_chk = 1;
-
-       if (ENABLE_FEATURE_2_4_MODULES
-                       && get_linux_version_code() <= KERNEL_VERSION(2,6,0))
-                               kr_chk = 0;
-
-       /* Convert to underscores, stop at first . */
-       for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) {
-               if (kr_chk && (afterslash[i] == '-'))
-                       modname[i] = '_';
-               else
-                       modname[i] = afterslash[i];
-       }
-       modname[i] = '\0';
-}
-#else
-void filename2modname(char *modname, const char *afterslash);
-#endif
-
-// There really should be a header file for this...
-
-int query_module(const char *name, int which, void *buf,
-                       size_t bufsize, size_t *ret);
+#include "modutils.h"
 
 int rmmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int rmmod_main(int argc, char **argv)
+int rmmod_main(int argc UNUSED_PARAM, char **argv)
 {
-       int n, ret = EXIT_SUCCESS;
+       int n;
        unsigned int flags = O_NONBLOCK|O_EXCL;
 
-#define misc_buf bb_common_bufsiz1
-
        /* Parse command line. */
-       n = getopt32(argv, "wfa");
+       n = getopt32(argv, "wfas"); // -s ignored
+       argv += optind;
+
        if (n & 1)      // --wait
                flags &= ~O_NONBLOCK;
        if (n & 2)      // --force
                flags |= O_TRUNC;
        if (n & 4) {
                /* Unload _all_ unused modules via NULL delete_module() call */
-               /* until the number of modules does not change */
-               size_t nmod = 0; /* number of modules */
-               size_t pnmod = -1; /* previous number of modules */
-
-               while (nmod != pnmod) {
-                       if (delete_module(NULL, flags) != 0) {
-                               if (errno == EFAULT)
-                                       return ret;
-                               bb_perror_msg_and_die("rmmod");
-                       }
-                       pnmod = nmod;
-                       // the 1 here is QM_MODULES.
-                       if (ENABLE_FEATURE_QUERY_MODULE_INTERFACE && query_module(NULL,
-                                       1, misc_buf, sizeof(misc_buf),
-                                       &nmod))
-                       {
-                               bb_perror_msg_and_die("QM_MODULES");
-                       }
-               }
+               if (bb_delete_module(NULL, flags) != 0 && errno != EFAULT)
+                       bb_perror_msg_and_die("rmmod");
                return EXIT_SUCCESS;
        }
 
-       if (optind == argc)
+       if (!*argv)
                bb_show_usage();
 
-       for (n = optind; n < argc; n++) {
-               if (ENABLE_FEATURE_2_6_MODULES) {
-                       filename2modname(misc_buf, bb_basename(argv[n]));
-               }
-
-               if (delete_module(ENABLE_FEATURE_2_6_MODULES ? misc_buf : argv[n], flags)) {
-                       bb_simple_perror_msg(argv[n]);
-                       ret = EXIT_FAILURE;
-               }
+       while (*argv) {
+               char modname[MODULE_NAME_LEN];
+               filename2modname(bb_basename(*argv++), modname);
+               if (bb_delete_module(modname, flags))
+                       bb_error_msg_and_die("cannot unload '%s': %s",
+                                            modname, moderror(errno));
        }
 
-       return ret;
+       return EXIT_SUCCESS;
 }