#include <linux/errno.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <linux/mm.h> /* defines GFP_KERNEL */
#include <linux/string.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/config.h>
/*
* Originally by Anonymous (as far as I know...)
* Linux version by Bas Laarhoven <bas@vimec.nl>
* 0.99.14 version by Jon Tombs <jon@gtex02.us.es>,
*
* Heavily modified by Bjorn Ekwall <bj0rn@blox.se> May 1994 (C)
* This source is covered by the GNU GPL, the same as all kernel sources.
*
* Features:
* - Supports stacked modules (removable only of there are no dependents).
* - Supports table of symbols defined by the modules.
* - Supports /proc/ksyms, showing value, name and owner of all
* the symbols defined by all modules (in stack order).
* - Added module dependencies information into /proc/modules
* - Supports redefines of all symbols, for streams-like behaviour.
* - Compatible with older versions of insmod.
*
* New addition in December 1994: (Bjorn Ekwall, idea from Jacques Gelinas)
* - Externally callable function:
*
* "int register_symtab(struct symbol_table *)"
*
* This function can be called from within the kernel,
* and ALSO from loadable modules.
* The goal is to assist in modularizing the kernel even more,
* and finally: reducing the number of entries in ksyms.c
* since every subsystem should now be able to decide and
* control exactly what symbols it wants to export, locally!
*
* On 1-Aug-95: <Matti.Aarnio@utu.fi> altered code to use same style as
* do /proc/net/XXX "files". Namely allow more than 4kB
* (or what the block size is) output.
*
* - Use dummy syscall functions for users who disable all
* module support. Similar to kernel/sys.c (Paul Gortmaker)
*/
#ifdef CONFIG_MODULES /* a *big* #ifdef block... */
static struct module kernel_module;
static struct module *module_list = &kernel_module;
static int freeing_modules; /* true if some modules are marked for deletion */
static struct module *find_module( const char *name);
static int get_mod_name( char *user_name, char *buf);
static int free_modules( void);
extern struct symbol_table symbol_table; /* in kernel/ksyms.c */
/*
* Called at boot time
*/
void init_modules(void) {
struct internal_symbol *sym;
int i;
for (i = 0, sym = symbol_table.symbol; sym->name; ++sym, ++i)
;
symbol_table.n_symbols = i;
kernel_module.symtab = &symbol_table;
kernel_module.state = MOD_RUNNING; /* Hah! */
kernel_module.name = "";
}
/*
* Allocate space for a module.
*/
asmlinkage unsigned long
sys_create_module(char *module_name, unsigned long size)
{
struct module *mp;
void* addr;
int error;
int npages;
int sspace = sizeof(struct module) + MOD_MAX_NAME;
char name[MOD_MAX_NAME];
if (!suser())
return -EPERM;
if (module_name == NULL || size == 0)
return -EINVAL;
if ((error = get_mod_name(module_name, name)) != 0)
return error;
if (find_module(name) != NULL) {
return -EEXIST;
}
if ((mp = (struct module*) kmalloc(sspace, GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
strcpy((char *)(mp + 1), name); /* why not? */
npages = (size + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE;
if ((addr = vmalloc(npages * PAGE_SIZE)) == 0) {
kfree_s(mp, sspace);
return -ENOMEM;
}
mp->next = module_list;
mp->ref = NULL;
mp->symtab = NULL;
mp->name = (char *)(mp + 1);
mp->size = npages;
mp->addr = addr;
mp->state = MOD_UNINITIALIZED;
mp->cleanup = NULL;
* (long *) addr = 0; /* set use count to zero */
module_list = mp; /* link it in */
pr_debug("module `%s' (%lu pages @ 0x%08lx) created\n",
mp->name, (unsigned long) mp->size, (unsigned long) mp->addr);
return (unsigned long) addr;
}
/*
* Initialize a module.
*/
asmlinkage int
sys_init_module(char *module_name, char *code, unsigned codesize,
struct mod_routines *routines,
struct symbol_table *symtab)
{
struct module *mp;
struct symbol_table *newtab;
char name[MOD_MAX_NAME];
int error;
struct mod_routines rt;
if (!suser())
return -EPERM;
#ifdef __i386__
/* A little bit of protection... we "know" where the user stack is... */
if (symtab && ((unsigned long)symtab > 0xb0000000)) {
printk(KERN_WARNING "warning: you are using an old insmod, no symbols will be inserted!\n");
symtab = NULL;
}
#endif
if ((error = get_mod_name(module_name, name)) != 0)
return error;
pr_debug("initializing module `%s', %d (0x%x) bytes\n",
name, codesize, codesize);
memcpy_fromfs(&rt, routines, sizeof rt);
if ((mp = find_module(name)) == NULL)
return -ENOENT;
if (codesize & MOD_AUTOCLEAN) {
/*
* set autoclean marker from codesize...
* set usage count to "zero"
*/
codesize &= ~MOD_AUTOCLEAN;
GET_USE_COUNT(mp) = MOD_AUTOCLEAN;
}
if ((codesize + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE > mp->size)
return -EINVAL;
memcpy_fromfs((char *)mp->addr + sizeof (long), code, codesize);
memset((char *)mp->addr + sizeof (long) + codesize, 0,
mp->size * PAGE_SIZE - (codesize + sizeof (long)));
pr_debug("module init entry = 0x%08lx, cleanup entry = 0x%08lx\n",
(unsigned long) rt.init, (unsigned long) rt.cleanup);
mp->cleanup = rt.cleanup;
/* update kernel symbol table */
if (symtab) { /* symtab == NULL means no new entries to handle */
struct internal_symbol *sym;
struct module_ref *ref;
int size;
int i;
int legal_start;
if ((error = verify_area(VERIFY_READ, &symtab->size, sizeof(symtab->size))))
return error;
size = get_user(&symtab->size);
if ((newtab = (struct symbol_table*) kmalloc(size, GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
if ((error = verify_area(VERIFY_READ, symtab, size))) {
kfree_s(newtab, size);
return error;
}
memcpy_fromfs((char *)(newtab), symtab, size);
/* sanity check */
legal_start = sizeof(struct symbol_table) +
newtab->n_symbols * sizeof(struct internal_symbol) +
newtab->n_refs * sizeof(struct module_ref);
if ((newtab->n_symbols < 0) || (newtab->n_refs < 0) || (legal_start > size)) {
printk(KERN_WARNING "Rejecting illegal symbol table (n_symbols=%d,n_refs=%d)\n",
newtab->n_symbols, newtab->n_refs);
kfree_s(newtab, size);
return -EINVAL;
}
/* relocate name pointers, index referred from start of table */
for (sym = &(newtab->symbol[0]), i = 0; i < newtab->n_symbols; ++sym, ++i) {
if ((unsigned long)sym->name < legal_start || size <= (unsigned long)sym->name) {
printk(KERN_WARNING "Rejecting illegal symbol table\n");
kfree_s(newtab, size);
return -EINVAL;
}
/* else */
sym->name += (long)newtab;
}
mp->symtab = newtab;
/* Update module references.
* On entry, from "insmod", ref->module points to
* the referenced module!
* Now it will point to the current module instead!
* The ref structure becomes the first link in the linked
* list of references to the referenced module.
* Also, "sym" from above, points to the first ref entry!!!
*/
for (ref = (struct module_ref *)sym, i = 0;
i < newtab->n_refs; ++ref, ++i) {
/* Check for valid reference */
struct module *link = module_list;
while (link && (ref->module != link))
link = link->next;
if (link == (struct module *)0) {
printk(KERN_WARNING "Non-module reference! Rejected!\n");
return -EINVAL;
}
ref->next = ref->module->ref;
ref->module->ref = ref;
ref->module = mp;
}
}
GET_USE_COUNT(mp) += 1;
if ((*rt.init)() != 0) {
GET_USE_COUNT(mp) = 0;
return -EBUSY;
}
GET_USE_COUNT(mp) -= 1;
mp->state = MOD_RUNNING;
return 0;
}
asmlinkage int
sys_delete_module(char *module_name)
{
struct module *mp;
char name[MOD_MAX_NAME];
int error;
if (!suser())
return -EPERM;
/* else */
if (module_name != NULL) {
if ((error = get_mod_name(module_name, name)) != 0)
return error;
if ((mp = find_module(name)) == NULL)
return -ENOENT;
if ((mp->ref != NULL) ||
((GET_USE_COUNT(mp) & ~(MOD_AUTOCLEAN | MOD_VISITED)) != 0))
return -EBUSY;
GET_USE_COUNT(mp) &= ~(MOD_AUTOCLEAN | MOD_VISITED);
if (mp->state == MOD_RUNNING)
(*mp->cleanup)();
mp->state = MOD_DELETED;
free_modules();
}
/* for automatic reaping */
else {
struct module *mp_next;
for (mp = module_list; mp != &kernel_module; mp = mp_next) {
mp_next = mp->next;
if ((mp->ref == NULL) && (mp->state == MOD_RUNNING) &&
((GET_USE_COUNT(mp) & ~MOD_VISITED) == MOD_AUTOCLEAN)) {
if ((GET_USE_COUNT(mp) & MOD_VISITED)) {
/* Don't reap until one "cycle" after last _use_ */
GET_USE_COUNT(mp) &= ~MOD_VISITED;
}
else {
GET_USE_COUNT(mp) &= ~(MOD_AUTOCLEAN | MOD_VISITED);
(*mp->cleanup)();
mp->state = MOD_DELETED;
free_modules();
}
}
}
}
return 0;
}
/*
* Copy the kernel symbol table to user space. If the argument is null,
* just return the size of the table.
*
* Note that the transient module symbols are copied _first_,
* in lifo order!!!
*
* The symbols to "insmod" are according to the "old" format: struct kernel_sym,
* which is actually quite handy for this purpose.
* Note that insmod inserts a struct symbol_table later on...
* (as that format is quite handy for the kernel...)
*
* For every module, the first (pseudo)symbol copied is the module name
* and the address of the module struct.
* This lets "insmod" keep track of references, and build the array of
* struct module_refs in the symbol table.
* The format of the module name is "#module", so that "insmod" can easily
* notice when a module name comes along. Also, this will make it possible
* to use old versions of "insmod", albeit with reduced functionality...
* The "kernel" module has an empty name.
*/
asmlinkage int
sys_get_kernel_syms(struct kernel_sym *table)
{
struct internal_symbol *from;
struct kernel_sym isym;
struct kernel_sym *to;
struct module *mp = module_list;
int i;
int nmodsyms = 0;
for (mp = module_list; mp; mp = mp->next) {
if (mp->symtab && mp->symtab->n_symbols) {
/* include the count for the module name! */
nmodsyms += mp->symtab->n_symbols + 1;
}
else
/* include the count for the module name! */
nmodsyms += 1; /* return modules without symbols too */
}
if (table != NULL) {
to = table;
if ((i = verify_area(VERIFY_WRITE, to, nmodsyms * sizeof(*table))))
return i;
/* copy all module symbols first (always LIFO order) */
for (mp = module_list; mp; mp = mp->next) {
if (mp->state == MOD_RUNNING) {
/* magic: write module info as a pseudo symbol */
isym.value = (unsigned long)mp;
sprintf(isym.name, "#%s", mp->name);
memcpy_tofs(to, &isym, sizeof isym);
++to;
if (mp->symtab != NULL) {
for (i = mp->symtab->n_symbols,
from = mp->symtab->symbol;
i > 0; --i, ++from, ++to) {
isym.value = (unsigned long)from->addr;
strncpy(isym.name, from->name, sizeof isym.name);
memcpy_tofs(to, &isym, sizeof isym);
}
}
}
}
}
return nmodsyms;
}
/*
* Copy the name of a module from user space.
*/
int
get_mod_name(char *user_name, char *buf)
{
int i;
i = 0;
for (i = 0 ; (buf[i] = get_user(user_name + i)) != '\0' ; ) {
if (++i >= MOD_MAX_NAME)
return -E2BIG;
}
return 0;
}
/*
* Look for a module by name, ignoring modules marked for deletion.
*/
struct module *
find_module( const char *name)
{
struct module *mp;
for (mp = module_list ; mp ; mp = mp->next) {
if (mp->state == MOD_DELETED)
continue;
if (!strcmp(mp->name, name))
break;
}
return mp;
}
static void
drop_refs(struct module *mp)
{
struct module *step;
struct module_ref *prev;
struct module_ref *ref;
for (step = module_list; step; step = step->next) {
for (prev = ref = step->ref; ref; ref = prev->next) {
if (ref->module == mp) {
if (ref == step->ref)
step->ref = ref->next;
else
prev->next = ref->next;
break; /* every module only references once! */
}
else
prev = ref;
}
}
}
/*
* Try to free modules which have been marked for deletion. Returns nonzero
* if a module was actually freed.
*/
int
free_modules( void)
{
struct module *mp;
struct module **mpp;
int did_deletion;
did_deletion = 0;
freeing_modules = 0;
mpp = &module_list;
while ((mp = *mpp) != NULL) {
if (mp->state != MOD_DELETED) {
mpp = &mp->next;
} else {
if ((GET_USE_COUNT(mp) != 0) || (mp->ref != NULL)) {
freeing_modules = 1;
mpp = &mp->next;
} else { /* delete it */
*mpp = mp->next;
if (mp->symtab) {
if (mp->symtab->n_refs)
drop_refs(mp);
if (mp->symtab->size)
kfree_s(mp->symtab, mp->symtab->size);
}
vfree(mp->addr);
kfree_s(mp, sizeof(struct module) + MOD_MAX_NAME);
did_deletion = 1;
}
}
}
return did_deletion;
}
/*
* Called by the /proc file system to return a current list of modules.
*/
int get_module_list(char *buf)
{
char *p;
const char *q;
int i;
struct module *mp;
struct module_ref *ref;
char size[32];
p = buf;
/* Do not show the kernel pseudo module */
for (mp = module_list ; mp && mp->next; mp = mp->next) {
if (p - buf > 4096 - 100)
break; /* avoid overflowing buffer */
q = mp->name;
if (*q == '\0' && mp->size == 0 && mp->ref == NULL)
continue; /* don't list modules for kernel syms */
i = 20;
while (*q) {
*p++ = *q++;
i--;
}
sprintf(size, "%d", mp->size);
i -= strlen(size);
if (i <= 0)
i = 1;
while (--i >= 0)
*p++ = ' ';
q = size;
while (*q)
*p++ = *q++;
if (mp->state == MOD_UNINITIALIZED)
q = " (uninitialized)";
else if (mp->state == MOD_RUNNING)
q = "";
else if (mp->state == MOD_DELETED)
q = " (deleted)";
else
q = " (bad state)";
while (*q)
*p++ = *q++;
*p++ = '\t';
if ((ref = mp->ref) != NULL) {
*p++ = '[';
for (; ref; ref = ref->next) {
q = ref->module->name;
while (*q)
*p++ = *q++;
if (ref->next)
*p++ = ' ';
}
*p++ = ']';
}
if (mp->state == MOD_RUNNING) {
sprintf(size,"\t%ld%s",
GET_USE_COUNT(mp) & ~(MOD_AUTOCLEAN | MOD_VISITED),
((GET_USE_COUNT(mp) & MOD_AUTOCLEAN)?
" (autoclean)":""));
q = size;
while (*q)
*p++ = *q++;
}
*p++ = '\n';
}
return p - buf;
}
/*
* Called by the /proc file system to return a current list of ksyms.
*/
int get_ksyms_list(char *buf, char **start, off_t offset, int length)
{
struct module *mp;
struct internal_symbol *sym;
int i;
char *p = buf;
int len = 0; /* code from net/ipv4/proc.c */
off_t pos = 0;
off_t begin = 0;
for (mp = module_list; mp; mp = mp->next) {
if ((mp->state == MOD_RUNNING) &&
(mp->symtab != NULL) &&
(mp->symtab->n_symbols > 0)) {
for (i = mp->symtab->n_symbols,
sym = mp->symtab->symbol;
i > 0; --i, ++sym) {
p = buf + len;
if (mp->name[0]) {
len += sprintf(p, "%08lx %s\t[%s]\n",
(long)sym->addr,
sym->name, mp->name);
} else {
len += sprintf(p, "%08lx %s\n",
(long)sym->addr,
sym->name);
}
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
pos = begin + len;
if (pos > offset+length)
goto leave_the_loop;
}
}
}
leave_the_loop:
*start = buf + (offset - begin);
len -= (offset - begin);
if (len > length)
len = length;
return len;
}
/*
* Rules:
* - The new symbol table should be statically allocated, or else you _have_
* to set the "size" field of the struct to the number of bytes allocated.
*
* - The strings that name the symbols will not be copied, maybe the pointers
*
* - For a loadable module, the function should only be called in the
* context of init_module
*
* Those are the only restrictions! (apart from not being reentrant...)
*
* If you want to remove a symbol table for a loadable module,
* the call looks like: "register_symtab(0)".
*
* The look of the code is mostly dictated by the format of
* the frozen struct symbol_table, due to compatibility demands.
*/
#define INTSIZ sizeof(struct internal_symbol)
#define REFSIZ sizeof(struct module_ref)
#define SYMSIZ sizeof(struct symbol_table)
#define MODSIZ sizeof(struct module)
static struct symbol_table nulltab;
int
register_symtab_from(struct symbol_table *intab, long *from)
{
struct module *mp;
struct module *link;
struct symbol_table *oldtab;
struct symbol_table *newtab;
struct module_ref *newref;
int size;
if (intab && (intab->n_symbols == 0)) {
struct internal_symbol *sym;
/* How many symbols, really? */
for (sym = intab->symbol; sym->name; ++sym)
intab->n_symbols +=1;
}
for (mp = module_list; mp != &kernel_module; mp = mp->next) {
/*
* "from" points to "mod_use_count_" (== start of module)
* or is == 0 if called from a non-module
*/
if ((unsigned long)(mp->addr) == (unsigned long)from)
break;
}
if (mp == &kernel_module) {
/* Aha! Called from an "internal" module */
if (!intab)
return 0; /* or -ESILLY_PROGRAMMER :-) */
/* create a pseudo module! */
if (!(mp = (struct module*) kmalloc(MODSIZ, GFP_KERNEL))) {
/* panic time! */
printk(KERN_ERR "Out of memory for new symbol table!\n");
return -ENOMEM;
}
/* else OK */
memset(mp, 0, MODSIZ);
mp->state = MOD_RUNNING; /* Since it is resident... */
mp->name = ""; /* This is still the "kernel" symbol table! */
mp->symtab = intab;
/* link it in _after_ the resident symbol table */
mp->next = kernel_module.next;
kernel_module.next = mp;
return 0;
}
/* else ******** Called from a loadable module **********/
/*
* This call should _only_ be done in the context of the
* call to init_module i.e. when loading the module!!
* Or else...
*/
/* Any table there before? */
if ((oldtab = mp->symtab) == (struct symbol_table*)0) {
/* No, just insert it! */
mp->symtab = intab;
return 0;
}
/* else ****** we have to replace the module symbol table ******/
if (oldtab->n_refs == 0) { /* no problems! */
mp->symtab = intab;
/* if the old table was kmalloc-ed, drop it */
if (oldtab->size > 0)
kfree_s(oldtab, oldtab->size);
return 0;
}
/* else */
/***** The module references other modules... insmod said so! *****/
/* We have to allocate a new symbol table, or we lose them! */
if (intab == (struct symbol_table*)0)
intab = &nulltab; /* easier code with zeroes in place */
/* the input symbol table space does not include the string table */
/* (it does for symbol tables that insmod creates) */
if (!(newtab = (struct symbol_table*)kmalloc(
size = SYMSIZ + intab->n_symbols * INTSIZ +
oldtab->n_refs * REFSIZ,
GFP_KERNEL))) {
/* panic time! */
printk(KERN_ERR "Out of memory for new symbol table!\n");
return -ENOMEM;
}
/* copy up to, and including, the new symbols */
memcpy(newtab, intab, SYMSIZ + intab->n_symbols * INTSIZ);
newtab->size = size;
newtab->n_refs = oldtab->n_refs;
/* copy references */
memcpy( ((char *)newtab) + SYMSIZ + intab->n_symbols * INTSIZ,
((char *)oldtab) + SYMSIZ + oldtab->n_symbols * INTSIZ,
oldtab->n_refs * REFSIZ);
/* relink references from the old table to the new one */
/* pointer to the first reference entry in newtab! Really! */
newref = (struct module_ref*) &(newtab->symbol[newtab->n_symbols]);
/* check for reference links from previous modules */
for ( link = module_list;
link && (link != &kernel_module);
link = link->next) {
if (link->ref && (link->ref->module == mp))
link->ref = newref++;
}
mp->symtab = newtab;
/* all references (if any) have been handled */
/* if the old table was kmalloc-ed, drop it */
if (oldtab->size > 0)
kfree_s(oldtab, oldtab->size);
return 0;
}
#else /* CONFIG_MODULES */
/* Dummy syscalls for people who don't want modules */
asmlinkage unsigned long sys_create_module(void)
{
return -ENOSYS;
}
asmlinkage int sys_init_module(void)
{
return -ENOSYS;
}
asmlinkage int sys_delete_module(void)
{
return -ENOSYS;
}
asmlinkage int sys_get_kernel_syms(void)
{
return -ENOSYS;
}
int register_symtab_from(struct symbol_table *intab, long *from)
{
return 0;
}
#endif /* CONFIG_MODULES */