/* * Install a module in the kernel. * * See the file COPYING for your rights (GNU GPL) * * Originally by Anonymous (as far as I know...) * Linux version by Bas Laarhoven * Modified by Jon Tombs. * * Support for transient and resident symbols * added by Bjorn Ekwall in June 1994 (C) * * Load map option conceived by Derek Atkins * * Support for versioned kernels and symbols: Bjorn Ekwall in December 1994 * * Merged in ksyms and rmmod in December 1994: Bjorn Ekwall * * Support for ELF modules: Bjorn Ekwall in December 1994 after having * mangled sources from, and been enlightened * and supported by Eric Youngdale * (the kludges are all mine, don't blame Eric...) * * Support for array initializers: Bjorn Ekwall in January 1995 * Support for string initializers: Bjorn Ekwall in January 1995 * Fixed major bug in a.out bss variable handling: March '95, Bas. * ELF fixes from H.J.Lu * Many ELF and other fixes from: * James Bottomley * Full support for MODPATH setting: Henrik Storner * Removed limitation of unversioned module vs. versioned kernel: Bjorn * Handle all combinations of ELF vs a.out kernels/modules: Bjorn * Added syslog error reporting with option "-s": Jacques Gelinas * Added MOD_AUTOCLEAN and option "-k" (for kerneld"): Bjorn and Jacques * mc68000: Andreas Schwab * More fixes (ELF SHN_UNDEF et al) by Andreas Schwab in February 1996 * Added Sparc support: Eddie C. Dost * Various improvements added by Hans Lermen */ #ifndef MOD_AUTOCLEAN /* defined in by the "kerneld"-patch */ #define MOD_AUTOCLEAN 0x40000000 /* big enough, but no sign problems... */ #endif static char *default_path[] = { ".", "/linux/modules", "/lib/modules/%s/fs", "/lib/modules/%s/net", "/lib/modules/%s/scsi", "/lib/modules/%s/block", "/lib/modules/%s/cdrom", "/lib/modules/%s/ipv4", "/lib/modules/%s/misc", "/lib/modules/default/fs", "/lib/modules/default/net", "/lib/modules/default/scsi", "/lib/modules/default/block", "/lib/modules/default/cdrom", "/lib/modules/default/ipv4", "/lib/modules/default/misc", "/lib/modules/fs", "/lib/modules/net", "/lib/modules/scsi", "/lib/modules/block", "/lib/modules/cdrom", "/lib/modules/ipv4", "/lib/modules/misc", 0 }; #include "insmod.h" #include "../Version.h" #define is_global(sp) (aout_flag?(sp->u.n.n_type & N_EXT) : \ (ELF32_ST_BIND(sp->u.e.st_info) != STB_LOCAL)) #define is_undef(sp) (aout_flag?((sp->u.n.n_type & ~N_EXT) == N_UNDF) : \ (sp->u.e.st_shndx == SHN_UNDEF)) /* hack: sizeof(struct elfhdr) > sizeof(struct exec) */ Elf32_Ehdr header; int aout_flag; /* to know the format of the module */ int elf_kernel = 0; /* to know the format of the kernel */ size_t codesize; size_t progsize; size_t bss1size; size_t bss2size; char *textseg; int nsymbols; struct symbol *symtab; char *stringtab; unsigned long addr; int verbose = 0; int silent_poll = 0; static int export_flag = 1; /* See comment at option handler in main() */ static int force_load = 0; static int loadmap = 0; static int versioned_kernel = 0; static struct kernel_sym nullsym; static struct symbol *symroot; static struct utsname uts_info; static char kd_dbmfile[40]; void * ckalloc(size_t nbytes) { void *p; if ((p = malloc(nbytes)) == NULL) { perror("insmod: malloc failed"); exit(2); } return p; } void * ckrealloc(void *ptr, size_t nbytes) { void *p; if ((p = realloc(ptr, nbytes)) == NULL) { perror("insmod: realloc failed"); exit(2); } return p; } static int create_module(const char *name, unsigned long size) { return syscall( __NR_create_module, name, size); } static int init_module(const char *name, void *code, unsigned codesize, struct mod_routines *routines, struct symbol_table *syms) { return syscall( __NR_init_module, name, code, codesize, routines, syms); } static int delete_module(const char *name) { return syscall( __NR_delete_module, name); } static int get_kernel_syms(struct kernel_sym *buffer) { return syscall( __NR_get_kernel_syms, buffer); } static void check_version(int force_load) { unsigned long kernel_version; /* Check if module and kernel version match */ kernel_version = looksym("kernel_version"); if (strcmp( (char*) textseg + kernel_version, uts_info.release)) { insmod_error ( "Error: The module was compiled on kernel version %s.\n" " This kernel is version %s. They don't match!\n" " Check that the module is usable with the current kernel,\n" " recompile the module and try again.", (char*) textseg + kernel_version, uts_info.release); if (force_load) insmod_error(" Trying to load it anyway..."); else exit( 2); } } /* * unversioned kernel, versioned module */ static int m_strncmp(const char *tabentry, const char *lookfor, size_t n) { int len = strlen(lookfor); int retval; if ((retval = strncmp(tabentry, lookfor, len)) != 0) return retval; /* else */ if ((strncmp(tabentry + len, "_R", 2) == 0) && (strlen(tabentry + len) == 10)) return 0; else return strcmp(tabentry, lookfor); } /* * versioned kernel, unversioned module */ static int k_strncmp(const char *tabentry, const char *lookfor, size_t n) { int len = strlen(tabentry); int retval; if ((retval = strncmp(tabentry, lookfor, len)) != 0) return retval; /* else */ if ((strncmp(lookfor + len, "_R", 2) == 0) && (strlen(lookfor + len) == 10)) return 0; else return strcmp(tabentry, lookfor); } struct strpatch { int where; /* offset from start */ int what; }; static struct strpatch *stringpatches; static int n_stringpatches; static void push_string(char *string, int patch_offset) { int len; unsigned int string_offset = progsize; char *p; if ((p = strchr(string, ',')) != (char *)0) len = p - string; else len = strlen(string); /* * If we want to use any string (even one with only numbers): * enclose the string with quotes, i.e. ' or " * (most probably escaped, if from a shell) * * Inspired by Giorgio Caset */ if (((string[0] == '\'') || (string[0] == '"')) && (string[len - 1] == string[0])) { ++string; /* strip quote */ len -= 2; /* and quote at the end as well */ } progsize += len + 1; /* JEJB: don't like this, but if we're copying strings, better make * sure memory actually exists to copy them into */ textseg = ckrealloc(textseg, progsize); strncpy(textseg + string_offset, string, len); *(textseg + string_offset + len) = '\0'; if (n_stringpatches == 0) stringpatches = (struct strpatch *)ckalloc(sizeof(struct strpatch)); else stringpatches = (struct strpatch *)ckrealloc(stringpatches, (n_stringpatches + 1) * sizeof(struct strpatch)); (*(stringpatches + n_stringpatches)).where = patch_offset; (*(stringpatches + n_stringpatches)).what = string_offset; ++n_stringpatches; } int main(int argc, char **argv) { FILE *fp; struct exec *aouthdr = (struct exec *)&header; struct kernel_sym *curr_module = &nullsym; struct kernel_sym *ksymtab = NULL; struct kernel_sym *ksym = NULL; struct kernel_sym *resident_ksym_start = NULL; struct mod_routines routines; struct symbol *sp; struct strpatch *patch; unsigned long init_func, cleanup_func; int (*tabcomp) (const char *, const char *, size_t) = strncmp; int fatal_error; int i; int nksyms; int versioned_module; int found_resident = -1; int autoclean = 0; /* for insertions from kerneld: option "-k" */ int bss_offset; struct symbol_table *newtab; int module_refs = 0; /* number of references modules */ int n_symboldefs = 0; /* number of defined symbols in the module */ int string_table_size = 0; /* size of the new symbol table string table */ struct internal_symbol *symp; struct module_ref *refp; char *stringp; char *filename; char spare_path[200]; /* just testing... */ char *modname = NULL; char *otextseg; /* JEJB: store the initial textseg, so we get offset */ char *p; /* find basename */ if ((p = strrchr(argv[0], '/')) != (char *)0) ++p; else p = argv[0]; if (strcmp(p, "rmmod") == 0) return (rmmod(argc, argv))?1:0; if (strcmp(p, "ksyms") == 0) return ksyms(argc, argv); /* else this is insmod! */ while (argc > 1 && (argv[1][0] == '-')) { p = &(argv[1][1]); while (*p) { switch (*p) { case 'f': /* force loading */ force_load = 1; break; case 'k': /* module loaded by kerneld, auto-cleanable */ autoclean = MOD_AUTOCLEAN; break; case 'm': /* generate loadmap */ loadmap = 1; break; case 'o': modname = argv[2]; --argc; ++argv; break; case 'p': /* silent poll mode */ silent_poll = 1; break; case 's': insmod_setsyslog("insmod"); break; case 'v': /* verbose output */ verbose = 1; break; case 'V': printf("Version " MODULES_VERSION "\n"); break; case 'x': /* do _not_ export externs */ export_flag = 0; break; case 'X': /* _do_ export externs */ export_flag = 1; break; } ++p; } --argc; ++argv; } if (argc < 2) { fputs("Usage:\n" "insmod [-fkmopsvVxX] [-o name] module [[sym=value]...]\n" "\n" " module Filename of a loadable kernel module (*.o)\n" " -f Force loading under wrong kernel version\n" " -k Make module (autoclean)-able\n" " -m Generate loadmap (so crashes can be traced)\n" " -o name Set internal modulname to name\n" " -p Poll mode, just check if the module matches the kernel\n" " -s Report errors via syslog\n" " -v Verbose output\n" " -V Show version\n" " -x do *not* export externs\n" " -X *do* export externs\n" , stderr); exit(2); } uname(&uts_info); sprintf(kd_dbmfile, "/lib/modules/%s/persist.gdbm", uts_info.release); filename = argv[1]; argv += 2; --argc; /* get the size of the current kernel symbol table */ nksyms = get_kernel_syms(NULL); if (nksyms < 0) { insmod_error("get_kernel_sys failed: Cannot find Kernel symbols!"); exit(2); } if (nksyms) { ksymtab = (struct kernel_sym *) ckalloc(nksyms * sizeof *ksymtab); /* NOTE!!! The order of the symbols is important */ if (get_kernel_syms(ksymtab) != nksyms) { insmod_error ("Kernel symbol problem"); exit(2); } } /* Is this a kernel with appended CRC-versions? */ for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) { if (found_resident == -1 && strcmp(ksym->name, "#") == 0) { resident_ksym_start = ksym; found_resident = i; break; } } /* look only in residents */ if (found_resident != -1) { ++ksym; /* The first symbol in a versioned kernel is _Using_Versions */ if ((strcmp(ksym->name, "_Using_Versions") == 0) || (strcmp(ksym->name, "Using_Versions") == 0)) versioned_kernel = 1; /* look at a "normal" symbol: */ ++ksym; if (((i + 1) < nksyms) && (ksym->name[0] != '_')) elf_kernel = 1; } /* construct the module name */ if (modname == NULL) { int len; if ((p = strrchr(filename, '/')) != NULL) p++; else p = filename; len = strlen(p); if (len > 2 && strcmp(p + len - 2, ".o") == 0) len -= 2; else if (len > 4 && strcmp(p + len - 4, ".mod") == 0) len -= 4; modname = (char*) ckalloc(len + 1); memcpy(modname, p, len); modname[len] = '\0'; } if ((strchr(filename, '/') == 0) && (strchr(filename, '.') == 0)) { struct stat dummy; char **defp; char *path; char *onepath; char *p; /* Look in the path given by MODPATH environment, * if not found, look in the default directories. * 950319 + Bjorn */ if ((path = getenv("MODPATH")) == 0) { onepath = ckalloc(2000); /* or whatever */ onepath[0] = '\0'; for (defp = default_path; *defp; ++defp) { sprintf(spare_path, *defp, uts_info.release); strcat(onepath, spare_path); strcat(onepath, ":"); } } else { onepath = ckalloc(strlen(path)+2); sprintf(onepath, "%s:", path); } for (path = onepath; (*path != '\0'); path += strlen(path)+1) { for (p = path; (*p != ':') && (*p != '\0'); p++) ; *p = '\0'; sprintf(spare_path, "%s/%s", path, filename); if ((stat(spare_path, &dummy) >= 0) && (dummy.st_mode & S_IFREG)) break; /* else */ strcat(spare_path, ".o"); if ((stat(spare_path, &dummy) >= 0) && (dummy.st_mode & S_IFREG)) break; } free(onepath); filename = spare_path; } /* open file and read header */ if ((fp = fopen(filename, "r")) == NULL) { insmod_error ("Cannot open %s", filename); exit(2); } /* sizeof(struct elfhdr) > sizeof(struct exec) */ fread(&header, sizeof(Elf32_Ehdr), 1, fp); if (feof(fp) || ferror(fp)) { insmod_error ("Could not read header of %s", filename); exit(2); } symtab = (struct symbol *)0; nsymbols = 0; if (N_MAGIC((*aouthdr)) == OMAGIC) { char *errstr; if ((errstr = load_aout(fp)) != (char *)0) { insmod_error ("%s: %s", filename, errstr); exit(2); } } else if ((header.e_ident[0] == 0x7f) && (strncmp(&header.e_ident[1], "ELF",3) == 0) && (header.e_type == ET_REL) && #ifdef __i386__ ((header.e_machine == 3) || (header.e_machine == 6)) #endif #ifdef __mc68000__ header.e_machine == EM_68K #endif #ifdef __sparc__ header.e_machine == EM_SPARC #endif ) { char *errstr; if ((errstr = load_elf(fp)) != (char *)0) { insmod_error ("%s: %s", filename, errstr); exit(2); } } else { insmod_error ("%s: not an object file", filename); exit(2); } /* JEJB: now we have textseg fixed after the load, so save value * for later finding offset if realloc forces a move */ otextseg = textseg; if (findsym("Using_Versions", NULL, strncmp)) versioned_module = 1; else versioned_module = 0; /* check version info */ if (verbose) { insmod_error ("versioned kernel: %s\nversioned module: %s", versioned_kernel ? "yes" : "no", versioned_module ? "yes" : "no"); insmod_error ("%s kernel\n%s module", elf_kernel ? "ELF" : "a.out", aout_flag ? "a.out" : "ELF"); } /* * Logic: * * versioned_kernel versioned_module action * ================ ================ ============================= * no no same kernel and module version * all symbols must match * no yes same kernel and module version * ignore symbol version suffix * yes no same kernel and module version * ignore symbol version suffix * yes yes all symbols must match, * including the version suffix */ switch ((versioned_kernel << 1) + versioned_module) { case 0: /* unversioned_kernel, unversioned_module */ /* Uwe Bonnes : */ if (!force_load) check_version(force_load); tabcomp = strncmp; break; case 1: /* unversioned_kernel, versioned_module */ check_version(force_load); tabcomp = m_strncmp; break; case 2: /* versioned_kernel, unversioned_module */ check_version(force_load); tabcomp = k_strncmp; break; case 3: /* versioned_kernel, versioned_module */ if (force_load) check_version(force_load); tabcomp = strncmp; break; } /* get initialization and cleanup routines */ init_func = looksym("init_module"); cleanup_func = looksym("cleanup_module"); /* bind undefined symbols (ELF-stuff hidden in defsym()) */ defsym(strncmp, "mod_use_count_", 0 - sizeof (int), N_BSS | N_EXT, TRANSIENT); /* First: resolve symbols using kernel transient symbols */ /* Then: use resident kernel symbols to resolve the last ones... */ for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) { /* Magic in this version of the new get_kernel_syms: * Every module is sent along as a symbol, * where the module name is represented as "#module", and * the address of the module struct is stuffed into the value. * The name "#" means that the symbols that follow are * kernel resident. */ if (ksym->name[0] == '#') { curr_module = ksym; continue; } if (defsym(tabcomp, ksym->name + ((!elf_kernel && (ksym->name[0] == '_'))?1:0), ksym->value, N_ABS | N_EXT, /* this is safe since curr_module was initialized properly */ (curr_module->name[1]) ? TRANSIENT : RESIDENT)) { /* kludge: mark referenced modules */ if (curr_module->name[1] && /* but not the kernel */ (curr_module->name[0] == '#')) { curr_module->name[0] = '+'; ++module_refs; } } } /* allocate space for "common" symbols */ /* and check for undefined symbols */ fatal_error = 0; bss_offset = codesize + bss1size; /* * Scan symbol table to determine global bss size */ for (sp = symtab ; sp < symtab + nsymbols ; sp++) { if ((symname(sp) == (char *)0) || (*symname(sp) == '\0')) continue; if ( ((aout_flag && (sp->u.n.n_type == (N_UNDF | N_EXT))) || (!aout_flag && is_global(sp) && is_undef(sp))) && (symvalue(sp) != 0)) { int len; if (aout_flag) { sp->u.n.n_type = N_BSS | N_EXT; len = symvalue(sp); if (verbose) insmod_error ( "bss2 sym %X, size =%7d: %s", bss_offset, len, symname( sp)); } else { /* I'm not sure I understand this... */ sp->u.e.st_info = (STB_GLOBAL << 4)| STT_OBJECT; sp->u.e.st_shndx = SHN_ABS; len = (sp->u.e.st_size)?(sp->u.e.st_size):4; } symvalue(sp) = bss_offset; bss_offset += len; } else if (is_undef(sp)) { if (versioned_module) { char *v = strrchr(symname(sp), '_'); if (v && (strncmp(v, "_R", 2) == 0) && (strlen(v) == 10)) *v = '\0'; insmod_error ("%s: wrong version or undefined", symname(sp)); if (v && !(*v)) *v = '_'; } else insmod_error ("%s undefined", symname(sp)); fatal_error = 1; } } if (fatal_error) { insmod_error ( "Loading failed! The module symbols " "(from linux-%s) don't match your linux-%s", (char*) textseg + looksym("kernel_version"), uts_info.release); exit(2); } /* Change values according to the parameters to "insmod" */ { struct symbol *change; int *patchme; char *param; char *val; int value; while (argc > 1) { param = *argv; ++argv; --argc; /* * This makes the following contruct legal: * insmod module.o -o new_name * This is used by the "options" configuration * lines for modprobe. */ if (strcmp(param, "-o") == 0) { modname = (char*) ckalloc(strlen(argv[0]) + 1); strcpy(modname, argv[0]); --argc; ++argv; continue; } if ((val = strchr(param, '=')) == NULL) continue; *val = '\0'; /* mark end of tag */ if (versioned_module) change = findsym(param, NULL, m_strncmp); else change = findsym(param, NULL, strncmp); if (change == NULL) { insmod_error ("Symbol '%s' not found", param); exit(2); } patchme = (int *)(textseg + symvalue(change)); do { ++val; #ifdef PERSIST /* * This enables the useage of previously * saved module persistent data to update * the symbol value. * * Syntax: * * symbol=?key:default,default... * * If no value can be found for the key, * the default values will be used. */ if (*val == '?') { #include GDBM_FILE dbf; datum key, data; char *x; key.dptr = val + 1; if ((x = strchr(val, ':')) != NULL) { *x = '\0'; val = ++x; } if ((key.dptr[0] == '\'') || (key.dptr[0] == '"')) { if ((x = strchr(key.dptr, key.dptr[0]))) { *x = '\0'; key.dptr++; } } key.dsize = strlen(key.dptr) + 1; if ((dbf = gdbm_open(kd_dbmfile, 0, GDBM_READER, 0600, NULL))) { data = gdbm_fetch(dbf, key); gdbm_close(dbf); if (data.dptr) { /* found a value! */ memcpy(patchme, data.dptr, data.dsize); free (data.dptr); /* ignore given info */ break; } } /* * No persistent data found, * use the given info */ if (*val == '\0') break; } #endif /* PERSIST */ if (*val < '0' || '9' < *val) { /* * Textseg may change during calls to * push_string() so we store the offset * * Timo Kokkonen */ if (*val != ',') push_string(val, symvalue(change)); } else { /* numerical */ if (val[0] == '0') { if (val[1] == 'x') sscanf(val, "%x", &value); else sscanf(val, "%o", &value); } else sscanf(val, "%d", &value); *patchme = value; } ++patchme; } while ((val = strchr(val, ',')) != (char *)0); } } /* * If we have survived this far, and are just checking, * that's it... */ if (silent_poll) exit(0); /* create the module */ errno = 0; /* make sure we have enough memory malloc'd for copy which kernel * will perform */ textseg = ckrealloc(textseg, progsize); /* We add "sizeof (int)" to skip over the use count */ addr = create_module(modname, progsize) + sizeof (int); switch (errno) { case EEXIST: insmod_error ("A module named %s already exists", modname); exit(2); case ENOMEM: insmod_error ("Cannot allocate space for module"); exit(2); case 0: break; default: perror("create_module"); exit(2); } /* perform relocation */ if (aout_flag) relocate_aout(fp, textseg - otextseg); else relocate_elf(fp, textseg - otextseg); /* * Patch in any new strings from the command line */ for (patch = stringpatches; n_stringpatches-- > 0; ++patch) { /* * Now we have to calculate the "absolute" address of the * string and update the "pointer" in the module image... * * Timo Kokkonen * * Pointer increment fixes by: * Alberto Vignani * Wolfgang Wander */ *((int *)(textseg + patch->where)) = addr + patch->what; /* insmod address */ /* kernel address */ } if (stringpatches) free(stringpatches); stringpatches = (struct strpatch *)0; n_stringpatches = 0; /* * Handle module "administrativia" */ init_func += addr; cleanup_func += addr; /* "hide" the module specific symbols, not to be exported! */ hidesym("cleanup_module"); hidesym("init_module"); hidesym("kernel_version"); hidesym("mod_use_count_"); /* Build the module symbol table */ /* abuse of *_other: * 0 refer kernel resident * 1 define module symbol, inserted in kernel syms * 2 refer module symbol * 3 won't happen (define resolved by other module?) */ /* * Get size info for the new symbol table * we already have module_refs */ for (sp = symtab ; export_flag && (sp < symtab + nsymbols) ; sp++) { if (is_global(sp) && (symother(sp) & DEF_BY_MODULE)) { string_table_size += strlen(symname(sp)) + 1; if (!elf_kernel) ++string_table_size; ++n_symboldefs; } } newtab = (struct symbol_table *)ckalloc(sizeof(struct symbol_table) + n_symboldefs * sizeof(struct internal_symbol) + module_refs * sizeof(struct module_ref) + string_table_size); newtab->size = sizeof(struct symbol_table) + n_symboldefs * sizeof(struct internal_symbol) + module_refs * sizeof(struct module_ref) + string_table_size; newtab->n_symbols = n_symboldefs; newtab->n_refs = module_refs; symp = &(newtab->symbol[0]); stringp = ((char *)symp) + n_symboldefs * sizeof(struct internal_symbol) + module_refs * sizeof(struct module_ref); /* update the string pointer (to a string index) in the symbol table */ for (sp = symtab ; export_flag && (sp < symtab + nsymbols) ; sp++) { if (is_global(sp) && (symother(sp) & DEF_BY_MODULE)) { symp->addr = (void *)(symvalue(sp) + addr); symp->name = (char *)(stringp - (char *)newtab); if (!elf_kernel) { strcpy(stringp, "_"); ++stringp; } strcpy(stringp, symname(sp)); stringp += strlen(symname(sp)) + 1; ++symp; } } refp = (struct module_ref *)symp; for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) { if (ksym->name[0] == '+') { refp->module = (struct module *)ksym->value; ++refp; } } /* load the module into the kernel */ /* NOTE: the symbol table == NULL if there are no defines/updates */ routines.init = (int (*)(void)) init_func; routines.cleanup = (void (*)(void)) cleanup_func; if (init_module(modname, textseg, /* An autoclean marker for a non-kerneld-aware kernel * will result in a rejection of the module. * This is what we want, since the "-k" option is useful * only with kerneld aware kernels... */ autoclean | progsize /*codesize*/, &routines, ((n_symboldefs + module_refs)? newtab : NULL)) < 0) { if (errno == EBUSY) { insmod_error ("Initialization of %s failed", modname); } else { perror("init_module"); } delete_module(modname); exit(1); } /* * Print a loadmap so that kernel panics can be identified. * Load map option conceived by Derek Atkins */ for (sp = symtab; loadmap && (sp < symtab + nsymbols) ; sp++) { char symtype = 'u'; if (aout_flag) { if ((sp->u.n.n_type & ~N_EXT) == N_ABS) continue; switch (sp->u.n.n_type & ~N_EXT) { case N_TEXT | N_EXT: symtype = 'T'; break; case N_TEXT: symtype = 't'; break; case N_DATA | N_EXT: symtype = 'D'; break; case N_DATA: symtype = 'd'; break; case N_BSS | N_EXT: symtype = 'B'; break; case N_BSS: symtype = 'b'; break; } } #if 0 else symtype = ' '; /* until someone does the work... */ #endif insmod_printf("%X %c %s\n", symvalue(sp) + addr, symtype, symname(sp)); } if (nksyms > 0) free(ksymtab); /* it has done its job */ exit(0); } void hidesym(const char *name) { struct symbol *sp; if ((sp = findsym(name, NULL, strncmp)) != NULL) { if (aout_flag) sp->u.n.n_type &= ~N_EXT; else sp->u.e.st_info = (STB_LOCAL << 4) | (ELF32_ST_TYPE(sp->u.e.st_info)); } } int defsym(int (*pfi) (const char *, const char *, size_t), const char *name, unsigned long value, int type, int source) { struct symbol *sp; /* * field "*_other" abused in the symbol table * in order to discriminate between defines and references * and resolves by kernel resident or transient symbols... */ if ((sp = findsym(name, NULL, pfi)) == NULL) return 0; /* symbol not used */ /* else */ /* * Multiply defined? * Well, this is what we _want_! * I.e. a module _should_ be able to replace a kernel resident * symbol, or even a symbol defined by another module! * NOTE: The symbols read from the kernel (the transient ones) * MUST be handled in the order: * last defined, ..., earlier defined, ... */ if (aout_flag) { if (sp->u.n.n_type != (N_UNDF | N_EXT)) return 0; /* symbol not used */ sp->u.n.n_type = type; } else { /* elf */ if ((ELF32_ST_BIND(sp->u.e.st_info) == STB_LOCAL) || (sp->u.e.st_shndx != SHN_UNDEF)) return 0; /* symbol not used */ /* hack follows... */ if (type & N_ABS) sp->u.e.st_info = (STB_GLOBAL << 4); else sp->u.e.st_info = (STB_LOCAL << 4); sp->u.e.st_shndx = SHN_ABS; /* or whatever */ } symother(sp) |= source; symvalue(sp) = value; return 1; /* symbol used */ } /* * Look up an name in the symbol table. If "add" is not null, add a * the entry to the table. The table is stored as a splay tree. */ struct symbol * findsym(const char *key, struct symbol *add, int (*pfi) (const char *, const char *, size_t)) { struct symbol *left, *right; struct symbol **leftp, **rightp; struct symbol *sp1, *sp2, *sp3; int cmp; int path1, path2; if (add) { if (aout_flag) add->u.n.n_un.n_name = (char *)key; else add->u.e.st_name = (Elf32_Word)key; } sp1 = symroot; if (sp1 == NULL) return add? symroot = add : NULL; leftp = &left, rightp = &right; for (;;) { cmp = (*pfi)(symname(sp1), key, SYM_MAX_NAME); if (cmp == 0) break; if (cmp > 0) { sp2 = sp1->child[0]; path1 = 0; } else { sp2 = sp1->child[1]; path1 = 1; } if (sp2 == NULL) { if (! add) break; sp2 = add; } cmp = (*pfi)(symname(sp2), key, SYM_MAX_NAME); if (cmp == 0) { one_level_only: if (path1 == 0) { /* sp2 is left child of sp1 */ *rightp = sp1; rightp = &sp1->child[0]; } else { *leftp = sp1; leftp = &sp1->child[1]; } sp1 = sp2; break; } if (cmp > 0) { sp3 = sp2->child[0]; path2 = 0; } else { sp3 = sp2->child[1]; path2 = 1; } if (sp3 == NULL) { if (! add) goto one_level_only; sp3 = add; } if (path1 == 0) { if (path2 == 0) { sp1->child[0] = sp2->child[1]; sp2->child[1] = sp1; *rightp = sp2; rightp = &sp2->child[0]; } else { *rightp = sp1; rightp = &sp1->child[0]; *leftp = sp2; leftp = &sp2->child[1]; } } else { if (path2 == 0) { *leftp = sp1; leftp = &sp1->child[1]; *rightp = sp2; rightp = &sp2->child[0]; } else { sp1->child[1] = sp2->child[0]; sp2->child[0] = sp1; *leftp = sp2; leftp = &sp2->child[1]; } } sp1 = sp3; } /* * Now sp1 points to the result of the search. If cmp is zero, * we had a match; otherwise not. */ *leftp = sp1->child[0]; *rightp = sp1->child[1]; sp1->child[0] = left; sp1->child[1] = right; symroot = sp1; return cmp == 0? sp1 : NULL; } unsigned long looksym(const char *name) { struct symbol *sp; sp = findsym(name, NULL, strncmp); if (sp == NULL) { insmod_error ("%s needed, but can't be found", name); exit(2); } return symvalue(sp); } /* * This is (was) rmmod, now merged with insmod! * * Original author: Jon Tombs , * and extended by Bjorn Ekwall in 1994 (C). * See the file COPYING for your rights (GNU GPL) */ #define WANT_TO_REMOVE 1 #define CAN_REMOVE 2 struct ref { int modnum; struct ref *next; }; struct mod { int status; /* or of: WANT_TO_REMOVE, CAN_REMOVE */ char name[MOD_MAX_NAME]; struct ref *ref; }; static struct mod *loaded; static int current = -1; /* build the references as shown in /proc/ksyms */ static void get_stacks() { FILE *fp; struct ref *rp; int i; char line[200]; /* or whatever... */ char *p; char *r; if ((fp = fopen("/proc/modules", "r")) == (FILE *)0) { perror("/proc/modules"); exit(1); } while (fgets(line, 200, fp)) { if (loaded) { ++current; loaded = (struct mod *)ckrealloc(loaded, (current + 1) * sizeof(struct mod)); } else { current = 0; loaded = (struct mod *)ckalloc(sizeof(struct mod)); } loaded[current].status = 0; loaded[current].ref = (struct ref *)0; /* save the module name */ p = strchr(line, ' '); *p = '\0'; strcpy(loaded[current].name, line); /* any references? */ if ((p = strchr(p + 1, '['))) { *(strrchr(p + 1, ']')) = '\0'; do { r = p + 1; if ((p = strchr(r, ' '))) *p = '\0'; for (i = 0; i < current; ++i) { if (strcmp(loaded[i].name, r) == 0) break; } if (i == current) { /* not found! */ insmod_error ( "Strange reference in " "/proc/modules: '%s' used by '%s'?", loaded[current].name, r); exit(1); } rp = (struct ref *)ckalloc(sizeof(struct ref)); rp->modnum = i; rp->next = loaded[current].ref; loaded[current].ref = rp; } while (p); } } fclose(fp); } int rmmod(int argc, char **argv) { struct ref *rp; int i, j; int errors = 0; int count; char **list; char *p; if (argc == 1) { fprintf(stderr, "usage: rmmod [-r] [-s] module ...\n"); return 1; } /* else */ if (strcmp(argv[1], "-r") != 0) { if (strcmp(argv[1], "-a") == 0) { /* delete all unused modules and stacks */ return (delete_module(NULL))?1:0; } /* else */ if (strcmp(argv[1], "-s") == 0){ insmod_setsyslog("rmmod"); --argc; ++argv; } while (argc > 1) { if ((p = strrchr(argv[1], '.')) && ((strcmp(p, ".o") == 0) || (strcmp(p, ".mod") == 0))) *p = '\0'; if (delete_module(argv[1]) < 0) { ++errors; perror(argv[1]); } --argc; ++argv; } return errors; } /* else recursive removal */ count = argc - 2; list = &(argv[2]); if (count <= 0) { insmod_error ("usage: rmmod [-r] module ..."); return 1; } get_stacks(); for (i = 0; i < count; ++i) { for (j = 0; j <= current; ++j) { if (strcmp(loaded[j].name, list[i]) == 0) { loaded[j].status = WANT_TO_REMOVE; break; } } if (j > current) { insmod_error("module '%s' not loaded", list[i]); ++errors; } } for (i = 0; i <= current; ++i) { if (loaded[i].ref || (loaded[i].status == WANT_TO_REMOVE)) loaded[i].status |= CAN_REMOVE; for (rp = loaded[i].ref; rp; rp = rp->next) { switch (loaded[rp->modnum].status) { case CAN_REMOVE: case WANT_TO_REMOVE | CAN_REMOVE: break; case WANT_TO_REMOVE: if (loaded[rp->modnum].ref == (struct ref *)0) break; /* else fallthtough */ default: loaded[i].status &= ~CAN_REMOVE; break; } } switch (loaded[i].status) { case CAN_REMOVE: case WANT_TO_REMOVE | CAN_REMOVE: if (delete_module(loaded[i].name) < 0) { ++errors; perror(loaded[i].name); } break; case WANT_TO_REMOVE: insmod_error("module '%s' is in use!", loaded[i].name); ++errors; break; } } return errors; } /* * Used to be ksyms.c, but merged as well... * * Get kernel symbol table(s). * * Bjorn Ekwall in 1994 (C) * See the file COPYING for your rights (GNU GPL) */ int ksyms(int argc, char **argv) { struct kernel_sym *ksymtab = NULL; struct kernel_sym *ksym = NULL; struct module module_struct; int nksyms; int i; int kmem = 0; int allsyms = 0; int show_header = 1; char *p; char *module_name = ""; while (argc > 1 && (argv[1][0] == '-')) { p = &(argv[1][1]); while (*p) { switch (*p) { case 'a': allsyms = 1; break; case 'm': if ((kmem = open("/dev/kmem", O_RDONLY)) < 0) { perror("/dev/kmem"); exit(2); } break; case 'h': show_header = 0; break; } ++p; } --argc; ++argv; } if (argc < 1) { fputs("Usage: ksyms [-a] [-h]\n", stderr); return 2; } /* get the size of the current kernel symbol table */ nksyms = get_kernel_syms(NULL); if (nksyms < 0) { insmod_error ("get_kernel_sys failed: Cannot find Kernel symbols!"); return 2; } if (nksyms) { ksymtab = (struct kernel_sym *) ckalloc(nksyms * sizeof *ksymtab); /* NOTE!!! The order of the symbols is important */ if (get_kernel_syms(ksymtab) != nksyms) { insmod_error("Kernel symbol problem"); return 2; } } if (show_header) printf("Address Symbol \tDefined by\n"); for (ksym = ksymtab, i = 0 ; i < nksyms ; ++i, ksym++) { /* Magic in this version of the new get_kernel_syms: * Every module is sent along as a symbol, * where the module name is represented as "#module", and * the address of the module struct is stuffed into the value. * The name "#" means that the symbols that follow are * kernel resident. */ if (ksym->name[0] == '#') { module_name = ksym->name + 1; if (!allsyms && (*module_name == '\0')) break; /* else */ if (kmem) { if ( (lseek(kmem, (off_t)ksym->value, SEEK_SET) > 0) && (read(kmem, (char *)&module_struct, sizeof(struct module)) == sizeof(struct module))) { printf("%lX --- (%dk) ---\t[%s]\n", (long)module_struct.addr, module_struct.size * 4, module_name); } else { perror("/dev/kmem"); return 2; } } continue; } printf("%lX %s", ksym->value, ksym->name); if (*module_name) printf("\t[%s]", module_name); printf("\n"); } return 0; }