home *** CD-ROM | disk | FTP | other *** search
- /* Insert a module into a running kernel.
- Copyright 1996, 1997 Linux International.
-
- New implementation contributed by Richard Henderson <rth@tamu.edu>
- Based on original work by Bjorn Eckwall <bj0rn@blox.se>
-
- 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. */
-
- #ident "$Id: insmod.c,v 1.1.1.1 1998/01/06 20:51:07 ewt Exp $"
-
- #include <sys/types.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <alloca.h>
- #include <limits.h>
- #include <ctype.h>
- #include <errno.h>
- #include <stddef.h>
- #include <sys/utsname.h>
- #include <sys/stat.h>
-
- #include "module.h"
- #include "obj.h"
- #include "util.h"
- #include "version.h"
-
- #include "logger.h"
-
- #define STRVERSIONLEN 32
-
-
- /*======================================================================*/
-
- int flag_force_load = 0;
- int flag_autoclean = 0;
- int flag_silent_poll = 0;
- int flag_verbose = 0;
- int flag_export = 1;
- int flag_load_map = 0;
-
- struct external_module
- {
- const char *name;
- ElfW(Addr) addr;
- int used;
- size_t nsyms;
- struct new_module_symbol *syms;
- };
-
- struct new_module_symbol *ksyms;
- size_t nksyms;
-
- struct external_module *ext_modules;
- int n_ext_modules;
- int n_ext_modules_used;
-
-
- /*======================================================================*/
-
- /* Given a bare module name, poke through the module path to find the file. */
-
- static char *
- search_module_path(char *base)
- {
- static const 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/ipv6:"
- "/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/ipv6:"
- "/lib/modules/default/misc:"
- "/lib/modules/fs:"
- "/lib/modules/net:"
- "/lib/modules/scsi:"
- "/lib/modules/block:"
- "/lib/modules/cdrom:"
- "/lib/modules/ipv4:"
- "/lib/modules/ipv6:"
- "/lib/modules/misc";
-
- char *path, *p, *filename;
- struct utsname uts_info;
- size_t len;
-
- if ((path = getenv("MODPATH")) == NULL)
- path = (char *)default_path;
-
- /* Make a copy so's we can mung it with strtok. */
- len = strlen(path)+1;
- p = alloca(len);
- path = memcpy(p, path, len);
-
- uname(&uts_info);
- filename = xmalloc(PATH_MAX);
-
- for (p = strtok(path, ":"); p != NULL ; p = strtok(NULL, ":"))
- {
- struct stat sb;
-
- len = snprintf(filename, PATH_MAX, p, uts_info.release);
- len += snprintf(filename+len, PATH_MAX-len, "/%s", base);
-
- if (stat(filename, &sb) == 0 && S_ISREG(sb.st_mode))
- return filename;
-
- snprintf(filename+len, PATH_MAX-len, ".o");
-
- if (stat(filename, &sb) == 0 && S_ISREG(sb.st_mode))
- return filename;
- }
-
- free(filename);
- return NULL;
- }
-
- /* Get the kernel version in the canonical integer form. */
-
- static int
- get_kernel_version(char str[STRVERSIONLEN])
- {
- struct utsname uts_info;
- char *p, *q;
- int a, b, c;
-
- if (uname(&uts_info) < 0)
- return -1;
- strncpy(str, uts_info.release, STRVERSIONLEN);
- p = uts_info.release;
-
- a = strtoul(p, &p, 10);
- if (*p != '.')
- return -1;
- b = strtoul(p+1, &p, 10);
- if (*p != '.')
- return -1;
- c = strtoul(p+1, &q, 10);
- if (p+1 == q)
- return -1;
-
- return a << 16 | b << 8 | c;
- }
-
- /* String comparison for non-co-versioned kernel and module. */
-
- 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);
- }
-
- /* 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;
-
- for (i = 0, s = syms; i < nsyms; ++i, ++s)
- {
- /* We don't really need to add all of the symbols, just the ones
- that already exist in the symtab -- this covers the undefineds
- and the weaks, all the rest get ignored. */
-
- struct obj_symbol *sym;
- sym = obj_find_symbol(f, (char *)s->name);
- if (sym)
- {
- sym = obj_add_symbol(f, (char *)s->name,
- ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE),
- idx, s->value, 0);
-
- /* Did our symbol just get installed? If so, mark the
- module as "used". */
-
- if (sym->secidx == idx)
- used = 1;
- }
- }
-
- return used;
- }
-
- static void
- add_kernel_symbols(struct obj_file *f)
- {
- struct external_module *m;
- size_t 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 void
- hide_special_symbols(struct obj_file *f)
- {
- static const char * const specials[] =
- {
- "cleanup_module",
- "init_module",
- "kernel_version",
- NULL
- };
-
- struct obj_symbol *sym;
- const char * const *p;
-
- for (p = specials; *p ; ++p)
- if ((sym = obj_find_symbol(f, *p)) != NULL)
- sym->info = ELFW(ST_INFO)(STB_LOCAL, ELFW(ST_TYPE)(sym->info));
- }
-
- static void
- print_load_map(struct obj_file *f)
- {
- int
- load_map_cmp(const void *a, const void *b)
- {
- struct obj_symbol **as = (struct obj_symbol **)a;
- struct obj_symbol **bs = (struct obj_symbol **)b;
- unsigned long aa = obj_symbol_final_value(f, *as);
- unsigned long ba = obj_symbol_final_value(f, *bs);
- return aa < ba ? -1 : aa > ba ? 1 : 0;
- }
-
- int i, nsyms, *loaded;
- struct obj_symbol *sym;
- struct obj_symbol **all, **p;
- struct obj_section *sec;
-
- /* Report on the section layout. */
-
- lprintf("Sections: Size %-*s Align",
- (int)(2*sizeof(void*)), "Address");
- for (sec = f->load_order; sec ; sec = sec->load_next)
- {
- int a;
- unsigned long tmp;
- for (a = -1, tmp = sec->header.sh_addralign; tmp ; ++a)
- tmp >>= 1;
- if (a == -1)
- a = 0;
-
- lprintf("%-16s%08lx %0*lx 2**%d", sec->name, sec->header.sh_size,
- (int)(2*sizeof(void*)), sec->header.sh_addr, a);
- }
-
- /* Quick reference which section indicies are loaded. */
-
- loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
- while (--i >= 0)
- loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
-
- /* Collect the symbols we'll be listing. */
-
- for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
- for (sym = f->symtab[i]; sym; sym = sym->next)
- if (sym->secidx <= SHN_HIRESERVE
- && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
- ++nsyms;
-
- all = alloca(nsyms * sizeof(struct obj_symbol *));
-
- for (i = 0, p = all; i < HASH_BUCKETS; ++i)
- for (sym = f->symtab[i]; sym; sym = sym->next)
- if (sym->secidx <= SHN_HIRESERVE
- && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
- *p++ = sym;
-
- /* Sort them by final value. */
-
- qsort(all, nsyms, sizeof(struct obj_file *), load_map_cmp);
-
- /* And list them. */
-
- lprintf("\nSymbols:");
- for (p = all; p < all+nsyms; ++p)
- {
- char type = '?';
- unsigned long value;
-
- sym = *p;
- if (sym->secidx == SHN_ABS)
- {
- type = 'A';
- value = sym->value;
- }
- else if (sym->secidx == SHN_UNDEF)
- {
- type = 'U';
- value = 0;
- }
- else
- {
- struct obj_section *sec = f->sections[sym->secidx];
-
- if (sec->header.sh_type == SHT_NOBITS)
- type = 'B';
- else if (sec->header.sh_flags & SHF_ALLOC)
- {
- if (sec->header.sh_flags & SHF_EXECINSTR)
- type = 'T';
- else if (sec->header.sh_flags & SHF_WRITE)
- type = 'D';
- else
- type = 'R';
- }
-
- value = sym->value + sec->header.sh_addr;
- }
-
- if (ELFW(ST_BIND)(sym->info) == STB_LOCAL)
- type = tolower(type);
-
- lprintf("%0*lx %c %s", (int)(2*sizeof(void*)), value,
- type, sym->name);
- }
- }
-
- /*======================================================================*/
- /* Functions relating to module loading in pre 2.1 kernels. */
-
- /* Fetch all the symbols and divvy them up as appropriate for the modules. */
-
- static int
- old_get_kernel_symbols(void)
- {
- struct old_kernel_sym *ks, *k;
- struct new_module_symbol *s;
- struct external_module *mod;
- int nks, nms, nmod, i;
-
- nks = get_kernel_syms(NULL);
- if (nks < 0)
- {
- error("get_kernel_syms: %m");
- return 0;
- }
-
- ks = k = xmalloc(nks * sizeof(*ks));
-
- if (get_kernel_syms(ks) != nks)
- {
- error("inconsistency with get_kernel_syms -- is someone else "
- "playing with modules?");
- free(ks);
- return 0;
- }
-
- /* Collect the module information. */
-
- mod = NULL;
- nmod = -1;
-
- while (k->name[0] == '#' && k->name[1])
- {
- struct old_kernel_sym *k2;
- struct new_module_symbol *s;
-
- /* Find out how many symbols this module has. */
- for (k2 = k+1; k2->name[0] != '#'; ++k2)
- continue;
- nms = k2 - k - 1;
-
- mod = xrealloc(mod, (++nmod+1) * sizeof(*mod));
- mod[nmod].name = k->name+1;
- mod[nmod].addr = k->value;
- mod[nmod].used = 0;
- mod[nmod].nsyms = nms;
- mod[nmod].syms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL);
-
- for (i = 0, ++k; i < nms; ++i, ++s, ++k)
- {
- s->name = (unsigned long)k->name;
- s->value = k->value;
- }
-
- k = k2;
- }
-
- ext_modules = mod;
- n_ext_modules = nmod+1;
-
- /* Now collect the symbols for the kernel proper. */
-
- if (k->name[0] == '#')
- ++k;
-
- nksyms = nms = nks - (k - ks);
- ksyms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL);
-
- for (i = 0; i < nms; ++i, ++s, ++k)
- {
- s->name = (unsigned long)k->name;
- s->value = k->value;
- }
-
- return 1;
- }
-
- /* Return the kernel symbol checksum version, or zero if not used. */
-
- static int
- old_is_kernel_checksummed(void)
- {
- /* Using_Versions is the first symbol. */
- if (nksyms > 0 && strcmp((char *)ksyms[0].name, "Using_Versions") == 0)
- return ksyms[0].value;
- else
- return 0;
- }
-
- /* Get the module's kernel version in the canonical integer form. */
-
- static int
- old_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
- {
- struct obj_symbol *sym;
- char *p, *q;
- int a, b, c;
-
- sym = obj_find_symbol(f, "kernel_version");
- if (sym == NULL)
- return -1;
- p = f->sections[sym->secidx]->contents + sym->value;
- 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;
- }
-
- static int
- old_is_module_checksummed(struct obj_file *f)
- {
- return obj_find_symbol(f, "Using_Versions") != NULL;
- }
-
- static int
- old_create_mod_use_count(struct obj_file *f)
- {
- struct obj_section *sec;
-
- sec = obj_create_alloced_section_first(f, ".moduse", sizeof(long),
- sizeof(long));
-
- obj_add_symbol(f, "mod_use_count_", ELFW(ST_INFO)(STB_LOCAL, STT_OBJECT),
- sec->idx, 0, sizeof(long));
-
- return 1;
- }
-
- static int
- old_process_module_arguments(struct obj_file *f, int argc, char **argv)
- {
- while (argc > 0)
- {
- char *p, *q;
- struct obj_symbol *sym;
- int *loc;
-
- p = *argv;
- if ((q = strchr(p, '=')) == NULL)
- continue;
- *q++ = '\0';
-
- sym = obj_find_symbol(f, p);
- if (sym == NULL)
- {
- error("symbol for parameter %s not found", p);
- return 0;
- }
-
- loc = (int *)(f->sections[sym->secidx]->contents + sym->value);
-
- if (*q >= '0' && *q <= '9')
- {
- do
- *loc++ = strtol(q, &q, 0);
- while (*q++ == ',');
- }
- else
- {
- char *contents = f->sections[sym->secidx]->contents;
- char *loc = contents + sym->value;
- char *r; /* To search for commas */
-
- /* Break the string with comas */
- while((r = strchr(q, ',')) != (char *) NULL)
- {
- *r++ = '\0';
- obj_string_patch(f, sym->secidx, loc-contents, q);
- loc += sizeof(char*);
- q = r;
- }
-
- /* last part */
- obj_string_patch(f, sym->secidx, loc-contents, q);
- }
-
- argc--, argv++;
- }
-
- return 1;
- }
-
- static int
- old_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
- {
- char *image;
- struct old_mod_routines routines;
- struct old_symbol_table *symtab;
- int ret;
-
- /* Create the symbol table */
- {
- int nsyms = 0, strsize = 0, total;
-
- /* Size things first... */
- if (flag_export)
- {
- int i;
- for (i = 0; i < HASH_BUCKETS; ++i)
- {
- struct obj_symbol *sym;
- for (sym = f->symtab[i]; sym; sym = sym->next)
- if (ELFW(ST_BIND)(sym->info) != STB_LOCAL
- && sym->secidx <= SHN_HIRESERVE)
- {
- sym->ksymidx = nsyms++;
- strsize += strlen(sym->name)+1;
- }
- }
- }
-
- total = (sizeof(struct old_symbol_table)
- + nsyms * sizeof(struct old_module_symbol)
- + n_ext_modules_used * sizeof(struct old_module_ref)
- + strsize);
- symtab = xmalloc(total);
- symtab->size = total;
- symtab->n_symbols = nsyms;
- symtab->n_refs = n_ext_modules_used;
-
- if (flag_export && nsyms)
- {
- struct old_module_symbol *ksym;
- char *str;
- int i;
-
- ksym = symtab->symbol;
- str = ((char *)ksym
- + nsyms * sizeof(struct old_module_symbol)
- + n_ext_modules_used * sizeof(struct old_module_ref));
-
- for (i = 0; i < HASH_BUCKETS; ++i)
- {
- struct obj_symbol *sym;
- for (sym = f->symtab[i]; sym; sym = sym->next)
- if (sym->ksymidx >= 0)
- {
- ksym->addr = obj_symbol_final_value(f, sym);
- ksym->name = (unsigned long)str - (unsigned long)symtab;
-
- str = stpcpy(str, sym->name)+1;
- ksym++;
- }
- }
- }
-
- if (n_ext_modules_used)
- {
- struct old_module_ref *ref;
- int i;
-
- ref = (struct old_module_ref *)
- ((char *)symtab->symbol + nsyms * sizeof(struct old_module_symbol));
-
- for (i = 0; i < n_ext_modules; ++i)
- if (ext_modules[i].used)
- ref++->module = ext_modules[i].addr;
- }
- }
-
- /* Fill in routines. */
-
- routines.init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
- routines.cleanup
- = obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module"));
-
- /* Whew! All of the initialization is complete. Collect the final
- module image and give it to the kernel. */
-
- image = xmalloc(m_size);
- obj_create_image(f, image);
-
- /* image holds the complete relocated module, accounting correctly for
- mod_use_count. However the old module kernel support assume that
- it is receiving something which does not contain mod_use_count. */
- ret = old_sys_init_module(m_name, image+sizeof(long),
- m_size | (flag_autoclean ? OLD_MOD_AUTOCLEAN : 0),
- &routines, symtab);
- if (ret)
- error("init_module: %m");
-
- free(image);
- free(symtab);
-
- return ret == 0;
- }
-
- /*======================================================================*/
- /* Functions relating to module loading after 2.1.18. */
-
- /* Fetch the loaded modules, and all currently exported symbols. */
-
- static int
- 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. */
-
- module_names = xmalloc(bufsize = 256);
- retry_modules_load:
- if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret))
- {
- if (errno == ENOSPC)
- {
- module_names = xrealloc(module_names, bufsize = ret);
- goto retry_modules_load;
- }
- error("QM_MODULES: %m\n");
- return 0;
- }
-
- n_ext_modules = nmod = ret;
- ext_modules = modules = xmalloc(nmod * sizeof(*modules));
- memset(modules, 0, nmod * sizeof(*modules));
-
- /* Collect the modules' symbols. */
-
- 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;
- error("module %s: QM_INFO: %m", mn);
- return 0;
- }
-
- syms = xmalloc(bufsize = 1024);
- retry_mod_sym_load:
- if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret))
- {
- switch (errno)
- {
- case ENOSPC:
- syms = xrealloc(syms, bufsize = ret);
- goto retry_mod_sym_load;
- case ENOENT:
- /* The module was removed out from underneath us. */
- continue;
- default:
- error("module %s: QM_SYMBOLS: %m", mn);
- return 0;
- }
- }
- 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)
- {
- syms = xrealloc(syms, bufsize = ret);
- goto retry_kern_sym_load;
- }
- error("kernel: QM_SYMBOLS: %m");
- return 0;
- }
- nksyms = nsyms = ret;
- ksyms = syms;
-
- for (j = 0, s = syms; j < nsyms; ++j, ++s)
- s->name += (unsigned long)syms;
-
- return 1;
- }
-
- /* 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 char *
- get_modinfo_value(struct obj_file *f, const char *key)
- {
- struct obj_section *sec;
- char *p, *v, *n, *ep;
- size_t klen = strlen(key);
-
- sec = obj_find_section(f, ".modinfo");
- if (sec == NULL)
- return NULL;
-
- p = sec->contents;
- ep = p + sec->header.sh_size;
- while (p < ep)
- {
- v = strchr(p, '=');
- n = strchr(p, '\0');
- if (v)
- {
- if (v-p == klen && strncmp(p, key, klen) == 0)
- return v+1;
- }
- else
- {
- if (n-p == klen && strcmp(p, key) == 0)
- return n;
- }
- p = n+1;
- }
-
- return NULL;
- }
-
- /* 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;
- 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;
- }
-
- static int
- new_is_module_checksummed(struct obj_file *f)
- {
- const char *p = get_modinfo_value(f, "using_checksums");
- if (p)
- return atoi(p);
- else
- return 0;
- }
-
- static int
- new_process_module_arguments(struct obj_file *f, int argc, char **argv)
- {
- while (argc > 0)
- {
- char *p, *q, *key;
- struct obj_symbol *sym;
- char *contents, *loc;
- int min, max, n;
-
- p = *argv;
- if ((q = strchr(p, '=')) == NULL)
- 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)
- {
- error("invalid parameter %s", key);
- return 0;
- }
-
- sym = obj_find_symbol(f, key);
- if (sym == NULL)
- {
- error("symbol for parameter %s not found", key);
- return 0;
- }
-
- 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')
- {
- error("improperly terminated string argument for %s",
- key);
- return 0;
- }
- else 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;
- }
- ++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 usefull, as the previous case
- doesn't null terminate the string ??? */
- str[r - q] = '\0';
-
- /* Keep next fields */
- q = r;
- }
- else
- {
- /* last string */
- str = q;
- q = "";
- }
- }
-
- 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 !) */
- long charssize; /* size of each member */
-
- /* Get the size of each member */
- /* Probably we should do that outside the loop ? */
- if(!isdigit(*(p + 1)))
- {
- error("parameter type 'c' for %s must be followed by the maximum size",
- key);
- return 0;
- }
- charssize = strtoul(p + 1, (char **) NULL, 10);
-
- /* Check length */
- if(strlen(str) >= charssize)
- {
- error("string too long for %s (max %ld)",
- key, charssize - 1);
- return 0;
- }
-
- /* Copy to location */
- strcpy((char *) loc, str);
- loc += charssize;
- }
- }
- else
- {
- long v = strtol(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:
- error("unknown parameter type '%c' for %s",
- *p, key);
- return 0;
- }
- }
-
- 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)
- {
- error("too many values for %s (max %d)", key, max);
- return 0;
- }
- ++q;
- break;
-
- default:
- error("invalid argument syntax for %s", key);
- return 0;
- }
- }
-
- end_of_arg:
- if (n < min)
- {
- error("too few values for %s (min %d)", key, min);
- return 0;
- }
-
- argc--, argv++;
- }
-
- return 1;
- }
-
- static int
- 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, "__this_module", ELFW(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);
-
- return 1;
- }
-
- 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, "__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_export && !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 (ELFW(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;
-
- sec = obj_find_section(f, ".this");
- module = (struct new_module *)sec->contents;
-
- module->size_of_struct = sizeof(*module);
- module->size = m_size;
- module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
-
- sec = obj_find_section(f, "__ksymtab");
- if (sec && sec->header.sh_size)
- {
- module->syms = sec->header.sh_addr;
- module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
- }
-
- if (n_ext_modules_used)
- {
- sec = obj_find_section(f, ".kmodtab");
- module->deps = sec->header.sh_addr;
- module->ndeps = n_ext_modules_used;
- }
-
- module->init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
- module->cleanup
- = obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module"));
-
- sec = obj_find_section(f, "__ex_table");
- if (sec)
- {
- module->ex_table_start = sec->header.sh_addr;
- module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
- }
-
- if (!arch_init_module(f, module))
- return 0;
-
- /* Whew! All of the initialization is complete. Collect the final
- module image and give it to the kernel. */
-
- image = xmalloc(m_size);
- obj_create_image(f, image);
-
- ret = new_sys_init_module(m_name, (struct new_module *)image);
- if (ret)
- error("init_module: %m");
-
- free(image);
-
- return ret == 0;
- }
-
-
- /*======================================================================*/
-
- int
- main(int argc, char **argv)
- {
- int k_version;
- int k_crcs;
- int k_new_syscalls;
- char k_strversion[STRVERSIONLEN];
-
- char *m_name = NULL;
- int m_version;
- ElfW(Addr) m_addr;
- unsigned long m_size;
- int m_crcs;
- int m_has_modinfo;
- char m_strversion[STRVERSIONLEN];
-
- char *filename;
- FILE *fp;
- struct obj_file *f;
- int o, noload = 0;
-
- error_file = "insmod";
-
- /* Process the command line. */
-
- while ((o = getopt(argc, argv, "fkmno:psvVxX")) != EOF)
- switch (o)
- {
- case 'f': /* force loading */
- flag_force_load = 1;
- break;
- case 'k': /* module loaded by kerneld, auto-cleanable */
- flag_autoclean = 1;
- break;
- case 'm': /* generate load map */
- flag_load_map = 1;
- break;
- case 'n':
- noload = 1;
- break;
- case 'o': /* name the output module */
- m_name = optarg;
- break;
- case 'p': /* silent poll mode */
- flag_silent_poll = 1;
- break;
- case 's': /* start syslog */
- setsyslog("insmod");
- break;
- case 'v': /* verbose output */
- flag_verbose = 1;
- break;
- case 'V':
- fputs("insmod version " MODUTILS_VERSION "\n", stderr);
- break;
- case 'x': /* do not export externs */
- flag_export = 0;
- break;
- case 'X': /* do export externs */
- flag_export = 1;
- break;
- default:
- goto usage;
- }
-
- if (optind >= argc)
- {
- usage:
- 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 load map (so crashes can be traced)\n"
- " -o name Set internal modulname to name\n"
- " -p Poll mode; 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 (default)\n"
- , stderr);
- return 1;
- }
-
- filename = argv[optind++];
-
- if (m_name == NULL)
- {
- size_t len;
- char *p;
-
- if ((p = strrchr(filename, '/')) != NULL)
- p++;
- else
- p = filename;
- len = strlen(p);
- if (len > 2 && p[len-2] == '.' && p[len-1] == 'o')
- len -= 2;
- else if (len > 4 && p[len-4] == '.' && p[len-3] == 'm'
- && p[len-2] == 'o' && p[len-1] == 'd')
- len -= 4;
-
- m_name = xmalloc(len+1);
- memcpy(m_name, p, len);
- m_name[len] = '\0';
- }
-
- /* Locate the file to be loaded. */
-
- if (!strchr(filename, '/') && !strchr(filename, '.'))
- {
- char *tmp = search_module_path(filename);
- if (tmp == NULL)
- {
- error("%s: no module by that name found", filename);
- return 1;
- }
- filename = tmp;
- }
-
- error_file = filename;
-
- /* And open it. */
-
- if ((fp = fopen(filename, "r")) == NULL)
- {
- error("%s: %m", filename);
- return 1;
- }
- else if ((f = obj_load(fp)) == NULL)
- return 1;
- fclose(fp);
-
- /* Version correspondence? */
-
- k_version = get_kernel_version(k_strversion);
- m_version = new_get_module_version(f, m_strversion);
- if (m_version != -1)
- m_has_modinfo = 1;
- else
- {
- m_has_modinfo = 0;
- m_version = old_get_module_version(f, m_strversion);
- if (m_version == -1)
- {
- error("couldn't find the kernel version the module was compiled for");
- return 1;
- }
- }
-
- if (strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0)
- {
- if (flag_force_load)
- {
- lprintf("Warning: kernel-module version mismatch\n"
- "\t%s was compiled for kernel version %s\n"
- "\twhile this kernel is version %s\n",
- filename, m_strversion, k_strversion);
- }
- else
- {
- error("kernel-module version mismatch\n"
- "\t%s was compiled for kernel version %s\n"
- "\twhile this kernel is version %s.",
- filename, m_strversion, k_strversion);
- return 1;
- }
- }
-
- k_new_syscalls = !query_module(NULL, 0, NULL, 0, NULL);
-
- if (k_new_syscalls)
- {
- if (!new_get_kernel_symbols())
- return 1;
- k_crcs = new_is_kernel_checksummed();
- }
- else
- {
- if (!old_get_kernel_symbols())
- return 1;
- k_crcs = old_is_kernel_checksummed();
- }
-
- if (m_has_modinfo)
- m_crcs = new_is_module_checksummed(f);
- else
- m_crcs = old_is_module_checksummed(f);
-
- if (m_crcs != k_crcs)
- obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
-
- /* Let the module know about the kernel symbols. */
-
- add_kernel_symbols(f);
-
- /* Allocate common symbols, symbol tables, and string tables. */
-
- if (k_new_syscalls
- ? !new_create_this_module(f, m_name)
- : !old_create_mod_use_count(f))
- return 1;
-
- if (!obj_allocate_commons_and_check_undefineds(f))
- return 1;
-
- if (optind < argc)
- {
- if (m_has_modinfo
- ? !new_process_module_arguments(f, argc-optind, argv+optind)
- : !old_process_module_arguments(f, argc-optind, argv+optind))
- return 1;
- }
-
- arch_create_got(f);
- hide_special_symbols(f);
-
- if (k_new_syscalls)
- new_create_module_ksymtab(f);
-
- if (errors)
- return 1;
-
- /* If we were just checking, we made it. */
-
- if (flag_silent_poll)
- return 0;
-
- /* Module has now finished growing; find its size and install it. */
-
- m_size = obj_load_size(f);
-
- if (noload)
- {
- /* Don't bother actually touching the kernel. */
- m_addr = 0x12340000;
- }
- else
- {
- errno = 0;
- m_addr = create_module(m_name, m_size);
- switch (errno)
- {
- case 0:
- break;
- case EEXIST:
- error("a module named %s already exists", m_name);
- return 1;
- case ENOMEM:
- error("can't allocate kernel memory for module; needed %lu bytes",
- m_size);
- return 1;
- default:
- error("create_module: %m");
- return 1;
- }
- }
-
- if (!obj_relocate(f, m_addr))
- {
- if (!noload)
- delete_module(m_name);
- return 1;
- }
-
- if (noload)
- ;
- else if (k_new_syscalls)
- new_init_module(m_name, f, m_size);
- else
- old_init_module(m_name, f, m_size);
- if (errors)
- {
- if (!noload)
- delete_module(m_name);
- return 1;
- }
-
- if (flag_load_map)
- print_load_map(f);
-
- return 0;
- }
-