2005-04-16 15:20:36 -07:00
/* Kernel module help for Alpha.
Copyright ( C ) 2002 Richard Henderson .
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>
# include <linux/slab.h>
#if 0
# define DEBUGP printk
# else
# define DEBUGP(fmt...)
# endif
void *
module_alloc ( unsigned long size )
{
if ( size = = 0 )
return NULL ;
return vmalloc ( size ) ;
}
void
module_free ( struct module * mod , void * module_region )
{
vfree ( module_region ) ;
}
/* Allocate the GOT at the end of the core sections. */
struct got_entry {
struct got_entry * next ;
2005-09-09 13:01:54 -07:00
Elf64_Sxword r_addend ;
2005-04-16 15:20:36 -07:00
int got_offset ;
} ;
static inline void
process_reloc_for_got ( Elf64_Rela * rela ,
struct got_entry * chains , Elf64_Xword * poffset )
{
unsigned long r_sym = ELF64_R_SYM ( rela - > r_info ) ;
unsigned long r_type = ELF64_R_TYPE ( rela - > r_info ) ;
2005-09-09 13:01:54 -07:00
Elf64_Sxword r_addend = rela - > r_addend ;
2005-04-16 15:20:36 -07:00
struct got_entry * g ;
if ( r_type ! = R_ALPHA_LITERAL )
return ;
for ( g = chains + r_sym ; g ; g = g - > next )
2005-09-09 13:01:54 -07:00
if ( g - > r_addend = = r_addend ) {
2005-04-16 15:20:36 -07:00
if ( g - > got_offset = = 0 ) {
g - > got_offset = * poffset ;
* poffset + = 8 ;
}
goto found_entry ;
}
g = kmalloc ( sizeof ( * g ) , GFP_KERNEL ) ;
g - > next = chains [ r_sym ] . next ;
2005-09-09 13:01:54 -07:00
g - > r_addend = r_addend ;
2005-04-16 15:20:36 -07:00
g - > got_offset = * poffset ;
* poffset + = 8 ;
chains [ r_sym ] . next = g ;
found_entry :
/* Trick: most of the ELF64_R_TYPE field is unused. There are
42 valid relocation types , and a 32 - bit field . Co - opt the
bits above 256 to store the got offset for this reloc . */
rela - > r_info | = g - > got_offset < < 8 ;
}
int
module_frob_arch_sections ( Elf64_Ehdr * hdr , Elf64_Shdr * sechdrs ,
char * secstrings , struct module * me )
{
struct got_entry * chains ;
Elf64_Rela * rela ;
Elf64_Shdr * esechdrs , * symtab , * s , * got ;
unsigned long nsyms , nrela , i ;
esechdrs = sechdrs + hdr - > e_shnum ;
symtab = got = NULL ;
/* Find out how large the symbol table is. Allocate one got_entry
head per symbol . Normally this will be enough , but not always .
We ' ll chain different offsets for the symbol down each head . */
for ( s = sechdrs ; s < esechdrs ; + + s )
if ( s - > sh_type = = SHT_SYMTAB )
symtab = s ;
else if ( ! strcmp ( " .got " , secstrings + s - > sh_name ) ) {
got = s ;
me - > arch . gotsecindex = s - sechdrs ;
}
if ( ! symtab ) {
printk ( KERN_ERR " module %s: no symbol table \n " , me - > name ) ;
return - ENOEXEC ;
}
if ( ! got ) {
printk ( KERN_ERR " module %s: no got section \n " , me - > name ) ;
return - ENOEXEC ;
}
nsyms = symtab - > sh_size / sizeof ( Elf64_Sym ) ;
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 01:49:03 -07:00
chains = kcalloc ( nsyms , sizeof ( struct got_entry ) , GFP_KERNEL ) ;
2008-04-28 02:13:44 -07:00
if ( ! chains ) {
printk ( KERN_ERR
" module %s: no memory for symbol chain buffer \n " ,
me - > name ) ;
return - ENOMEM ;
}
2005-04-16 15:20:36 -07:00
got - > sh_size = 0 ;
got - > sh_addralign = 8 ;
got - > sh_type = SHT_NOBITS ;
/* Examine all LITERAL relocations to find out what GOT entries
are required . This sizes the GOT section as well . */
for ( s = sechdrs ; s < esechdrs ; + + s )
if ( s - > sh_type = = SHT_RELA ) {
nrela = s - > sh_size / sizeof ( Elf64_Rela ) ;
rela = ( void * ) hdr + s - > sh_offset ;
for ( i = 0 ; i < nrela ; + + i )
process_reloc_for_got ( rela + i , chains ,
& got - > sh_size ) ;
}
/* Free the memory we allocated. */
for ( i = 0 ; i < nsyms ; + + i ) {
struct got_entry * g , * n ;
for ( g = chains [ i ] . next ; g ; g = n ) {
n = g - > next ;
kfree ( g ) ;
}
}
kfree ( chains ) ;
return 0 ;
}
int
apply_relocate ( Elf64_Shdr * sechdrs , const char * strtab , unsigned int symindex ,
unsigned int relsec , struct module * me )
{
printk ( KERN_ERR " module %s: REL relocation unsupported \n " , me - > name ) ;
return - ENOEXEC ;
}
int
apply_relocate_add ( Elf64_Shdr * sechdrs , const char * strtab ,
unsigned int symindex , unsigned int relsec ,
struct module * me )
{
Elf64_Rela * rela = ( void * ) sechdrs [ relsec ] . sh_addr ;
unsigned long i , n = sechdrs [ relsec ] . sh_size / sizeof ( * rela ) ;
Elf64_Sym * symtab , * sym ;
void * base , * location ;
unsigned long got , gp ;
DEBUGP ( " Applying relocate section %u to %u \n " , relsec ,
sechdrs [ relsec ] . sh_info ) ;
base = ( void * ) sechdrs [ sechdrs [ relsec ] . sh_info ] . sh_addr ;
symtab = ( Elf64_Sym * ) sechdrs [ symindex ] . sh_addr ;
/* The small sections were sorted to the end of the segment.
The following should definitely cover them . */
gp = ( u64 ) me - > module_core + me - > core_size - 0x8000 ;
got = sechdrs [ me - > arch . gotsecindex ] . sh_addr ;
for ( i = 0 ; i < n ; i + + ) {
unsigned long r_sym = ELF64_R_SYM ( rela [ i ] . r_info ) ;
unsigned long r_type = ELF64_R_TYPE ( rela [ i ] . r_info ) ;
unsigned long r_got_offset = r_type > > 8 ;
unsigned long value , hi , lo ;
r_type & = 0xff ;
/* This is where to make the change. */
location = base + rela [ i ] . r_offset ;
/* This is the symbol it is referring to. Note that all
unresolved symbols have been resolved . */
sym = symtab + r_sym ;
value = sym - > st_value + rela [ i ] . r_addend ;
switch ( r_type ) {
case R_ALPHA_NONE :
break ;
case R_ALPHA_REFQUAD :
/* BUG() can produce misaligned relocations. */
( ( u32 * ) location ) [ 0 ] = value ;
( ( u32 * ) location ) [ 1 ] = value > > 32 ;
break ;
case R_ALPHA_GPREL32 :
value - = gp ;
if ( ( int ) value ! = value )
goto reloc_overflow ;
* ( u32 * ) location = value ;
break ;
case R_ALPHA_LITERAL :
hi = got + r_got_offset ;
lo = hi - gp ;
if ( ( short ) lo ! = lo )
goto reloc_overflow ;
* ( u16 * ) location = lo ;
* ( u64 * ) hi = value ;
break ;
case R_ALPHA_LITUSE :
break ;
case R_ALPHA_GPDISP :
value = gp - ( u64 ) location ;
lo = ( short ) value ;
hi = ( int ) ( value - lo ) ;
if ( hi + lo ! = value )
goto reloc_overflow ;
* ( u16 * ) location = hi > > 16 ;
* ( u16 * ) ( location + rela [ i ] . r_addend ) = lo ;
break ;
case R_ALPHA_BRSGP :
/* BRSGP is only allowed to bind to local symbols.
If the section is undef , this means that the
value was resolved from somewhere else . */
if ( sym - > st_shndx = = SHN_UNDEF )
goto reloc_overflow ;
if ( ( sym - > st_other & STO_ALPHA_STD_GPLOAD ) = =
STO_ALPHA_STD_GPLOAD )
/* Omit the prologue. */
value + = 8 ;
/* FALLTHRU */
case R_ALPHA_BRADDR :
value - = ( u64 ) location + 4 ;
if ( value & 3 )
goto reloc_overflow ;
value = ( long ) value > > 2 ;
if ( value + ( 1 < < 21 ) > = 1 < < 22 )
goto reloc_overflow ;
value & = 0x1fffff ;
value | = * ( u32 * ) location & ~ 0x1fffff ;
* ( u32 * ) location = value ;
break ;
case R_ALPHA_HINT :
break ;
case R_ALPHA_SREL32 :
value - = ( u64 ) location ;
if ( ( int ) value ! = value )
goto reloc_overflow ;
* ( u32 * ) location = value ;
break ;
case R_ALPHA_SREL64 :
value - = ( u64 ) location ;
* ( u64 * ) location = value ;
break ;
case R_ALPHA_GPRELHIGH :
value = ( long ) ( value - gp + 0x8000 ) > > 16 ;
if ( ( short ) value ! = value )
goto reloc_overflow ;
* ( u16 * ) location = value ;
break ;
case R_ALPHA_GPRELLOW :
value - = gp ;
* ( u16 * ) location = value ;
break ;
case R_ALPHA_GPREL16 :
value - = gp ;
if ( ( short ) value ! = value )
goto reloc_overflow ;
* ( u16 * ) location = value ;
break ;
default :
printk ( KERN_ERR " module %s: Unknown relocation: %lu \n " ,
me - > name , r_type ) ;
return - ENOEXEC ;
reloc_overflow :
if ( ELF64_ST_TYPE ( sym - > st_info ) = = STT_SECTION )
printk ( KERN_ERR
2007-04-16 22:53:17 -07:00
" module %s: Relocation (type %lu) overflow vs section %d \n " ,
me - > name , r_type , sym - > st_shndx ) ;
2005-04-16 15:20:36 -07:00
else
printk ( KERN_ERR
2007-04-16 22:53:17 -07:00
" module %s: Relocation (type %lu) overflow vs %s \n " ,
me - > name , r_type , strtab + sym - > st_name ) ;
2005-04-16 15:20:36 -07:00
return - ENOEXEC ;
}
}
return 0 ;
}
int
module_finalize ( const Elf_Ehdr * hdr , const Elf_Shdr * sechdrs ,
struct module * me )
{
return 0 ;
}
void
module_arch_cleanup ( struct module * mod )
{
}