2011-11-01 03:40:46 +04:00
/*
* Kernel module loader for Hexagon
*
2012-09-20 01:22:02 +04:00
* Copyright ( c ) 2010 - 2011 , The Linux Foundation . All rights reserved .
2011-11-01 03:40:46 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
*/
# include <asm/module.h>
# include <linux/elf.h>
# include <linux/module.h>
# include <linux/moduleloader.h>
# include <linux/vmalloc.h>
#if 0
# define DEBUGP printk
# else
# define DEBUGP(fmt , ...)
# endif
/*
* module_frob_arch_sections - tweak got / plt sections .
* @ hdr - pointer to elf header
* @ sechdrs - pointer to elf load section headers
* @ secstrings - symbol names
* @ mod - pointer to module
*/
int module_frob_arch_sections ( Elf_Ehdr * hdr , Elf_Shdr * sechdrs ,
char * secstrings ,
struct module * mod )
{
unsigned int i ;
int found = 0 ;
/* Look for .plt and/or .got.plt and/or .init.plt sections */
for ( i = 0 ; i < hdr - > e_shnum ; i + + ) {
DEBUGP ( " Section %d is %s \n " , i ,
secstrings + sechdrs [ i ] . sh_name ) ;
if ( strcmp ( secstrings + sechdrs [ i ] . sh_name , " .plt " ) = = 0 )
found = i + 1 ;
if ( strcmp ( secstrings + sechdrs [ i ] . sh_name , " .got.plt " ) = = 0 )
found = i + 1 ;
if ( strcmp ( secstrings + sechdrs [ i ] . sh_name , " .rela.plt " ) = = 0 )
found = i + 1 ;
}
/* At this time, we don't support modules comiled with -shared */
if ( found ) {
printk ( KERN_WARNING
" Module '%s' contains unexpected .plt/.got sections. \n " ,
mod - > name ) ;
/* return -ENOEXEC; */
}
return 0 ;
}
/*
* apply_relocate_add - perform rela relocations .
* @ sechdrs - pointer to section headers
* @ strtab - some sort of start address ?
* @ symindex - symbol index offset or something ?
* @ relsec - address to relocate to ?
* @ module - pointer to module
*
* Perform rela relocations .
*/
int apply_relocate_add ( Elf_Shdr * sechdrs , const char * strtab ,
unsigned int symindex , unsigned int relsec ,
struct module * module )
{
unsigned int i ;
Elf32_Sym * sym ;
uint32_t * location ;
uint32_t value ;
unsigned int nrelocs = sechdrs [ relsec ] . sh_size / sizeof ( Elf32_Rela ) ;
Elf32_Rela * rela = ( void * ) sechdrs [ relsec ] . sh_addr ;
Elf32_Word sym_info = sechdrs [ relsec ] . sh_info ;
Elf32_Sym * sym_base = ( Elf32_Sym * ) sechdrs [ symindex ] . sh_addr ;
void * loc_base = ( void * ) sechdrs [ sym_info ] . sh_addr ;
DEBUGP ( " Applying relocations in section %u to section %u base=%p \n " ,
relsec , sym_info , loc_base ) ;
for ( i = 0 ; i < nrelocs ; i + + ) {
/* Symbol to relocate */
sym = sym_base + ELF32_R_SYM ( rela [ i ] . r_info ) ;
/* Where to make the change */
location = loc_base + rela [ i ] . r_offset ;
/* `Everything is relative'. */
value = sym - > st_value + rela [ i ] . r_addend ;
DEBUGP ( " %d: value=%08x loc=%p reloc=%d symbol=%s \n " ,
i , value , location , ELF32_R_TYPE ( rela [ i ] . r_info ) ,
sym - > st_name ?
& strtab [ sym - > st_name ] : " (anonymous) " ) ;
switch ( ELF32_R_TYPE ( rela [ i ] . r_info ) ) {
case R_HEXAGON_B22_PCREL : {
int dist = ( int ) ( value - ( uint32_t ) location ) ;
if ( ( dist < - 0x00800000 ) | |
( dist > = 0x00800000 ) ) {
printk ( KERN_ERR
" %s: %s: %08x=%08x-%08x %s \n " ,
module - > name ,
" R_HEXAGON_B22_PCREL reloc out of range " ,
dist , value , ( uint32_t ) location ,
sym - > st_name ?
& strtab [ sym - > st_name ] : " (anonymous) " ) ;
return - ENOEXEC ;
}
DEBUGP ( " B22_PCREL contents: %08X. \n " , * location ) ;
* location & = ~ 0x01ff3fff ;
* location | = 0x00003fff & dist ;
* location | = 0x01ff0000 & ( dist < < 2 ) ;
DEBUGP ( " Contents after reloc: %08x \n " , * location ) ;
break ;
}
case R_HEXAGON_HI16 :
value = ( value > > 16 ) & 0xffff ;
/* fallthrough */
case R_HEXAGON_LO16 :
* location & = ~ 0x00c03fff ;
* location | = value & 0x3fff ;
* location | = ( value & 0xc000 ) < < 8 ;
break ;
case R_HEXAGON_32 :
* location = value ;
break ;
case R_HEXAGON_32_PCREL :
* location = value - ( uint32_t ) location ;
break ;
case R_HEXAGON_PLT_B22_PCREL :
case R_HEXAGON_GOTOFF_LO16 :
case R_HEXAGON_GOTOFF_HI16 :
printk ( KERN_ERR " %s: GOT/PLT relocations unsupported \n " ,
module - > name ) ;
return - ENOEXEC ;
default :
printk ( KERN_ERR " %s: unknown relocation: %u \n " ,
module - > name ,
ELF32_R_TYPE ( rela [ i ] . r_info ) ) ;
return - ENOEXEC ;
}
}
return 0 ;
}