2005-06-24 09:01:16 +04:00
/*
2006-10-04 01:01:26 +04:00
* arch / xtensa / kernel / module . c
2005-06-24 09:01:16 +04:00
*
* Module support .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2007-08-15 00:02:06 +04:00
* Copyright ( C ) 2001 - 2006 Tensilica Inc .
2005-06-24 09:01:16 +04:00
*
* Chris Zankel < chris @ zankel . net >
*
*/
# include <linux/module.h>
# 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/cache.h>
2007-08-15 00:02:06 +04:00
static int
decode_calln_opcode ( unsigned char * location )
{
# ifdef __XTENSA_EB__
return ( location [ 0 ] & 0xf0 ) = = 0x50 ;
# endif
# ifdef __XTENSA_EL__
return ( location [ 0 ] & 0xf ) = = 0x5 ;
# endif
}
static int
decode_l32r_opcode ( unsigned char * location )
{
# ifdef __XTENSA_EB__
return ( location [ 0 ] & 0xf0 ) = = 0x10 ;
# endif
# ifdef __XTENSA_EL__
return ( location [ 0 ] & 0xf ) = = 0x1 ;
# endif
2005-06-24 09:01:16 +04:00
}
int apply_relocate_add ( Elf32_Shdr * sechdrs ,
const char * strtab ,
unsigned int symindex ,
unsigned int relsec ,
2007-08-15 00:02:06 +04:00
struct module * mod )
2005-06-24 09:01:16 +04:00
{
2007-08-15 00:02:06 +04:00
unsigned int i ;
2012-11-29 04:53:51 +04:00
Elf32_Rela * rela = ( void * ) sechdrs [ relsec ] . sh_addr ;
2007-08-15 00:02:06 +04:00
Elf32_Sym * sym ;
unsigned char * location ;
uint32_t value ;
2017-12-15 23:00:30 +03:00
pr_debug ( " Applying relocate section %u to %u \n " , relsec ,
sechdrs [ relsec ] . sh_info ) ;
2007-08-15 00:02:06 +04:00
for ( i = 0 ; i < sechdrs [ relsec ] . sh_size / sizeof ( * rela ) ; i + + ) {
location = ( char * ) sechdrs [ sechdrs [ relsec ] . sh_info ] . sh_addr
+ rela [ i ] . r_offset ;
sym = ( Elf32_Sym * ) sechdrs [ symindex ] . sh_addr
+ ELF32_R_SYM ( rela [ i ] . r_info ) ;
value = sym - > st_value + rela [ i ] . r_addend ;
switch ( ELF32_R_TYPE ( rela [ i ] . r_info ) ) {
case R_XTENSA_NONE :
case R_XTENSA_DIFF8 :
case R_XTENSA_DIFF16 :
case R_XTENSA_DIFF32 :
case R_XTENSA_ASM_EXPAND :
break ;
case R_XTENSA_32 :
case R_XTENSA_PLT :
* ( uint32_t * ) location + = value ;
break ;
case R_XTENSA_SLOT0_OP :
if ( decode_calln_opcode ( location ) ) {
value - = ( ( unsigned long ) location & - 4 ) + 4 ;
if ( ( value & 3 ) ! = 0 | |
( ( value + ( 1 < < 19 ) ) > > 20 ) ! = 0 ) {
2017-12-15 23:00:30 +03:00
pr_err ( " %s: relocation out of range, "
2007-08-15 00:02:06 +04:00
" section %d reloc %d "
" sym '%s' \n " ,
mod - > name , relsec , i ,
strtab + sym - > st_name ) ;
return - ENOEXEC ;
}
value = ( signed int ) value > > 2 ;
# ifdef __XTENSA_EB__
location [ 0 ] = ( ( location [ 0 ] & ~ 0x3 ) |
( ( value > > 16 ) & 0x3 ) ) ;
location [ 1 ] = ( value > > 8 ) & 0xff ;
location [ 2 ] = value & 0xff ;
# endif
# ifdef __XTENSA_EL__
location [ 0 ] = ( ( location [ 0 ] & ~ 0xc0 ) |
( ( value < < 6 ) & 0xc0 ) ) ;
location [ 1 ] = ( value > > 2 ) & 0xff ;
location [ 2 ] = ( value > > 10 ) & 0xff ;
# endif
} else if ( decode_l32r_opcode ( location ) ) {
value - = ( ( ( unsigned long ) location + 3 ) & - 4 ) ;
if ( ( value & 3 ) ! = 0 | |
( signed int ) value > > 18 ! = - 1 ) {
2017-12-15 23:00:30 +03:00
pr_err ( " %s: relocation out of range, "
2007-08-15 00:02:06 +04:00
" section %d reloc %d "
" sym '%s' \n " ,
mod - > name , relsec , i ,
strtab + sym - > st_name ) ;
return - ENOEXEC ;
}
value = ( signed int ) value > > 2 ;
# ifdef __XTENSA_EB__
location [ 1 ] = ( value > > 8 ) & 0xff ;
location [ 2 ] = value & 0xff ;
# endif
# ifdef __XTENSA_EL__
location [ 1 ] = value & 0xff ;
location [ 2 ] = ( value > > 8 ) & 0xff ;
# endif
}
/* FIXME: Ignore any other opcodes. The Xtensa
assembler currently assumes that the linker will
always do relaxation and so all PC - relative
operands need relocations . ( The assembler also
writes out the tentative PC - relative values ,
assuming no link - time relaxation , so it is usually
safe to ignore the relocations . ) If the
assembler ' s " --no-link-relax " flag can be made to
work , and if all kernel modules can be assembled
with that flag , then unexpected relocations could
be detected here . */
break ;
case R_XTENSA_SLOT1_OP :
case R_XTENSA_SLOT2_OP :
case R_XTENSA_SLOT3_OP :
case R_XTENSA_SLOT4_OP :
case R_XTENSA_SLOT5_OP :
case R_XTENSA_SLOT6_OP :
case R_XTENSA_SLOT7_OP :
case R_XTENSA_SLOT8_OP :
case R_XTENSA_SLOT9_OP :
case R_XTENSA_SLOT10_OP :
case R_XTENSA_SLOT11_OP :
case R_XTENSA_SLOT12_OP :
case R_XTENSA_SLOT13_OP :
case R_XTENSA_SLOT14_OP :
2017-12-15 23:00:30 +03:00
pr_err ( " %s: unexpected FLIX relocation: %u \n " ,
2007-08-15 00:02:06 +04:00
mod - > name ,
ELF32_R_TYPE ( rela [ i ] . r_info ) ) ;
return - ENOEXEC ;
case R_XTENSA_SLOT0_ALT :
case R_XTENSA_SLOT1_ALT :
case R_XTENSA_SLOT2_ALT :
case R_XTENSA_SLOT3_ALT :
case R_XTENSA_SLOT4_ALT :
case R_XTENSA_SLOT5_ALT :
case R_XTENSA_SLOT6_ALT :
case R_XTENSA_SLOT7_ALT :
case R_XTENSA_SLOT8_ALT :
case R_XTENSA_SLOT9_ALT :
case R_XTENSA_SLOT10_ALT :
case R_XTENSA_SLOT11_ALT :
case R_XTENSA_SLOT12_ALT :
case R_XTENSA_SLOT13_ALT :
case R_XTENSA_SLOT14_ALT :
2017-12-15 23:00:30 +03:00
pr_err ( " %s: unexpected ALT relocation: %u \n " ,
2007-08-15 00:02:06 +04:00
mod - > name ,
ELF32_R_TYPE ( rela [ i ] . r_info ) ) ;
return - ENOEXEC ;
default :
2017-12-15 23:00:30 +03:00
pr_err ( " %s: unexpected relocation: %u \n " ,
2007-08-15 00:02:06 +04:00
mod - > name ,
ELF32_R_TYPE ( rela [ i ] . r_info ) ) ;
return - ENOEXEC ;
}
}
return 0 ;
2005-06-24 09:01:16 +04:00
}