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