2005-04-16 15:20:36 -07:00
/* Kernel module help for i386.
Copyright ( C ) 2001 Rusty Russell .
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
*/
# include <linux/moduleloader.h>
# include <linux/elf.h>
# include <linux/vmalloc.h>
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/kernel.h>
2006-12-08 02:36:21 -08:00
# include <linux/bug.h>
2005-04-16 15:20:36 -07:00
#if 0
# define DEBUGP printk
# else
# define DEBUGP(fmt...)
# endif
void * module_alloc ( unsigned long size )
{
if ( size = = 0 )
return NULL ;
return vmalloc_exec ( size ) ;
}
/* Free memory returned from module_alloc */
void module_free ( struct module * mod , void * module_region )
{
vfree ( module_region ) ;
/* FIXME: If module_region == mod->init_region, trim exception
table entries . */
}
/* We don't need anything special. */
int module_frob_arch_sections ( Elf_Ehdr * hdr ,
Elf_Shdr * sechdrs ,
char * secstrings ,
struct module * mod )
{
return 0 ;
}
int apply_relocate ( Elf32_Shdr * sechdrs ,
const char * strtab ,
unsigned int symindex ,
unsigned int relsec ,
struct module * me )
{
unsigned int i ;
Elf32_Rel * rel = ( void * ) sechdrs [ relsec ] . sh_addr ;
Elf32_Sym * sym ;
uint32_t * location ;
DEBUGP ( " Applying relocate section %u to %u \n " , relsec ,
sechdrs [ relsec ] . sh_info ) ;
for ( i = 0 ; i < sechdrs [ relsec ] . sh_size / sizeof ( * rel ) ; i + + ) {
/* This is where to make the change */
location = ( void * ) sechdrs [ sechdrs [ relsec ] . sh_info ] . sh_addr
+ rel [ i ] . r_offset ;
/* This is the symbol it is referring to. Note that all
undefined symbols have been resolved . */
sym = ( Elf32_Sym * ) sechdrs [ symindex ] . sh_addr
+ ELF32_R_SYM ( rel [ i ] . r_info ) ;
switch ( ELF32_R_TYPE ( rel [ i ] . r_info ) ) {
case R_386_32 :
/* We add the value into the location given */
* location + = sym - > st_value ;
break ;
case R_386_PC32 :
/* Add the value, subtract its postition */
* location + = sym - > st_value - ( uint32_t ) location ;
break ;
default :
printk ( KERN_ERR " module %s: Unknown relocation: %u \n " ,
me - > name , ELF32_R_TYPE ( rel [ i ] . r_info ) ) ;
return - ENOEXEC ;
}
}
return 0 ;
}
int apply_relocate_add ( Elf32_Shdr * sechdrs ,
const char * strtab ,
unsigned int symindex ,
unsigned int relsec ,
struct module * me )
{
printk ( KERN_ERR " module %s: ADD RELOCATION unsupported \n " ,
me - > name ) ;
return - ENOEXEC ;
}
int module_finalize ( const Elf_Ehdr * hdr ,
const Elf_Shdr * sechdrs ,
struct module * me )
{
2006-12-07 02:14:08 +01:00
const Elf_Shdr * s , * text = NULL , * alt = NULL , * locks = NULL ,
* para = NULL ;
2005-04-16 15:20:36 -07:00
char * secstrings = ( void * ) hdr + sechdrs [ hdr - > e_shstrndx ] . sh_offset ;
for ( s = sechdrs ; s < sechdrs + hdr - > e_shnum ; s + + ) {
2006-03-23 02:59:32 -08:00
if ( ! strcmp ( " .text " , secstrings + s - > sh_name ) )
text = s ;
if ( ! strcmp ( " .altinstructions " , secstrings + s - > sh_name ) )
alt = s ;
if ( ! strcmp ( " .smp_locks " , secstrings + s - > sh_name ) )
locks = s ;
2006-12-07 02:14:08 +01:00
if ( ! strcmp ( " .parainstructions " , secstrings + s - > sh_name ) )
para = s ;
2006-03-23 02:59:32 -08:00
}
if ( alt ) {
/* patch .altinstructions */
void * aseg = ( void * ) alt - > sh_addr ;
apply_alternatives ( aseg , aseg + alt - > sh_size ) ;
}
if ( locks & & text ) {
void * lseg = ( void * ) locks - > sh_addr ;
void * tseg = ( void * ) text - > sh_addr ;
alternatives_smp_module_add ( me , me - > name ,
lseg , lseg + locks - > sh_size ,
tseg , tseg + text - > sh_size ) ;
}
2006-12-07 02:14:08 +01:00
if ( para ) {
void * pseg = ( void * ) para - > sh_addr ;
apply_paravirt ( pseg , pseg + para - > sh_size ) ;
}
2006-12-08 02:36:21 -08:00
return module_bug_finalize ( hdr , sechdrs , me ) ;
2005-04-16 15:20:36 -07:00
}
void module_arch_cleanup ( struct module * mod )
{
2006-03-23 02:59:32 -08:00
alternatives_smp_module_del ( mod ) ;
2006-12-08 02:36:21 -08:00
module_bug_cleanup ( mod ) ;
2005-04-16 15:20:36 -07:00
}