2005-04-17 02:20:36 +04:00
/* Postprocess module symbol versions
*
* Copyright 2003 Kai Germaschewski
* Copyright 2002 - 2004 Rusty Russell , IBM Corporation
2006-02-26 22:11:17 +03:00
* Copyright 2006 Sam Ravnborg
2005-04-17 02:20:36 +04:00
* Based in part on module - init - tools / depmod . c , file2alias
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* Usage : modpost vmlinux module1 . o module2 . o . . .
*/
# include <ctype.h>
# include "modpost.h"
2006-06-09 23:53:55 +04:00
# include "../../include/linux/license.h"
2005-04-17 02:20:36 +04:00
/* Are we using CONFIG_MODVERSIONS? */
int modversions = 0 ;
/* Warn about undefined symbols? (do so if we have vmlinux) */
int have_vmlinux = 0 ;
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
static int all_versions = 0 ;
2006-01-29 00:15:55 +03:00
/* If we are modposting external module set to 1 */
static int external_module = 0 ;
2006-09-08 00:08:54 +04:00
/* Only warn about unresolved symbols */
static int warn_unresolved = 0 ;
2006-06-09 09:12:53 +04:00
/* How a symbol is exported */
2006-07-01 13:44:23 +04:00
enum export {
export_plain , export_unused , export_gpl ,
export_unused_gpl , export_gpl_future , export_unknown
} ;
2005-04-17 02:20:36 +04:00
2006-01-28 19:19:35 +03:00
void fatal ( const char * fmt , . . . )
2005-04-17 02:20:36 +04:00
{
va_list arglist ;
fprintf ( stderr , " FATAL: " ) ;
va_start ( arglist , fmt ) ;
vfprintf ( stderr , fmt , arglist ) ;
va_end ( arglist ) ;
exit ( 1 ) ;
}
2006-01-28 19:19:35 +03:00
void warn ( const char * fmt , . . . )
2005-04-17 02:20:36 +04:00
{
va_list arglist ;
fprintf ( stderr , " WARNING: " ) ;
va_start ( arglist , fmt ) ;
vfprintf ( stderr , fmt , arglist ) ;
va_end ( arglist ) ;
}
2006-10-07 15:35:32 +04:00
void merror ( const char * fmt , . . . )
{
va_list arglist ;
fprintf ( stderr , " ERROR: " ) ;
va_start ( arglist , fmt ) ;
vfprintf ( stderr , fmt , arglist ) ;
va_end ( arglist ) ;
}
2006-01-29 00:15:55 +03:00
static int is_vmlinux ( const char * modname )
{
const char * myname ;
if ( ( myname = strrchr ( modname , ' / ' ) ) )
myname + + ;
else
myname = modname ;
return strcmp ( myname , " vmlinux " ) = = 0 ;
}
2005-04-17 02:20:36 +04:00
void * do_nofail ( void * ptr , const char * expr )
{
if ( ! ptr ) {
fatal ( " modpost: Memory allocation failure: %s. \n " , expr ) ;
}
return ptr ;
}
/* A list of all modules we processed */
static struct module * modules ;
2006-01-28 19:19:35 +03:00
static struct module * find_module ( char * modname )
2005-04-17 02:20:36 +04:00
{
struct module * mod ;
for ( mod = modules ; mod ; mod = mod - > next )
if ( strcmp ( mod - > name , modname ) = = 0 )
break ;
return mod ;
}
2006-01-28 19:19:35 +03:00
static struct module * new_module ( char * modname )
2005-04-17 02:20:36 +04:00
{
struct module * mod ;
char * p , * s ;
2006-03-03 18:46:04 +03:00
2005-04-17 02:20:36 +04:00
mod = NOFAIL ( malloc ( sizeof ( * mod ) ) ) ;
memset ( mod , 0 , sizeof ( * mod ) ) ;
p = NOFAIL ( strdup ( modname ) ) ;
/* strip trailing .o */
if ( ( s = strrchr ( p , ' . ' ) ) ! = NULL )
if ( strcmp ( s , " .o " ) = = 0 )
* s = ' \0 ' ;
/* add to list */
mod - > name = p ;
2006-06-09 23:53:55 +04:00
mod - > gpl_compatible = - 1 ;
2005-04-17 02:20:36 +04:00
mod - > next = modules ;
modules = mod ;
return mod ;
}
/* A hash of all exported symbols,
* struct symbol is also used for lists of unresolved symbols */
# define SYMBOL_HASH_SIZE 1024
struct symbol {
struct symbol * next ;
struct module * module ;
unsigned int crc ;
int crc_valid ;
unsigned int weak : 1 ;
2006-01-29 00:15:55 +03:00
unsigned int vmlinux : 1 ; /* 1 if symbol is defined in vmlinux */
unsigned int kernel : 1 ; /* 1 if symbol is from kernel
* ( only for external modules ) * */
2006-01-29 00:22:33 +03:00
unsigned int preloaded : 1 ; /* 1 if symbol from Module.symvers */
2006-06-09 09:12:53 +04:00
enum export export ; /* Type of export */
2005-04-17 02:20:36 +04:00
char name [ 0 ] ;
} ;
static struct symbol * symbolhash [ SYMBOL_HASH_SIZE ] ;
/* This is based on the hash agorithm from gdbm, via tdb */
static inline unsigned int tdb_hash ( const char * name )
{
unsigned value ; /* Used to compute the hash value. */
unsigned i ; /* Used to cycle through random values. */
/* Set the initial value from the key size. */
for ( value = 0x238F13AF * strlen ( name ) , i = 0 ; name [ i ] ; i + + )
value = ( value + ( ( ( unsigned char * ) name ) [ i ] < < ( i * 5 % 24 ) ) ) ;
return ( 1103515243 * value + 12345 ) ;
}
2006-01-28 19:19:35 +03:00
/**
* Allocate a new symbols for use in the hash of exported symbols or
* the list of unresolved symbols per module
* */
static struct symbol * alloc_symbol ( const char * name , unsigned int weak ,
struct symbol * next )
2005-04-17 02:20:36 +04:00
{
struct symbol * s = NOFAIL ( malloc ( sizeof ( * s ) + strlen ( name ) + 1 ) ) ;
memset ( s , 0 , sizeof ( * s ) ) ;
strcpy ( s - > name , name ) ;
s - > weak = weak ;
s - > next = next ;
return s ;
}
/* For the hash of exported symbols */
2006-06-09 09:12:53 +04:00
static struct symbol * new_symbol ( const char * name , struct module * module ,
enum export export )
2005-04-17 02:20:36 +04:00
{
unsigned int hash ;
struct symbol * new ;
hash = tdb_hash ( name ) % SYMBOL_HASH_SIZE ;
new = symbolhash [ hash ] = alloc_symbol ( name , 0 , symbolhash [ hash ] ) ;
new - > module = module ;
2006-06-09 09:12:53 +04:00
new - > export = export ;
2006-01-29 00:15:55 +03:00
return new ;
2005-04-17 02:20:36 +04:00
}
2006-01-28 19:19:35 +03:00
static struct symbol * find_symbol ( const char * name )
2005-04-17 02:20:36 +04:00
{
struct symbol * s ;
/* For our purposes, .foo matches foo. PPC64 needs this. */
if ( name [ 0 ] = = ' . ' )
name + + ;
for ( s = symbolhash [ tdb_hash ( name ) % SYMBOL_HASH_SIZE ] ; s ; s = s - > next ) {
if ( strcmp ( s - > name , name ) = = 0 )
return s ;
}
return NULL ;
}
2006-06-09 09:12:53 +04:00
static struct {
const char * str ;
enum export export ;
} export_list [ ] = {
{ . str = " EXPORT_SYMBOL " , . export = export_plain } ,
2006-07-01 13:44:23 +04:00
{ . str = " EXPORT_UNUSED_SYMBOL " , . export = export_unused } ,
2006-06-09 09:12:53 +04:00
{ . str = " EXPORT_SYMBOL_GPL " , . export = export_gpl } ,
2006-07-01 13:44:23 +04:00
{ . str = " EXPORT_UNUSED_SYMBOL_GPL " , . export = export_unused_gpl } ,
2006-06-09 09:12:53 +04:00
{ . str = " EXPORT_SYMBOL_GPL_FUTURE " , . export = export_gpl_future } ,
{ . str = " (unknown) " , . export = export_unknown } ,
} ;
static const char * export_str ( enum export ex )
{
return export_list [ ex ] . str ;
}
static enum export export_no ( const char * s )
{
int i ;
2006-07-01 12:10:19 +04:00
if ( ! s )
return export_unknown ;
2006-06-09 09:12:53 +04:00
for ( i = 0 ; export_list [ i ] . export ! = export_unknown ; i + + ) {
if ( strcmp ( export_list [ i ] . str , s ) = = 0 )
return export_list [ i ] . export ;
}
return export_unknown ;
}
static enum export export_from_sec ( struct elf_info * elf , Elf_Section sec )
{
if ( sec = = elf - > export_sec )
return export_plain ;
2006-07-01 13:44:23 +04:00
else if ( sec = = elf - > export_unused_sec )
return export_unused ;
2006-06-09 09:12:53 +04:00
else if ( sec = = elf - > export_gpl_sec )
return export_gpl ;
2006-07-01 13:44:23 +04:00
else if ( sec = = elf - > export_unused_gpl_sec )
return export_unused_gpl ;
2006-06-09 09:12:53 +04:00
else if ( sec = = elf - > export_gpl_future_sec )
return export_gpl_future ;
else
return export_unknown ;
}
2006-01-28 19:19:35 +03:00
/**
* Add an exported symbol - it may have already been added without a
* CRC , in this case just update the CRC
* */
2006-06-09 09:12:53 +04:00
static struct symbol * sym_add_exported ( const char * name , struct module * mod ,
enum export export )
2005-04-17 02:20:36 +04:00
{
struct symbol * s = find_symbol ( name ) ;
if ( ! s ) {
2006-06-09 09:12:53 +04:00
s = new_symbol ( name , mod , export ) ;
2006-01-29 00:22:33 +03:00
} else {
if ( ! s - > preloaded ) {
2006-03-05 15:48:58 +03:00
warn ( " %s: '%s' exported twice. Previous export "
2006-01-29 00:22:33 +03:00
" was in %s%s \n " , mod - > name , name ,
s - > module - > name ,
is_vmlinux ( s - > module - > name ) ? " " : " .ko " ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-01-29 00:22:33 +03:00
s - > preloaded = 0 ;
2006-01-29 00:15:55 +03:00
s - > vmlinux = is_vmlinux ( mod - > name ) ;
s - > kernel = 0 ;
2006-06-09 09:12:53 +04:00
s - > export = export ;
2006-01-29 00:15:55 +03:00
return s ;
}
static void sym_update_crc ( const char * name , struct module * mod ,
2006-06-09 09:12:53 +04:00
unsigned int crc , enum export export )
2006-01-29 00:15:55 +03:00
{
struct symbol * s = find_symbol ( name ) ;
if ( ! s )
2006-06-09 09:12:53 +04:00
s = new_symbol ( name , mod , export ) ;
2006-01-29 00:15:55 +03:00
s - > crc = crc ;
s - > crc_valid = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-01-28 19:19:35 +03:00
void * grab_file ( const char * filename , unsigned long * size )
2005-04-17 02:20:36 +04:00
{
struct stat st ;
void * map ;
int fd ;
fd = open ( filename , O_RDONLY ) ;
if ( fd < 0 | | fstat ( fd , & st ) ! = 0 )
return NULL ;
* size = st . st_size ;
map = mmap ( NULL , * size , PROT_READ | PROT_WRITE , MAP_PRIVATE , fd , 0 ) ;
close ( fd ) ;
if ( map = = MAP_FAILED )
return NULL ;
return map ;
}
2006-01-28 19:19:35 +03:00
/**
* Return a copy of the next line in a mmap ' ed file .
* spaces in the beginning of the line is trimmed away .
* Return a pointer to a static buffer .
* */
char * get_next_line ( unsigned long * pos , void * file , unsigned long size )
2005-04-17 02:20:36 +04:00
{
static char line [ 4096 ] ;
int skip = 1 ;
size_t len = 0 ;
signed char * p = ( signed char * ) file + * pos ;
char * s = line ;
for ( ; * pos < size ; ( * pos ) + + )
{
if ( skip & & isspace ( * p ) ) {
p + + ;
continue ;
}
skip = 0 ;
if ( * p ! = ' \n ' & & ( * pos < size ) ) {
len + + ;
* s + + = * p + + ;
if ( len > 4095 )
break ; /* Too long, stop */
} else {
/* End of string */
* s = ' \0 ' ;
return line ;
}
}
/* End of buffer */
return NULL ;
}
2006-01-28 19:19:35 +03:00
void release_file ( void * file , unsigned long size )
2005-04-17 02:20:36 +04:00
{
munmap ( file , size ) ;
}
2007-02-26 17:33:52 +03:00
static int parse_elf ( struct elf_info * info , const char * filename )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
2007-02-26 17:33:52 +03:00
Elf_Ehdr * hdr ;
2005-04-17 02:20:36 +04:00
Elf_Shdr * sechdrs ;
Elf_Sym * sym ;
hdr = grab_file ( filename , & info - > size ) ;
if ( ! hdr ) {
perror ( filename ) ;
2006-06-25 01:46:54 +04:00
exit ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
info - > hdr = hdr ;
2007-02-26 17:33:52 +03:00
if ( info - > size < sizeof ( * hdr ) ) {
/* file too small, assume this is an empty .o file */
return 0 ;
}
/* Is this a valid ELF file? */
if ( ( hdr - > e_ident [ EI_MAG0 ] ! = ELFMAG0 ) | |
( hdr - > e_ident [ EI_MAG1 ] ! = ELFMAG1 ) | |
( hdr - > e_ident [ EI_MAG2 ] ! = ELFMAG2 ) | |
( hdr - > e_ident [ EI_MAG3 ] ! = ELFMAG3 ) ) {
/* Not an ELF file - silently ignore it */
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* Fix endianness in ELF header */
hdr - > e_shoff = TO_NATIVE ( hdr - > e_shoff ) ;
hdr - > e_shstrndx = TO_NATIVE ( hdr - > e_shstrndx ) ;
hdr - > e_shnum = TO_NATIVE ( hdr - > e_shnum ) ;
hdr - > e_machine = TO_NATIVE ( hdr - > e_machine ) ;
sechdrs = ( void * ) hdr + hdr - > e_shoff ;
info - > sechdrs = sechdrs ;
/* Fix endianness in section headers */
for ( i = 0 ; i < hdr - > e_shnum ; i + + ) {
sechdrs [ i ] . sh_type = TO_NATIVE ( sechdrs [ i ] . sh_type ) ;
sechdrs [ i ] . sh_offset = TO_NATIVE ( sechdrs [ i ] . sh_offset ) ;
sechdrs [ i ] . sh_size = TO_NATIVE ( sechdrs [ i ] . sh_size ) ;
sechdrs [ i ] . sh_link = TO_NATIVE ( sechdrs [ i ] . sh_link ) ;
sechdrs [ i ] . sh_name = TO_NATIVE ( sechdrs [ i ] . sh_name ) ;
}
/* Find symbol table. */
for ( i = 1 ; i < hdr - > e_shnum ; i + + ) {
const char * secstrings
= ( void * ) hdr + sechdrs [ hdr - > e_shstrndx ] . sh_offset ;
2006-06-09 09:12:53 +04:00
const char * secname ;
2005-04-17 02:20:36 +04:00
2007-02-26 17:33:52 +03:00
if ( sechdrs [ i ] . sh_offset > info - > size ) {
fatal ( " %s is truncated. sechdrs[i].sh_offset=%u > sizeof(*hrd)=%ul \n " , filename , ( unsigned int ) sechdrs [ i ] . sh_offset , sizeof ( * hdr ) ) ;
return 0 ;
}
2006-06-09 09:12:53 +04:00
secname = secstrings + sechdrs [ i ] . sh_name ;
if ( strcmp ( secname , " .modinfo " ) = = 0 ) {
2005-04-17 02:20:36 +04:00
info - > modinfo = ( void * ) hdr + sechdrs [ i ] . sh_offset ;
info - > modinfo_len = sechdrs [ i ] . sh_size ;
2006-06-09 09:12:53 +04:00
} else if ( strcmp ( secname , " __ksymtab " ) = = 0 )
info - > export_sec = i ;
2006-07-01 13:44:23 +04:00
else if ( strcmp ( secname , " __ksymtab_unused " ) = = 0 )
info - > export_unused_sec = i ;
2006-06-09 09:12:53 +04:00
else if ( strcmp ( secname , " __ksymtab_gpl " ) = = 0 )
info - > export_gpl_sec = i ;
2006-07-01 13:44:23 +04:00
else if ( strcmp ( secname , " __ksymtab_unused_gpl " ) = = 0 )
info - > export_unused_gpl_sec = i ;
2006-06-09 09:12:53 +04:00
else if ( strcmp ( secname , " __ksymtab_gpl_future " ) = = 0 )
info - > export_gpl_future_sec = i ;
2005-04-17 02:20:36 +04:00
if ( sechdrs [ i ] . sh_type ! = SHT_SYMTAB )
continue ;
info - > symtab_start = ( void * ) hdr + sechdrs [ i ] . sh_offset ;
2006-03-03 18:46:04 +03:00
info - > symtab_stop = ( void * ) hdr + sechdrs [ i ] . sh_offset
2005-04-17 02:20:36 +04:00
+ sechdrs [ i ] . sh_size ;
2006-03-03 18:46:04 +03:00
info - > strtab = ( void * ) hdr +
2005-04-17 02:20:36 +04:00
sechdrs [ sechdrs [ i ] . sh_link ] . sh_offset ;
}
if ( ! info - > symtab_start ) {
2006-01-28 18:57:26 +03:00
fatal ( " %s has no symtab? \n " , filename ) ;
2005-04-17 02:20:36 +04:00
}
/* Fix endianness in symbols */
for ( sym = info - > symtab_start ; sym < info - > symtab_stop ; sym + + ) {
sym - > st_shndx = TO_NATIVE ( sym - > st_shndx ) ;
sym - > st_name = TO_NATIVE ( sym - > st_name ) ;
sym - > st_value = TO_NATIVE ( sym - > st_value ) ;
sym - > st_size = TO_NATIVE ( sym - > st_size ) ;
}
2007-02-26 17:33:52 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2006-01-28 19:19:35 +03:00
static void parse_elf_finish ( struct elf_info * info )
2005-04-17 02:20:36 +04:00
{
release_file ( info - > hdr , info - > size ) ;
}
2006-03-02 13:35:31 +03:00
# define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_"
# define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_"
2005-04-17 02:20:36 +04:00
2006-01-28 19:19:35 +03:00
static void handle_modversions ( struct module * mod , struct elf_info * info ,
Elf_Sym * sym , const char * symname )
2005-04-17 02:20:36 +04:00
{
unsigned int crc ;
2006-06-09 09:12:53 +04:00
enum export export = export_from_sec ( info , sym - > st_shndx ) ;
2005-04-17 02:20:36 +04:00
switch ( sym - > st_shndx ) {
case SHN_COMMON :
2006-01-28 18:57:26 +03:00
warn ( " \" %s \" [%s] is COMMON symbol \n " , symname , mod - > name ) ;
2005-04-17 02:20:36 +04:00
break ;
case SHN_ABS :
/* CRC'd symbol */
if ( memcmp ( symname , CRC_PFX , strlen ( CRC_PFX ) ) = = 0 ) {
crc = ( unsigned int ) sym - > st_value ;
2006-06-09 09:12:53 +04:00
sym_update_crc ( symname + strlen ( CRC_PFX ) , mod , crc ,
export ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case SHN_UNDEF :
/* undefined symbol */
if ( ELF_ST_BIND ( sym - > st_info ) ! = STB_GLOBAL & &
ELF_ST_BIND ( sym - > st_info ) ! = STB_WEAK )
break ;
/* ignore global offset table */
if ( strcmp ( symname , " _GLOBAL_OFFSET_TABLE_ " ) = = 0 )
break ;
/* ignore __this_module, it will be resolved shortly */
if ( strcmp ( symname , MODULE_SYMBOL_PREFIX " __this_module " ) = = 0 )
break ;
2005-08-20 00:44:57 +04:00
/* cope with newer glibc (2.3.4 or higher) STT_ definition in elf.h */
# if defined(STT_REGISTER) || defined(STT_SPARC_REGISTER)
/* add compatibility with older glibc */
# ifndef STT_SPARC_REGISTER
# define STT_SPARC_REGISTER STT_REGISTER
# endif
2005-04-17 02:20:36 +04:00
if ( info - > hdr - > e_machine = = EM_SPARC | |
info - > hdr - > e_machine = = EM_SPARCV9 ) {
/* Ignore register directives. */
2005-08-20 00:44:57 +04:00
if ( ELF_ST_TYPE ( sym - > st_info ) = = STT_SPARC_REGISTER )
2005-04-17 02:20:36 +04:00
break ;
2006-03-03 18:46:04 +03:00
if ( symname [ 0 ] = = ' . ' ) {
char * munged = strdup ( symname ) ;
munged [ 0 ] = ' _ ' ;
munged [ 1 ] = toupper ( munged [ 1 ] ) ;
symname = munged ;
}
2005-04-17 02:20:36 +04:00
}
# endif
2006-03-03 18:46:04 +03:00
2005-04-17 02:20:36 +04:00
if ( memcmp ( symname , MODULE_SYMBOL_PREFIX ,
strlen ( MODULE_SYMBOL_PREFIX ) ) = = 0 )
mod - > unres = alloc_symbol ( symname +
strlen ( MODULE_SYMBOL_PREFIX ) ,
ELF_ST_BIND ( sym - > st_info ) = = STB_WEAK ,
mod - > unres ) ;
break ;
default :
/* All exported symbols */
if ( memcmp ( symname , KSYMTAB_PFX , strlen ( KSYMTAB_PFX ) ) = = 0 ) {
2006-06-09 09:12:53 +04:00
sym_add_exported ( symname + strlen ( KSYMTAB_PFX ) , mod ,
export ) ;
2005-04-17 02:20:36 +04:00
}
if ( strcmp ( symname , MODULE_SYMBOL_PREFIX " init_module " ) = = 0 )
mod - > has_init = 1 ;
if ( strcmp ( symname , MODULE_SYMBOL_PREFIX " cleanup_module " ) = = 0 )
mod - > has_cleanup = 1 ;
break ;
}
}
2006-01-28 19:19:35 +03:00
/**
* Parse tag = value strings from . modinfo section
* */
2005-04-17 02:20:36 +04:00
static char * next_string ( char * string , unsigned long * secsize )
{
/* Skip non-zero chars */
while ( string [ 0 ] ) {
string + + ;
if ( ( * secsize ) - - < = 1 )
return NULL ;
}
/* Skip any zero padding. */
while ( ! string [ 0 ] ) {
string + + ;
if ( ( * secsize ) - - < = 1 )
return NULL ;
}
return string ;
}
2006-06-09 23:53:55 +04:00
static char * get_next_modinfo ( void * modinfo , unsigned long modinfo_len ,
const char * tag , char * info )
2005-04-17 02:20:36 +04:00
{
char * p ;
unsigned int taglen = strlen ( tag ) ;
unsigned long size = modinfo_len ;
2006-06-09 23:53:55 +04:00
if ( info ) {
size - = info - ( char * ) modinfo ;
modinfo = next_string ( info , & size ) ;
}
2005-04-17 02:20:36 +04:00
for ( p = modinfo ; p ; p = next_string ( p , & size ) ) {
if ( strncmp ( p , tag , taglen ) = = 0 & & p [ taglen ] = = ' = ' )
return p + taglen + 1 ;
}
return NULL ;
}
2006-06-09 23:53:55 +04:00
static char * get_modinfo ( void * modinfo , unsigned long modinfo_len ,
const char * tag )
{
return get_next_modinfo ( modinfo , modinfo_len , tag , NULL ) ;
}
2006-02-27 00:18:11 +03:00
/**
* Test if string s ends in string sub
* return 0 if match
* */
static int strrcmp ( const char * s , const char * sub )
{
int slen , sublen ;
2006-03-03 18:46:04 +03:00
2006-02-27 00:18:11 +03:00
if ( ! s | | ! sub )
return 1 ;
2006-03-03 18:46:04 +03:00
2006-02-27 00:18:11 +03:00
slen = strlen ( s ) ;
sublen = strlen ( sub ) ;
2006-03-03 18:46:04 +03:00
2006-02-27 00:18:11 +03:00
if ( ( slen = = 0 ) | | ( sublen = = 0 ) )
return 1 ;
if ( sublen > slen )
return 1 ;
return memcmp ( s + slen - sublen , sub , sublen ) ;
}
/**
* Whitelist to allow certain references to pass with no warning .
* Pattern 1 :
* If a module parameter is declared __initdata and permissions = 0
* then this is legal despite the warning generated .
* We cannot see value of permissions here , so just ignore
* this pattern .
* The pattern is identified by :
* tosec = . init . data
2006-03-05 02:16:26 +03:00
* fromsec = . data *
2006-02-27 00:18:11 +03:00
* atsym = __param *
2006-03-03 18:46:04 +03:00
*
2006-02-27 00:18:11 +03:00
* Pattern 2 :
2006-04-15 22:17:12 +04:00
* Many drivers utilise a * driver container with references to
2006-02-27 00:18:11 +03:00
* add , remove , probe functions etc .
* These functions may often be marked __init and we do not want to
* warn here .
* the pattern is identified by :
2006-04-15 01:54:13 +04:00
* tosec = . init . text | . exit . text | . init . data
2006-02-27 00:18:11 +03:00
* fromsec = . data
2007-02-26 18:45:41 +03:00
* atsym = * driver , * _template , * _sht , * _ops , * _probe , * probe_one , * _console
2007-01-11 03:52:44 +03:00
*
* Pattern 3 :
2007-02-26 19:49:06 +03:00
* Whitelist all references from . pci_fixup * section to . init . text
* This is part of the PCI init when built - in
*
* Pattern 4 :
* Whitelist all refereces from . text . head to . init . data
* Whitelist all refereces from . text . head to . init . text
*
* Pattern 5 :
2007-01-11 03:52:44 +03:00
* Some symbols belong to init section but still it is ok to reference
* these from non - init sections as these symbols don ' t have any memory
* allocated for them and symbol address and value are same . So even
* if init section is freed , its ok to reference those symbols .
* For ex . symbols marking the init section boundaries .
* This pattern is identified by
* refsymname = __init_begin , _sinittext , _einittext
2007-02-26 19:49:06 +03:00
*
* Pattern 6 :
2007-02-26 18:45:41 +03:00
* During the early init phase we have references from . init . text to
* . text we have an intended section mismatch - do not warn about it .
* See kernel_init ( ) in init / main . c
* tosec = . init . text
* fromsec = . text
* atsym = kernel_init
2007-02-27 11:14:58 +03:00
*
* Pattern 7 :
* Logos used in drivers / video / logo reside in __initdata but the
* funtion that references them are EXPORT_SYMBOL ( ) so cannot be
* marker __init . So we whitelist them here .
* The pattern is :
* tosec = . init . data
* fromsec = . text *
* refsymname = logo_
2007-04-29 22:53:01 +04:00
*
* Pattern 8 :
* Symbols contained in . paravirtprobe may safely reference . init . text .
* The pattern is :
* tosec = . init . text
* fromsec = . paravirtprobe
*
2007-05-08 11:23:10 +04:00
* Pattern 9 :
* Some of functions are common code between boot time and hotplug
* time . The bootmem allocater is called only boot time in its
* functions . So it ' s ok to reference .
* tosec = . init . text
*
* Pattern 10 :
* ia64 has machvec table for each platform . It is mixture of function
* pointer of . init . text and . text .
* fromsec = . machvec
2006-02-27 00:18:11 +03:00
* */
2006-08-08 12:32:11 +04:00
static int secref_whitelist ( const char * modname , const char * tosec ,
2007-01-11 03:52:44 +03:00
const char * fromsec , const char * atsym ,
const char * refsymname )
2006-02-27 00:18:11 +03:00
{
int f1 = 1 , f2 = 1 ;
const char * * s ;
const char * pat2sym [ ] = {
2006-04-15 22:17:12 +04:00
" driver " ,
2006-04-15 01:54:13 +04:00
" _template " , /* scsi uses *_template a lot */
" _sht " , /* scsi also used *_sht to some extent */
2006-02-27 00:18:11 +03:00
" _ops " ,
" _probe " ,
" _probe_one " ,
2007-01-11 03:52:44 +03:00
" _console " ,
2007-05-02 21:27:08 +04:00
" apic_es7000 " ,
2006-02-27 00:18:11 +03:00
NULL
} ;
2006-03-03 18:46:04 +03:00
2007-01-11 03:52:44 +03:00
const char * pat3refsym [ ] = {
" __init_begin " ,
" _sinittext " ,
" _einittext " ,
NULL
} ;
2007-05-08 11:23:10 +04:00
const char * pat4sym [ ] = {
" sparse_index_alloc " ,
" zone_wait_table_init " ,
NULL
} ;
2006-02-27 00:18:11 +03:00
/* Check for pattern 1 */
if ( strcmp ( tosec , " .init.data " ) ! = 0 )
f1 = 0 ;
2006-03-05 02:16:26 +03:00
if ( strncmp ( fromsec , " .data " , strlen ( " .data " ) ) ! = 0 )
2006-02-27 00:18:11 +03:00
f1 = 0 ;
if ( strncmp ( atsym , " __param " , strlen ( " __param " ) ) ! = 0 )
f1 = 0 ;
if ( f1 )
return f1 ;
/* Check for pattern 2 */
2006-03-03 18:46:04 +03:00
if ( ( strcmp ( tosec , " .init.text " ) ! = 0 ) & &
2006-04-15 01:54:13 +04:00
( strcmp ( tosec , " .exit.text " ) ! = 0 ) & &
( strcmp ( tosec , " .init.data " ) ! = 0 ) )
2006-02-27 00:18:11 +03:00
f2 = 0 ;
if ( strcmp ( fromsec , " .data " ) ! = 0 )
f2 = 0 ;
for ( s = pat2sym ; * s ; s + + )
if ( strrcmp ( atsym , * s ) = = 0 )
f1 = 1 ;
2006-08-08 12:32:11 +04:00
if ( f1 & & f2 )
return 1 ;
2006-02-27 00:18:11 +03:00
2007-02-26 19:49:06 +03:00
/* Check for pattern 3 */
if ( ( strncmp ( fromsec , " .pci_fixup " , strlen ( " .pci_fixup " ) ) = = 0 ) & &
( strcmp ( tosec , " .init.text " ) = = 0 ) )
return 1 ;
2007-02-13 15:26:22 +03:00
2007-02-26 18:45:41 +03:00
/* Check for pattern 4 */
2007-02-26 19:49:06 +03:00
if ( ( strcmp ( fromsec , " .text.head " ) = = 0 ) & &
( ( strcmp ( tosec , " .init.data " ) = = 0 ) | |
( strcmp ( tosec , " .init.text " ) = = 0 ) ) )
return 1 ;
/* Check for pattern 5 */
for ( s = pat3refsym ; * s ; s + + )
if ( strcmp ( refsymname , * s ) = = 0 )
return 1 ;
/* Check for pattern 6 */
2007-02-26 18:45:41 +03:00
if ( ( strcmp ( tosec , " .init.text " ) = = 0 ) & &
( strcmp ( fromsec , " .text " ) = = 0 ) & &
( strcmp ( refsymname , " kernel_init " ) = = 0 ) )
2006-08-08 12:32:11 +04:00
return 1 ;
2007-01-11 03:52:44 +03:00
2007-02-27 11:14:58 +03:00
/* Check for pattern 7 */
if ( ( strcmp ( tosec , " .init.data " ) = = 0 ) & &
( strncmp ( fromsec , " .text " , strlen ( " .text " ) ) = = 0 ) & &
( strncmp ( refsymname , " logo_ " , strlen ( " logo_ " ) ) = = 0 ) )
return 1 ;
2007-04-29 22:53:01 +04:00
/* Check for pattern 8 */
if ( ( strcmp ( tosec , " .init.text " ) = = 0 ) & &
( strcmp ( fromsec , " .paravirtprobe " ) = = 0 ) )
2007-02-13 15:26:22 +03:00
return 1 ;
2007-05-08 11:23:10 +04:00
/* Check for pattern 9 */
if ( ( strcmp ( tosec , " .init.text " ) = = 0 ) & &
( strcmp ( fromsec , " .text " ) = = 0 ) )
for ( s = pat4sym ; * s ; s + + )
if ( strcmp ( atsym , * s ) = = 0 )
return 1 ;
/* Check for pattern 10 */
if ( strcmp ( fromsec , " .machvec " ) = = 0 )
return 1 ;
2006-08-09 10:23:55 +04:00
return 0 ;
2006-02-27 00:18:11 +03:00
}
2006-02-19 13:53:35 +03:00
/**
* Find symbol based on relocation record info .
* In some cases the symbol supplied is a valid symbol so
* return refsym . If st_name ! = 0 we assume this is a valid symbol .
* In other cases the symbol needs to be looked up in the symbol table
* based on section and address .
* * */
static Elf_Sym * find_elf_symbol ( struct elf_info * elf , Elf_Addr addr ,
Elf_Sym * relsym )
{
Elf_Sym * sym ;
if ( relsym - > st_name ! = 0 )
return relsym ;
for ( sym = elf - > symtab_start ; sym < elf - > symtab_stop ; sym + + ) {
if ( sym - > st_shndx ! = relsym - > st_shndx )
continue ;
if ( sym - > st_value = = addr )
return sym ;
}
return NULL ;
}
2007-02-21 00:58:16 +03:00
static inline int is_arm_mapping_symbol ( const char * str )
{
return str [ 0 ] = = ' $ ' & & strchr ( " atd " , str [ 1 ] )
& & ( str [ 2 ] = = ' \0 ' | | str [ 2 ] = = ' . ' ) ;
}
/*
* If there ' s no name there , ignore it ; likewise , ignore it if it ' s
* one of the magic symbols emitted used by current ARM tools .
*
* Otherwise if find_symbols_between ( ) returns those symbols , they ' ll
* fail the whitelist tests and cause lots of false alarms . . . fixable
* only by merging __exit and __init sections into __text , bloating
* the kernel ( which is especially evil on embedded platforms ) .
*/
static inline int is_valid_name ( struct elf_info * elf , Elf_Sym * sym )
{
const char * name = elf - > strtab + sym - > st_name ;
if ( ! name | | ! strlen ( name ) )
return 0 ;
return ! is_arm_mapping_symbol ( name ) ;
}
2006-02-18 00:42:02 +03:00
/*
2006-03-05 14:02:46 +03:00
* Find symbols before or equal addr and after addr - in the section sec .
* If we find two symbols with equal offset prefer one with a valid name .
* The ELF format may have a better way to detect what type of symbol
* it is , but this works for now .
2006-02-18 00:42:02 +03:00
* */
static void find_symbols_between ( struct elf_info * elf , Elf_Addr addr ,
const char * sec ,
Elf_Sym * * before , Elf_Sym * * after )
{
Elf_Sym * sym ;
Elf_Ehdr * hdr = elf - > hdr ;
Elf_Addr beforediff = ~ 0 ;
Elf_Addr afterdiff = ~ 0 ;
const char * secstrings = ( void * ) hdr +
elf - > sechdrs [ hdr - > e_shstrndx ] . sh_offset ;
2006-03-03 18:46:04 +03:00
2006-02-18 00:42:02 +03:00
* before = NULL ;
* after = NULL ;
for ( sym = elf - > symtab_start ; sym < elf - > symtab_stop ; sym + + ) {
const char * symsec ;
if ( sym - > st_shndx > = SHN_LORESERVE )
continue ;
symsec = secstrings + elf - > sechdrs [ sym - > st_shndx ] . sh_name ;
if ( strcmp ( symsec , sec ) ! = 0 )
continue ;
2007-02-21 00:58:16 +03:00
if ( ! is_valid_name ( elf , sym ) )
continue ;
2006-02-18 00:42:02 +03:00
if ( sym - > st_value < = addr ) {
if ( ( addr - sym - > st_value ) < beforediff ) {
beforediff = addr - sym - > st_value ;
* before = sym ;
}
2006-03-05 14:02:46 +03:00
else if ( ( addr - sym - > st_value ) = = beforediff ) {
2007-02-21 00:58:16 +03:00
* before = sym ;
2006-03-05 14:02:46 +03:00
}
2006-02-18 00:42:02 +03:00
}
else
{
if ( ( sym - > st_value - addr ) < afterdiff ) {
afterdiff = sym - > st_value - addr ;
* after = sym ;
}
2006-03-05 14:02:46 +03:00
else if ( ( sym - > st_value - addr ) = = afterdiff ) {
2007-02-21 00:58:16 +03:00
* after = sym ;
2006-03-05 14:02:46 +03:00
}
2006-02-18 00:42:02 +03:00
}
}
}
/**
* Print a warning about a section mismatch .
* Try to find symbols near it so user can find it .
2006-02-27 00:18:11 +03:00
* Check whitelist before warning - it may be a false positive .
2006-02-18 00:42:02 +03:00
* */
static void warn_sec_mismatch ( const char * modname , const char * fromsec ,
struct elf_info * elf , Elf_Sym * sym , Elf_Rela r )
{
2006-02-19 13:53:35 +03:00
const char * refsymname = " " ;
Elf_Sym * before , * after ;
Elf_Sym * refsym ;
2006-02-18 00:42:02 +03:00
Elf_Ehdr * hdr = elf - > hdr ;
Elf_Shdr * sechdrs = elf - > sechdrs ;
const char * secstrings = ( void * ) hdr +
sechdrs [ hdr - > e_shstrndx ] . sh_offset ;
const char * secname = secstrings + sechdrs [ sym - > st_shndx ] . sh_name ;
2006-03-03 18:46:04 +03:00
2006-02-18 00:42:02 +03:00
find_symbols_between ( elf , r . r_offset , fromsec , & before , & after ) ;
2006-02-19 13:53:35 +03:00
refsym = find_elf_symbol ( elf , r . r_addend , sym ) ;
if ( refsym & & strlen ( elf - > strtab + refsym - > st_name ) )
refsymname = elf - > strtab + refsym - > st_name ;
2006-02-27 00:18:11 +03:00
/* check whitelist - we may ignore it */
2006-03-03 18:46:04 +03:00
if ( before & &
2006-08-08 12:32:11 +04:00
secref_whitelist ( modname , secname , fromsec ,
2007-01-11 03:52:44 +03:00
elf - > strtab + before - > st_name , refsymname ) )
2006-02-27 00:18:11 +03:00
return ;
2006-03-03 18:46:04 +03:00
2006-02-18 00:42:02 +03:00
if ( before & & after ) {
2006-02-19 13:53:35 +03:00
warn ( " %s - Section mismatch: reference to %s:%s from %s "
" between '%s' (at offset 0x%llx) and '%s' \n " ,
modname , secname , refsymname , fromsec ,
2006-02-18 00:42:02 +03:00
elf - > strtab + before - > st_name ,
2006-02-19 13:53:35 +03:00
( long long ) r . r_offset ,
2006-02-18 00:42:02 +03:00
elf - > strtab + after - > st_name ) ;
} else if ( before ) {
2006-02-19 13:53:35 +03:00
warn ( " %s - Section mismatch: reference to %s:%s from %s "
" after '%s' (at offset 0x%llx) \n " ,
2006-03-03 18:46:04 +03:00
modname , secname , refsymname , fromsec ,
2006-02-18 00:42:02 +03:00
elf - > strtab + before - > st_name ,
2006-02-19 13:53:35 +03:00
( long long ) r . r_offset ) ;
2006-02-18 00:42:02 +03:00
} else if ( after ) {
2006-02-19 13:53:35 +03:00
warn ( " %s - Section mismatch: reference to %s:%s from %s "
" before '%s' (at offset -0x%llx) \n " ,
2006-03-03 18:46:04 +03:00
modname , secname , refsymname , fromsec ,
2006-04-11 02:54:16 +04:00
elf - > strtab + after - > st_name ,
2006-02-19 13:53:35 +03:00
( long long ) r . r_offset ) ;
2006-02-18 00:42:02 +03:00
} else {
2006-02-19 13:53:35 +03:00
warn ( " %s - Section mismatch: reference to %s:%s from %s "
" (offset 0x%llx) \n " ,
modname , secname , fromsec , refsymname ,
( long long ) r . r_offset ) ;
2006-02-18 00:42:02 +03:00
}
}
/**
* A module includes a number of sections that are discarded
* either when loaded or when used as built - in .
* For loaded modules all functions marked __init and all data
* marked __initdata will be discarded when the module has been intialized .
* Likewise for modules used built - in the sections marked __exit
* are discarded because __exit marked function are supposed to be called
* only when a moduel is unloaded which never happes for built - in modules .
* The check_sec_ref ( ) function traverses all relocation records
* to find all references to a section that reference a section that will
* be discarded and warns about it .
* */
static void check_sec_ref ( struct module * mod , const char * modname ,
struct elf_info * elf ,
int section ( const char * ) ,
int section_ref_ok ( const char * ) )
{
int i ;
Elf_Sym * sym ;
Elf_Ehdr * hdr = elf - > hdr ;
Elf_Shdr * sechdrs = elf - > sechdrs ;
const char * secstrings = ( void * ) hdr +
sechdrs [ hdr - > e_shstrndx ] . sh_offset ;
2006-03-03 18:46:04 +03:00
2006-02-18 00:42:02 +03:00
/* Walk through all sections */
for ( i = 0 ; i < hdr - > e_shnum ; i + + ) {
2006-05-21 02:00:26 +04:00
const char * name = secstrings + sechdrs [ i ] . sh_name ;
const char * secname ;
Elf_Rela r ;
2006-05-21 02:00:28 +04:00
unsigned int r_sym ;
2006-02-18 00:42:02 +03:00
/* We want to process only relocation sections and not .init */
2006-05-21 02:00:26 +04:00
if ( sechdrs [ i ] . sh_type = = SHT_RELA ) {
Elf_Rela * rela ;
Elf_Rela * start = ( void * ) hdr + sechdrs [ i ] . sh_offset ;
Elf_Rela * stop = ( void * ) start + sechdrs [ i ] . sh_size ;
name + = strlen ( " .rela " ) ;
if ( section_ref_ok ( name ) )
continue ;
2006-02-18 00:42:02 +03:00
2006-05-21 02:00:26 +04:00
for ( rela = start ; rela < stop ; rela + + ) {
r . r_offset = TO_NATIVE ( rela - > r_offset ) ;
2006-05-21 02:00:28 +04:00
# if KERNEL_ELFCLASS == ELFCLASS64
if ( hdr - > e_machine = = EM_MIPS ) {
r_sym = ELF64_MIPS_R_SYM ( rela - > r_info ) ;
r_sym = TO_NATIVE ( r_sym ) ;
} else {
r . r_info = TO_NATIVE ( rela - > r_info ) ;
r_sym = ELF_R_SYM ( r . r_info ) ;
}
# else
r . r_info = TO_NATIVE ( rela - > r_info ) ;
r_sym = ELF_R_SYM ( r . r_info ) ;
# endif
2006-05-21 02:00:26 +04:00
r . r_addend = TO_NATIVE ( rela - > r_addend ) ;
2006-05-21 02:00:28 +04:00
sym = elf - > symtab_start + r_sym ;
2006-05-21 02:00:26 +04:00
/* Skip special sections */
if ( sym - > st_shndx > = SHN_LORESERVE )
continue ;
secname = secstrings +
sechdrs [ sym - > st_shndx ] . sh_name ;
if ( section ( secname ) )
warn_sec_mismatch ( modname , name ,
elf , sym , r ) ;
}
} else if ( sechdrs [ i ] . sh_type = = SHT_REL ) {
Elf_Rel * rel ;
Elf_Rel * start = ( void * ) hdr + sechdrs [ i ] . sh_offset ;
Elf_Rel * stop = ( void * ) start + sechdrs [ i ] . sh_size ;
name + = strlen ( " .rel " ) ;
if ( section_ref_ok ( name ) )
2006-02-18 00:42:02 +03:00
continue ;
2006-05-21 02:00:26 +04:00
for ( rel = start ; rel < stop ; rel + + ) {
r . r_offset = TO_NATIVE ( rel - > r_offset ) ;
2006-05-21 02:00:28 +04:00
# if KERNEL_ELFCLASS == ELFCLASS64
if ( hdr - > e_machine = = EM_MIPS ) {
r_sym = ELF64_MIPS_R_SYM ( rel - > r_info ) ;
r_sym = TO_NATIVE ( r_sym ) ;
} else {
r . r_info = TO_NATIVE ( rel - > r_info ) ;
r_sym = ELF_R_SYM ( r . r_info ) ;
}
# else
r . r_info = TO_NATIVE ( rel - > r_info ) ;
r_sym = ELF_R_SYM ( r . r_info ) ;
# endif
2006-05-21 02:00:26 +04:00
r . r_addend = 0 ;
2006-05-21 02:00:28 +04:00
sym = elf - > symtab_start + r_sym ;
2006-05-21 02:00:26 +04:00
/* Skip special sections */
if ( sym - > st_shndx > = SHN_LORESERVE )
continue ;
secname = secstrings +
sechdrs [ sym - > st_shndx ] . sh_name ;
if ( section ( secname ) )
warn_sec_mismatch ( modname , name ,
elf , sym , r ) ;
}
2006-02-18 00:42:02 +03:00
}
}
}
/**
* Functions used only during module init is marked __init and is stored in
* a . init . text section . Likewise data is marked __initdata and stored in
* a . init . data section .
* If this section is one of these sections return 1
* See include / linux / init . h for the details
* */
static int init_section ( const char * name )
{
if ( strcmp ( name , " .init " ) = = 0 )
return 1 ;
if ( strncmp ( name , " .init. " , strlen ( " .init. " ) ) = = 0 )
return 1 ;
return 0 ;
}
/**
* Identify sections from which references to a . init section is OK .
2006-03-03 18:46:04 +03:00
*
2006-02-18 00:42:02 +03:00
* Unfortunately references to read only data that referenced . init
* sections had to be excluded . Almost all of these are false
* positives , they are created by gcc . The downside of excluding rodata
* is that there really are some user references from rodata to
* init code , e . g . drivers / video / vgacon . c :
2006-03-03 18:46:04 +03:00
*
2006-02-18 00:42:02 +03:00
* const struct consw vga_con = {
* con_startup : vgacon_startup ,
*
* where vgacon_startup is __init . If you want to wade through the false
* positives , take out the check for rodata .
* */
static int init_section_ref_ok ( const char * name )
{
const char * * s ;
/* Absolute section names */
const char * namelist1 [ ] = {
" .init " ,
2006-03-05 02:16:26 +03:00
" .opd " , /* see comment [OPD] at exit_section_ref_ok() */
" .toc1 " , /* used by ppc64 */
2006-02-18 00:42:02 +03:00
" .stab " ,
2007-02-06 03:34:00 +03:00
" .data.rel.ro " , /* used by parisc64 */
2006-12-07 04:14:08 +03:00
" .parainstructions " ,
2006-02-18 00:42:02 +03:00
" .text.lock " ,
2006-03-05 02:16:26 +03:00
" __bug_table " , /* used by powerpc for BUG() */
2006-02-18 00:42:02 +03:00
" .pci_fixup_header " ,
" .pci_fixup_final " ,
" .pdr " ,
" __param " ,
2006-06-24 02:22:43 +04:00
" __ex_table " ,
" .fixup " ,
2006-06-08 03:23:26 +04:00
" .smp_locks " ,
2006-06-08 22:37:30 +04:00
" .plt " , /* seen on ARCH=um build on x86_64. Harmless */
2006-10-20 05:47:19 +04:00
" __ftr_fixup " , /* powerpc cpu feature fixup */
" __fw_ftr_fixup " , /* powerpc firmware feature fixup */
2006-02-18 00:42:02 +03:00
NULL
} ;
/* Start of section names */
const char * namelist2 [ ] = {
" .init. " ,
" .altinstructions " ,
" .eh_frame " ,
" .debug " ,
2006-12-07 04:14:08 +03:00
" .parainstructions " ,
2007-02-06 03:34:00 +03:00
" .rodata " ,
2006-02-18 00:42:02 +03:00
NULL
} ;
2006-02-22 23:24:50 +03:00
/* part of section name */
const char * namelist3 [ ] = {
" .unwind " , /* sample: IA_64.unwind.init.text */
NULL
} ;
2006-02-18 00:42:02 +03:00
for ( s = namelist1 ; * s ; s + + )
if ( strcmp ( * s , name ) = = 0 )
return 1 ;
2006-03-03 18:46:04 +03:00
for ( s = namelist2 ; * s ; s + + )
2006-02-18 00:42:02 +03:00
if ( strncmp ( * s , name , strlen ( * s ) ) = = 0 )
return 1 ;
2006-03-03 18:46:04 +03:00
for ( s = namelist3 ; * s ; s + + )
2006-03-05 13:34:15 +03:00
if ( strstr ( name , * s ) ! = NULL )
2006-02-22 23:24:50 +03:00
return 1 ;
2006-06-24 02:22:43 +04:00
if ( strrcmp ( name , " .init " ) = = 0 )
return 1 ;
2006-02-18 00:42:02 +03:00
return 0 ;
}
/*
* Functions used only during module exit is marked __exit and is stored in
* a . exit . text section . Likewise data is marked __exitdata and stored in
* a . exit . data section .
* If this section is one of these sections return 1
* See include / linux / init . h for the details
* */
static int exit_section ( const char * name )
{
if ( strcmp ( name , " .exit.text " ) = = 0 )
return 1 ;
if ( strcmp ( name , " .exit.data " ) = = 0 )
return 1 ;
return 0 ;
2006-03-03 18:46:04 +03:00
2006-02-18 00:42:02 +03:00
}
/*
* Identify sections from which references to a . exit section is OK .
2006-03-03 18:46:04 +03:00
*
2006-02-18 00:42:02 +03:00
* [ OPD ] Keith Ownes < kaos @ sgi . com > commented :
* For our future { in } sanity , add a comment that this is the ppc . opd
* section , not the ia64 . opd section .
* ia64 . opd should not point to discarded sections .
2006-04-15 01:54:13 +04:00
* [ . rodata ] like for . init . text we ignore . rodata references - same reason
2006-02-18 00:42:02 +03:00
* */
static int exit_section_ref_ok ( const char * name )
{
const char * * s ;
/* Absolute section names */
const char * namelist1 [ ] = {
" .exit.text " ,
" .exit.data " ,
" .init.text " ,
2006-04-15 01:54:13 +04:00
" .rodata " ,
2006-02-18 00:42:02 +03:00
" .opd " , /* See comment [OPD] */
2006-03-05 02:16:26 +03:00
" .toc1 " , /* used by ppc64 */
2006-02-18 00:42:02 +03:00
" .altinstructions " ,
" .pdr " ,
2006-03-05 02:16:26 +03:00
" __bug_table " , /* used by powerpc for BUG() */
2006-02-18 00:42:02 +03:00
" .exitcall.exit " ,
" .eh_frame " ,
2006-12-13 11:33:57 +03:00
" .parainstructions " ,
2006-02-18 00:42:02 +03:00
" .stab " ,
2006-06-24 02:22:43 +04:00
" __ex_table " ,
" .fixup " ,
2006-06-08 03:23:26 +04:00
" .smp_locks " ,
2006-06-08 22:37:30 +04:00
" .plt " , /* seen on ARCH=um build on x86_64. Harmless */
2006-02-18 00:42:02 +03:00
NULL
} ;
/* Start of section names */
const char * namelist2 [ ] = {
" .debug " ,
NULL
} ;
2006-02-22 23:24:50 +03:00
/* part of section name */
const char * namelist3 [ ] = {
" .unwind " , /* Sample: IA_64.unwind.exit.text */
NULL
} ;
2006-03-03 18:46:04 +03:00
2006-02-18 00:42:02 +03:00
for ( s = namelist1 ; * s ; s + + )
if ( strcmp ( * s , name ) = = 0 )
return 1 ;
2006-03-03 18:46:04 +03:00
for ( s = namelist2 ; * s ; s + + )
2006-02-18 00:42:02 +03:00
if ( strncmp ( * s , name , strlen ( * s ) ) = = 0 )
return 1 ;
2006-03-03 18:46:04 +03:00
for ( s = namelist3 ; * s ; s + + )
2006-03-05 13:34:15 +03:00
if ( strstr ( name , * s ) ! = NULL )
2006-02-22 23:24:50 +03:00
return 1 ;
2006-02-18 00:42:02 +03:00
return 0 ;
}
2006-01-28 19:19:35 +03:00
static void read_symbols ( char * modname )
2005-04-17 02:20:36 +04:00
{
const char * symname ;
char * version ;
2006-06-09 23:53:55 +04:00
char * license ;
2005-04-17 02:20:36 +04:00
struct module * mod ;
struct elf_info info = { } ;
Elf_Sym * sym ;
2007-02-26 17:33:52 +03:00
if ( ! parse_elf ( & info , modname ) )
return ;
2005-04-17 02:20:36 +04:00
mod = new_module ( modname ) ;
/* When there's no vmlinux, don't print warnings about
* unresolved symbols ( since there ' ll be too many ; ) */
if ( is_vmlinux ( modname ) ) {
have_vmlinux = 1 ;
mod - > skip = 1 ;
}
2006-06-09 23:53:55 +04:00
license = get_modinfo ( info . modinfo , info . modinfo_len , " license " ) ;
while ( license ) {
if ( license_is_gpl_compatible ( license ) )
mod - > gpl_compatible = 1 ;
else {
mod - > gpl_compatible = 0 ;
break ;
}
license = get_next_modinfo ( info . modinfo , info . modinfo_len ,
" license " , license ) ;
}
2005-04-17 02:20:36 +04:00
for ( sym = info . symtab_start ; sym < info . symtab_stop ; sym + + ) {
symname = info . strtab + sym - > st_name ;
handle_modversions ( mod , & info , sym , symname ) ;
handle_moddevtable ( mod , & info , sym , symname ) ;
}
2006-02-18 00:42:02 +03:00
check_sec_ref ( mod , modname , & info , init_section , init_section_ref_ok ) ;
check_sec_ref ( mod , modname , & info , exit_section , exit_section_ref_ok ) ;
2005-04-17 02:20:36 +04:00
version = get_modinfo ( info . modinfo , info . modinfo_len , " version " ) ;
if ( version )
maybe_frob_rcs_version ( modname , version , info . modinfo ,
version - ( char * ) info . hdr ) ;
if ( version | | ( all_versions & & ! is_vmlinux ( modname ) ) )
get_src_version ( modname , mod - > srcversion ,
sizeof ( mod - > srcversion ) - 1 ) ;
parse_elf_finish ( & info ) ;
/* Our trick to get versioning for struct_module - it's
* never passed as an argument to an exported function , so
* the automatic versioning doesn ' t pick it up , but it ' s really
* important anyhow */
if ( modversions )
mod - > unres = alloc_symbol ( " struct_module " , 0 , mod - > unres ) ;
}
# define SZ 500
/* We first write the generated file into memory using the
* following helper , then compare to the file on disk and
* only update the later if anything changed */
2006-01-28 19:19:35 +03:00
void __attribute__ ( ( format ( printf , 2 , 3 ) ) ) buf_printf ( struct buffer * buf ,
const char * fmt , . . . )
2005-04-17 02:20:36 +04:00
{
char tmp [ SZ ] ;
int len ;
va_list ap ;
2006-03-03 18:46:04 +03:00
2005-04-17 02:20:36 +04:00
va_start ( ap , fmt ) ;
len = vsnprintf ( tmp , SZ , fmt , ap ) ;
2006-03-17 10:04:08 +03:00
buf_write ( buf , tmp , len ) ;
2005-04-17 02:20:36 +04:00
va_end ( ap ) ;
}
2006-01-28 19:19:35 +03:00
void buf_write ( struct buffer * buf , const char * s , int len )
2005-04-17 02:20:36 +04:00
{
if ( buf - > size - buf - > pos < len ) {
2006-03-17 10:04:08 +03:00
buf - > size + = len + SZ ;
2005-04-17 02:20:36 +04:00
buf - > p = realloc ( buf - > p , buf - > size ) ;
}
strncpy ( buf - > p + buf - > pos , s , len ) ;
buf - > pos + = len ;
}
2006-07-01 13:44:23 +04:00
static void check_for_gpl_usage ( enum export exp , const char * m , const char * s )
{
const char * e = is_vmlinux ( m ) ? " " : " .ko " ;
switch ( exp ) {
case export_gpl :
fatal ( " modpost: GPL-incompatible module %s%s "
" uses GPL-only symbol '%s' \n " , m , e , s ) ;
break ;
case export_unused_gpl :
fatal ( " modpost: GPL-incompatible module %s%s "
" uses GPL-only symbol marked UNUSED '%s' \n " , m , e , s ) ;
break ;
case export_gpl_future :
warn ( " modpost: GPL-incompatible module %s%s "
" uses future GPL-only symbol '%s' \n " , m , e , s ) ;
break ;
case export_plain :
case export_unused :
case export_unknown :
/* ignore */
break ;
}
}
static void check_for_unused ( enum export exp , const char * m , const char * s )
{
const char * e = is_vmlinux ( m ) ? " " : " .ko " ;
switch ( exp ) {
case export_unused :
case export_unused_gpl :
warn ( " modpost: module %s%s "
" uses symbol '%s' marked UNUSED \n " , m , e , s ) ;
break ;
default :
/* ignore */
break ;
}
}
static void check_exports ( struct module * mod )
2006-06-09 23:53:55 +04:00
{
struct symbol * s , * exp ;
for ( s = mod - > unres ; s ; s = s - > next ) {
2006-06-10 07:45:06 +04:00
const char * basename ;
2006-06-09 23:53:55 +04:00
exp = find_symbol ( s - > name ) ;
if ( ! exp | | exp - > module = = mod )
continue ;
2006-06-10 07:45:06 +04:00
basename = strrchr ( mod - > name , ' / ' ) ;
2006-06-09 23:53:55 +04:00
if ( basename )
basename + + ;
2006-07-01 13:44:23 +04:00
else
basename = mod - > name ;
if ( ! mod - > gpl_compatible )
check_for_gpl_usage ( exp - > export , basename , exp - > name ) ;
check_for_unused ( exp - > export , basename , exp - > name ) ;
2006-06-09 23:53:55 +04:00
}
}
2006-01-28 19:19:35 +03:00
/**
* Header for the generated file
* */
static void add_header ( struct buffer * b , struct module * mod )
2005-04-17 02:20:36 +04:00
{
buf_printf ( b , " #include <linux/module.h> \n " ) ;
buf_printf ( b , " #include <linux/vermagic.h> \n " ) ;
buf_printf ( b , " #include <linux/compiler.h> \n " ) ;
buf_printf ( b , " \n " ) ;
buf_printf ( b , " MODULE_INFO(vermagic, VERMAGIC_STRING); \n " ) ;
buf_printf ( b , " \n " ) ;
buf_printf ( b , " struct module __this_module \n " ) ;
buf_printf ( b , " __attribute__((section( \" .gnu.linkonce.this_module \" ))) = { \n " ) ;
2005-09-23 08:42:11 +04:00
buf_printf ( b , " .name = KBUILD_MODNAME, \n " ) ;
2005-04-17 02:20:36 +04:00
if ( mod - > has_init )
buf_printf ( b , " .init = init_module, \n " ) ;
if ( mod - > has_cleanup )
buf_printf ( b , " #ifdef CONFIG_MODULE_UNLOAD \n "
" .exit = cleanup_module, \n "
" #endif \n " ) ;
buf_printf ( b , " }; \n " ) ;
}
2006-01-28 19:19:35 +03:00
/**
* Record CRCs for unresolved symbols
* */
2006-09-08 00:08:54 +04:00
static int add_versions ( struct buffer * b , struct module * mod )
2005-04-17 02:20:36 +04:00
{
struct symbol * s , * exp ;
2006-09-08 00:08:54 +04:00
int err = 0 ;
2005-04-17 02:20:36 +04:00
for ( s = mod - > unres ; s ; s = s - > next ) {
exp = find_symbol ( s - > name ) ;
if ( ! exp | | exp - > module = = mod ) {
2006-09-08 00:08:54 +04:00
if ( have_vmlinux & & ! s - > weak ) {
2006-10-07 15:35:32 +04:00
if ( warn_unresolved ) {
warn ( " \" %s \" [%s.ko] undefined! \n " ,
s - > name , mod - > name ) ;
} else {
merror ( " \" %s \" [%s.ko] undefined! \n " ,
s - > name , mod - > name ) ;
err = 1 ;
}
2006-09-08 00:08:54 +04:00
}
2005-04-17 02:20:36 +04:00
continue ;
}
s - > module = exp - > module ;
s - > crc_valid = exp - > crc_valid ;
s - > crc = exp - > crc ;
}
if ( ! modversions )
2006-09-08 00:08:54 +04:00
return err ;
2005-04-17 02:20:36 +04:00
buf_printf ( b , " \n " ) ;
buf_printf ( b , " static const struct modversion_info ____versions[] \n " ) ;
buf_printf ( b , " __attribute_used__ \n " ) ;
buf_printf ( b , " __attribute__((section( \" __versions \" ))) = { \n " ) ;
for ( s = mod - > unres ; s ; s = s - > next ) {
if ( ! s - > module ) {
continue ;
}
if ( ! s - > crc_valid ) {
2006-01-28 18:57:26 +03:00
warn ( " \" %s \" [%s.ko] has no CRC! \n " ,
2005-04-17 02:20:36 +04:00
s - > name , mod - > name ) ;
continue ;
}
buf_printf ( b , " \t { %#8x, \" %s \" }, \n " , s - > crc , s - > name ) ;
}
buf_printf ( b , " }; \n " ) ;
2006-09-08 00:08:54 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2006-01-28 19:19:35 +03:00
static void add_depends ( struct buffer * b , struct module * mod ,
struct module * modules )
2005-04-17 02:20:36 +04:00
{
struct symbol * s ;
struct module * m ;
int first = 1 ;
for ( m = modules ; m ; m = m - > next ) {
m - > seen = is_vmlinux ( m - > name ) ;
}
buf_printf ( b , " \n " ) ;
buf_printf ( b , " static const char __module_depends[] \n " ) ;
buf_printf ( b , " __attribute_used__ \n " ) ;
buf_printf ( b , " __attribute__((section( \" .modinfo \" ))) = \n " ) ;
buf_printf ( b , " \" depends= " ) ;
for ( s = mod - > unres ; s ; s = s - > next ) {
2007-02-26 21:46:52 +03:00
const char * p ;
2005-04-17 02:20:36 +04:00
if ( ! s - > module )
continue ;
if ( s - > module - > seen )
continue ;
s - > module - > seen = 1 ;
2007-02-26 21:46:52 +03:00
if ( ( p = strrchr ( s - > module - > name , ' / ' ) ) ! = NULL )
p + + ;
else
p = s - > module - > name ;
buf_printf ( b , " %s%s " , first ? " " : " , " , p ) ;
2005-04-17 02:20:36 +04:00
first = 0 ;
}
buf_printf ( b , " \" ; \n " ) ;
}
2006-01-28 19:19:35 +03:00
static void add_srcversion ( struct buffer * b , struct module * mod )
2005-04-17 02:20:36 +04:00
{
if ( mod - > srcversion [ 0 ] ) {
buf_printf ( b , " \n " ) ;
buf_printf ( b , " MODULE_INFO(srcversion, \" %s \" ); \n " ,
mod - > srcversion ) ;
}
}
2006-01-28 19:19:35 +03:00
static void write_if_changed ( struct buffer * b , const char * fname )
2005-04-17 02:20:36 +04:00
{
char * tmp ;
FILE * file ;
struct stat st ;
file = fopen ( fname , " r " ) ;
if ( ! file )
goto write ;
if ( fstat ( fileno ( file ) , & st ) < 0 )
goto close_write ;
if ( st . st_size ! = b - > pos )
goto close_write ;
tmp = NOFAIL ( malloc ( b - > pos ) ) ;
if ( fread ( tmp , 1 , b - > pos , file ) ! = b - > pos )
goto free_write ;
if ( memcmp ( tmp , b - > p , b - > pos ) ! = 0 )
goto free_write ;
free ( tmp ) ;
fclose ( file ) ;
return ;
free_write :
free ( tmp ) ;
close_write :
fclose ( file ) ;
write :
file = fopen ( fname , " w " ) ;
if ( ! file ) {
perror ( fname ) ;
exit ( 1 ) ;
}
if ( fwrite ( b - > p , 1 , b - > pos , file ) ! = b - > pos ) {
perror ( fname ) ;
exit ( 1 ) ;
}
fclose ( file ) ;
}
2006-06-09 09:12:53 +04:00
/* parse Module.symvers file. line format:
2006-07-01 12:10:19 +04:00
* 0x12345678 < tab > symbol < tab > module [ [ < tab > export ] < tab > something ]
2006-06-09 09:12:53 +04:00
* */
2006-01-29 00:15:55 +03:00
static void read_dump ( const char * fname , unsigned int kernel )
2005-04-17 02:20:36 +04:00
{
unsigned long size , pos = 0 ;
void * file = grab_file ( fname , & size ) ;
char * line ;
if ( ! file )
/* No symbol versions, silently ignore */
return ;
while ( ( line = get_next_line ( & pos , file , size ) ) ) {
2006-07-01 12:10:19 +04:00
char * symname , * modname , * d , * export , * end ;
2005-04-17 02:20:36 +04:00
unsigned int crc ;
struct module * mod ;
2006-01-29 00:15:55 +03:00
struct symbol * s ;
2005-04-17 02:20:36 +04:00
if ( ! ( symname = strchr ( line , ' \t ' ) ) )
goto fail ;
* symname + + = ' \0 ' ;
if ( ! ( modname = strchr ( symname , ' \t ' ) ) )
goto fail ;
* modname + + = ' \0 ' ;
2006-06-11 10:02:06 +04:00
if ( ( export = strchr ( modname , ' \t ' ) ) ! = NULL )
2006-06-09 09:12:53 +04:00
* export + + = ' \0 ' ;
2006-07-01 12:10:19 +04:00
if ( export & & ( ( end = strchr ( export , ' \t ' ) ) ! = NULL ) )
* end = ' \0 ' ;
2005-04-17 02:20:36 +04:00
crc = strtoul ( line , & d , 16 ) ;
if ( * symname = = ' \0 ' | | * modname = = ' \0 ' | | * d ! = ' \0 ' )
goto fail ;
if ( ! ( mod = find_module ( modname ) ) ) {
if ( is_vmlinux ( modname ) ) {
have_vmlinux = 1 ;
}
mod = new_module ( NOFAIL ( strdup ( modname ) ) ) ;
mod - > skip = 1 ;
}
2006-06-09 09:12:53 +04:00
s = sym_add_exported ( symname , mod , export_no ( export ) ) ;
2006-01-29 00:22:33 +03:00
s - > kernel = kernel ;
s - > preloaded = 1 ;
2006-06-09 09:12:53 +04:00
sym_update_crc ( symname , mod , crc , export_no ( export ) ) ;
2005-04-17 02:20:36 +04:00
}
return ;
fail :
fatal ( " parse error in symbol dump file \n " ) ;
}
2006-01-29 00:15:55 +03:00
/* For normal builds always dump all symbols.
* For external modules only dump symbols
* that are not read from kernel Module . symvers .
* */
static int dump_sym ( struct symbol * sym )
{
if ( ! external_module )
return 1 ;
if ( sym - > vmlinux | | sym - > kernel )
return 0 ;
return 1 ;
}
2006-03-03 18:46:04 +03:00
2006-01-28 19:19:35 +03:00
static void write_dump ( const char * fname )
2005-04-17 02:20:36 +04:00
{
struct buffer buf = { } ;
struct symbol * symbol ;
int n ;
for ( n = 0 ; n < SYMBOL_HASH_SIZE ; n + + ) {
symbol = symbolhash [ n ] ;
while ( symbol ) {
2006-01-29 00:15:55 +03:00
if ( dump_sym ( symbol ) )
2006-06-09 09:12:53 +04:00
buf_printf ( & buf , " 0x%08x \t %s \t %s \t %s \n " ,
2006-03-03 18:46:04 +03:00
symbol - > crc , symbol - > name ,
2006-06-09 09:12:53 +04:00
symbol - > module - > name ,
export_str ( symbol - > export ) ) ;
2005-04-17 02:20:36 +04:00
symbol = symbol - > next ;
}
}
write_if_changed ( & buf , fname ) ;
}
2006-01-28 19:19:35 +03:00
int main ( int argc , char * * argv )
2005-04-17 02:20:36 +04:00
{
struct module * mod ;
struct buffer buf = { } ;
char fname [ SZ ] ;
2006-01-29 00:15:55 +03:00
char * kernel_read = NULL , * module_read = NULL ;
char * dump_write = NULL ;
2005-04-17 02:20:36 +04:00
int opt ;
2006-09-08 00:08:54 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2006-09-08 00:08:54 +04:00
while ( ( opt = getopt ( argc , argv , " i:I:mo:aw " ) ) ! = - 1 ) {
2005-04-17 02:20:36 +04:00
switch ( opt ) {
case ' i ' :
2006-01-29 00:15:55 +03:00
kernel_read = optarg ;
break ;
case ' I ' :
module_read = optarg ;
external_module = 1 ;
2005-04-17 02:20:36 +04:00
break ;
case ' m ' :
modversions = 1 ;
break ;
case ' o ' :
dump_write = optarg ;
break ;
case ' a ' :
all_versions = 1 ;
break ;
2006-09-08 00:08:54 +04:00
case ' w ' :
warn_unresolved = 1 ;
break ;
2005-04-17 02:20:36 +04:00
default :
exit ( 1 ) ;
}
}
2006-01-29 00:15:55 +03:00
if ( kernel_read )
read_dump ( kernel_read , 1 ) ;
if ( module_read )
read_dump ( module_read , 0 ) ;
2005-04-17 02:20:36 +04:00
while ( optind < argc ) {
read_symbols ( argv [ optind + + ] ) ;
}
2006-06-09 23:53:55 +04:00
for ( mod = modules ; mod ; mod = mod - > next ) {
if ( mod - > skip )
continue ;
2006-07-01 13:44:23 +04:00
check_exports ( mod ) ;
2006-06-09 23:53:55 +04:00
}
2006-09-08 00:08:54 +04:00
err = 0 ;
2005-04-17 02:20:36 +04:00
for ( mod = modules ; mod ; mod = mod - > next ) {
if ( mod - > skip )
continue ;
buf . pos = 0 ;
add_header ( & buf , mod ) ;
2006-09-08 00:08:54 +04:00
err | = add_versions ( & buf , mod ) ;
2005-04-17 02:20:36 +04:00
add_depends ( & buf , mod , modules ) ;
add_moddevtable ( & buf , mod ) ;
add_srcversion ( & buf , mod ) ;
sprintf ( fname , " %s.mod.c " , mod - > name ) ;
write_if_changed ( & buf , fname ) ;
}
if ( dump_write )
write_dump ( dump_write ) ;
2006-09-08 00:08:54 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}