2013-01-29 17:58:05 +04:00
/*
* Copyright ( C ) 2004 , 2007 - 2010 , 2011 - 2012 Synopsys , Inc . ( www . synopsys . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/moduleloader.h>
# include <linux/kernel.h>
# include <linux/elf.h>
# include <linux/vmalloc.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/string.h>
2013-01-22 15:33:19 +04:00
# include <asm/unwind.h>
2013-01-29 17:58:05 +04:00
static inline void arc_write_me ( unsigned short * addr , unsigned long value )
{
* addr = ( value & 0xffff0000 ) > > 16 ;
* ( addr + 1 ) = ( value & 0xffff ) ;
}
2013-06-24 19:52:06 +04:00
/*
* This gets called before relocation loop in generic loader
* Make a note of the section index of unwinding section
2013-01-22 15:33:19 +04:00
*/
int module_frob_arch_sections ( Elf_Ehdr * hdr , Elf_Shdr * sechdrs ,
char * secstr , struct module * mod )
{
# ifdef CONFIG_ARC_DW2_UNWIND
mod - > arch . unw_sec_idx = 0 ;
mod - > arch . unw_info = NULL ;
# endif
2017-01-16 21:48:09 +03:00
mod - > arch . secstr = secstr ;
2013-03-06 15:23:45 +04:00
return 0 ;
2013-01-22 15:33:19 +04:00
}
void module_arch_cleanup ( struct module * mod )
{
# ifdef CONFIG_ARC_DW2_UNWIND
if ( mod - > arch . unw_info )
unwind_remove_table ( mod - > arch . unw_info , 0 ) ;
# endif
}
2013-01-29 17:58:05 +04:00
int apply_relocate_add ( Elf32_Shdr * sechdrs ,
const char * strtab ,
unsigned int symindex , /* sec index for sym tbl */
unsigned int relsec , /* sec index for relo sec */
struct module * module )
{
2016-10-25 21:23:19 +03:00
int i , n , relo_type ;
2013-01-29 17:58:05 +04:00
Elf32_Rela * rel_entry = ( void * ) sechdrs [ relsec ] . sh_addr ;
Elf32_Sym * sym_entry , * sym_sec ;
2016-10-25 21:23:19 +03:00
Elf32_Addr relocation , location , tgt_addr ;
2016-10-25 20:43:20 +03:00
unsigned int tgtsec ;
2013-01-29 17:58:05 +04:00
2016-10-25 21:23:19 +03:00
/*
* @ relsec has relocations e . g . . rela . init . text
* @ tgtsec is section to patch e . g . . init . text
*/
2016-10-25 20:43:20 +03:00
tgtsec = sechdrs [ relsec ] . sh_info ;
2016-10-25 21:23:19 +03:00
tgt_addr = sechdrs [ tgtsec ] . sh_addr ;
2013-01-29 17:58:05 +04:00
sym_sec = ( Elf32_Sym * ) sechdrs [ symindex ] . sh_addr ;
n = sechdrs [ relsec ] . sh_size / sizeof ( * rel_entry ) ;
2016-10-25 21:23:19 +03:00
pr_debug ( " \n Section to fixup %s @%x \n " ,
module - > arch . secstr + sechdrs [ tgtsec ] . sh_name , tgt_addr ) ;
2013-01-29 17:58:05 +04:00
pr_debug ( " ========================================================= \n " ) ;
2016-10-25 21:23:19 +03:00
pr_debug ( " r_off \t r_add \t st_value ADDRESS VALUE \n " ) ;
2013-01-29 17:58:05 +04:00
pr_debug ( " ========================================================= \n " ) ;
/* Loop thru entries in relocation section */
for ( i = 0 ; i < n ; i + + ) {
2016-10-25 21:23:19 +03:00
const char * s ;
2013-01-29 17:58:05 +04:00
/* This is where to make the change */
2016-10-25 21:23:19 +03:00
location = tgt_addr + rel_entry [ i ] . r_offset ;
2013-01-29 17:58:05 +04:00
/* This is the symbol it is referring to. Note that all
undefined symbols have been resolved . */
sym_entry = sym_sec + ELF32_R_SYM ( rel_entry [ i ] . r_info ) ;
relocation = sym_entry - > st_value + rel_entry [ i ] . r_addend ;
2016-10-25 21:23:19 +03:00
if ( sym_entry - > st_name = = 0 & & ELF_ST_TYPE ( sym_entry - > st_info ) = = STT_SECTION ) {
s = module - > arch . secstr + sechdrs [ sym_entry - > st_shndx ] . sh_name ;
} else {
s = strtab + sym_entry - > st_name ;
}
pr_debug ( " %x \t %x \t %x %x %x [%s] \n " ,
rel_entry [ i ] . r_offset , rel_entry [ i ] . r_addend ,
sym_entry - > st_value , location , relocation , s ) ;
2013-01-29 17:58:05 +04:00
/* This assumes modules are built with -mlong-calls
* so any branches / jumps are absolute 32 bit jmps
* global data access again is abs 32 bit .
* Both of these are handled by same relocation type
*/
relo_type = ELF32_R_TYPE ( rel_entry [ i ] . r_info ) ;
2016-09-13 02:50:50 +03:00
if ( likely ( R_ARC_32_ME = = relo_type ) ) /* ME ( S + A ) */
2013-01-29 17:58:05 +04:00
arc_write_me ( ( unsigned short * ) location , relocation ) ;
2016-09-13 02:50:50 +03:00
else if ( R_ARC_32 = = relo_type ) /* ( S + A ) */
2013-01-29 17:58:05 +04:00
* ( ( Elf32_Addr * ) location ) = relocation ;
2016-09-13 02:50:50 +03:00
else if ( R_ARC_32_PCREL = = relo_type ) /* ( S + A ) - PDATA ) */
* ( ( Elf32_Addr * ) location ) = relocation - location ;
2013-01-29 17:58:05 +04:00
else
goto relo_err ;
}
2016-10-25 20:43:20 +03:00
2017-01-16 21:48:09 +03:00
# ifdef CONFIG_ARC_DW2_UNWIND
2016-10-25 20:43:20 +03:00
if ( strcmp ( module - > arch . secstr + sechdrs [ tgtsec ] . sh_name , " .eh_frame " ) = = 0 )
module - > arch . unw_sec_idx = tgtsec ;
2017-01-16 21:48:09 +03:00
# endif
2016-10-25 20:43:20 +03:00
2013-01-29 17:58:05 +04:00
return 0 ;
relo_err :
pr_err ( " %s: unknown relocation: %u \n " ,
module - > name , ELF32_R_TYPE ( rel_entry [ i ] . r_info ) ) ;
return - ENOEXEC ;
}
2013-01-22 15:33:19 +04:00
/* Just before lift off: After sections have been relocated, we add the
* dwarf section to unwinder table pool
* This couldn ' t be done in module_frob_arch_sections ( ) because
* relocations had not been applied by then
*/
int module_finalize ( const Elf32_Ehdr * hdr , const Elf_Shdr * sechdrs ,
struct module * mod )
{
# ifdef CONFIG_ARC_DW2_UNWIND
void * unw ;
int unwsec = mod - > arch . unw_sec_idx ;
if ( unwsec ) {
unw = unwind_add_table ( mod , ( void * ) sechdrs [ unwsec ] . sh_addr ,
sechdrs [ unwsec ] . sh_size ) ;
mod - > arch . unw_info = unw ;
}
# endif
2013-03-06 15:23:45 +04:00
return 0 ;
2013-01-22 15:33:19 +04:00
}