2022-03-22 14:03:39 +00:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Module kallsyms support
*
* Copyright ( C ) 2010 Rusty Russell
*/
# include <linux/module.h>
# include <linux/kallsyms.h>
# include <linux/buildid.h>
# include <linux/bsearch.h>
# include "internal.h"
/* Lookup exported symbol in given range of kernel_symbols */
static const struct kernel_symbol * lookup_exported_symbol ( const char * name ,
const struct kernel_symbol * start ,
const struct kernel_symbol * stop )
{
return bsearch ( name , start , stop - start ,
sizeof ( struct kernel_symbol ) , cmp_name ) ;
}
static int is_exported ( const char * name , unsigned long value ,
const struct module * mod )
{
const struct kernel_symbol * ks ;
if ( ! mod )
ks = lookup_exported_symbol ( name , __start___ksymtab , __stop___ksymtab ) ;
else
ks = lookup_exported_symbol ( name , mod - > syms , mod - > syms + mod - > num_syms ) ;
return ks & & kernel_symbol_value ( ks ) = = value ;
}
/* As per nm */
static char elf_type ( const Elf_Sym * sym , const struct load_info * info )
{
const Elf_Shdr * sechdrs = info - > sechdrs ;
if ( ELF_ST_BIND ( sym - > st_info ) = = STB_WEAK ) {
if ( ELF_ST_TYPE ( sym - > st_info ) = = STT_OBJECT )
return ' v ' ;
else
return ' w ' ;
}
if ( sym - > st_shndx = = SHN_UNDEF )
return ' U ' ;
if ( sym - > st_shndx = = SHN_ABS | | sym - > st_shndx = = info - > index . pcpu )
return ' a ' ;
if ( sym - > st_shndx > = SHN_LORESERVE )
return ' ? ' ;
if ( sechdrs [ sym - > st_shndx ] . sh_flags & SHF_EXECINSTR )
return ' t ' ;
if ( sechdrs [ sym - > st_shndx ] . sh_flags & SHF_ALLOC & &
sechdrs [ sym - > st_shndx ] . sh_type ! = SHT_NOBITS ) {
if ( ! ( sechdrs [ sym - > st_shndx ] . sh_flags & SHF_WRITE ) )
return ' r ' ;
else if ( sechdrs [ sym - > st_shndx ] . sh_flags & ARCH_SHF_SMALL )
return ' g ' ;
else
return ' d ' ;
}
if ( sechdrs [ sym - > st_shndx ] . sh_type = = SHT_NOBITS ) {
if ( sechdrs [ sym - > st_shndx ] . sh_flags & ARCH_SHF_SMALL )
return ' s ' ;
else
return ' b ' ;
}
if ( strstarts ( info - > secstrings + sechdrs [ sym - > st_shndx ] . sh_name ,
" .debug " ) ) {
return ' n ' ;
}
return ' ? ' ;
}
static bool is_core_symbol ( const Elf_Sym * src , const Elf_Shdr * sechdrs ,
unsigned int shnum , unsigned int pcpundx )
{
const Elf_Shdr * sec ;
if ( src - > st_shndx = = SHN_UNDEF | |
src - > st_shndx > = shnum | |
! src - > st_name )
return false ;
# ifdef CONFIG_KALLSYMS_ALL
if ( src - > st_shndx = = pcpundx )
return true ;
# endif
sec = sechdrs + src - > st_shndx ;
if ( ! ( sec - > sh_flags & SHF_ALLOC )
# ifndef CONFIG_KALLSYMS_ALL
| | ! ( sec - > sh_flags & SHF_EXECINSTR )
# endif
| | ( sec - > sh_entsize & INIT_OFFSET_MASK ) )
return false ;
return true ;
}
/*
* We only allocate and copy the strings needed by the parts of symtab
* we keep . This is simple , but has the effect of making multiple
* copies of duplicates . We could be more sophisticated , see
* linux - kernel thread starting with
* < 73 defb5e4bca04a6431392cc341112b1 @ localhost > .
*/
void layout_symtab ( struct module * mod , struct load_info * info )
{
Elf_Shdr * symsect = info - > sechdrs + info - > index . sym ;
Elf_Shdr * strsect = info - > sechdrs + info - > index . str ;
const Elf_Sym * src ;
unsigned int i , nsrc , ndst , strtab_size = 0 ;
/* Put symbol section at end of init part of module. */
symsect - > sh_flags | = SHF_ALLOC ;
symsect - > sh_entsize = module_get_offset ( mod , & mod - > init_layout . size , symsect ,
info - > index . sym ) | INIT_OFFSET_MASK ;
pr_debug ( " \t %s \n " , info - > secstrings + symsect - > sh_name ) ;
src = ( void * ) info - > hdr + symsect - > sh_offset ;
nsrc = symsect - > sh_size / sizeof ( * src ) ;
/* Compute total space required for the core symbols' strtab. */
for ( ndst = i = 0 ; i < nsrc ; i + + ) {
if ( i = = 0 | | is_livepatch_module ( mod ) | |
is_core_symbol ( src + i , info - > sechdrs , info - > hdr - > e_shnum ,
info - > index . pcpu ) ) {
strtab_size + = strlen ( & info - > strtab [ src [ i ] . st_name ] ) + 1 ;
ndst + + ;
}
}
/* Append room for core symbols at end of core part. */
2022-02-23 13:02:13 +01:00
info - > symoffs = ALIGN ( mod - > data_layout . size , symsect - > sh_addralign ? : 1 ) ;
info - > stroffs = mod - > data_layout . size = info - > symoffs + ndst * sizeof ( Elf_Sym ) ;
mod - > data_layout . size + = strtab_size ;
2022-07-01 12:44:03 +03:00
/* Note add_kallsyms() computes strtab_size as core_typeoffs - stroffs */
2022-02-23 13:02:13 +01:00
info - > core_typeoffs = mod - > data_layout . size ;
mod - > data_layout . size + = ndst * sizeof ( char ) ;
mod - > data_layout . size = strict_align ( mod - > data_layout . size ) ;
2022-03-22 14:03:39 +00:00
/* Put string table section at end of init part of module. */
strsect - > sh_flags | = SHF_ALLOC ;
strsect - > sh_entsize = module_get_offset ( mod , & mod - > init_layout . size , strsect ,
info - > index . str ) | INIT_OFFSET_MASK ;
pr_debug ( " \t %s \n " , info - > secstrings + strsect - > sh_name ) ;
/* We'll tack temporary mod_kallsyms on the end. */
mod - > init_layout . size = ALIGN ( mod - > init_layout . size ,
__alignof__ ( struct mod_kallsyms ) ) ;
info - > mod_kallsyms_init_off = mod - > init_layout . size ;
mod - > init_layout . size + = sizeof ( struct mod_kallsyms ) ;
info - > init_typeoffs = mod - > init_layout . size ;
mod - > init_layout . size + = nsrc * sizeof ( char ) ;
2022-02-23 10:01:01 +01:00
mod - > init_layout . size = strict_align ( mod - > init_layout . size ) ;
2022-03-22 14:03:39 +00:00
}
/*
* We use the full symtab and strtab which layout_symtab arranged to
* be appended to the init section . Later we switch to the cut - down
* core - only ones .
*/
void add_kallsyms ( struct module * mod , const struct load_info * info )
{
unsigned int i , ndst ;
const Elf_Sym * src ;
Elf_Sym * dst ;
char * s ;
Elf_Shdr * symsec = & info - > sechdrs [ info - > index . sym ] ;
2022-07-01 12:44:03 +03:00
unsigned long strtab_size ;
2022-03-22 14:03:39 +00:00
/* Set up to point into init section. */
2022-03-22 14:03:40 +00:00
mod - > kallsyms = ( void __rcu * ) mod - > init_layout . base +
info - > mod_kallsyms_init_off ;
2022-03-22 14:03:39 +00:00
2022-07-11 18:17:19 +01:00
rcu_read_lock ( ) ;
2022-03-22 14:03:39 +00:00
/* The following is safe since this pointer cannot change */
2022-07-11 18:17:19 +01:00
rcu_dereference ( mod - > kallsyms ) - > symtab = ( void * ) symsec - > sh_addr ;
rcu_dereference ( mod - > kallsyms ) - > num_symtab = symsec - > sh_size / sizeof ( Elf_Sym ) ;
2022-03-22 14:03:39 +00:00
/* Make sure we get permanent strtab: don't use info->strtab. */
2022-07-11 18:17:19 +01:00
rcu_dereference ( mod - > kallsyms ) - > strtab =
2022-03-22 14:03:40 +00:00
( void * ) info - > sechdrs [ info - > index . str ] . sh_addr ;
2022-07-11 18:17:19 +01:00
rcu_dereference ( mod - > kallsyms ) - > typetab = mod - > init_layout . base + info - > init_typeoffs ;
2022-03-22 14:03:39 +00:00
/*
* Now populate the cut down core kallsyms for after init
* and set types up while we still have access to sections .
*/
2022-02-23 13:02:13 +01:00
mod - > core_kallsyms . symtab = dst = mod - > data_layout . base + info - > symoffs ;
mod - > core_kallsyms . strtab = s = mod - > data_layout . base + info - > stroffs ;
mod - > core_kallsyms . typetab = mod - > data_layout . base + info - > core_typeoffs ;
2022-07-01 12:44:03 +03:00
strtab_size = info - > core_typeoffs - info - > stroffs ;
2022-07-11 18:17:19 +01:00
src = rcu_dereference ( mod - > kallsyms ) - > symtab ;
for ( ndst = i = 0 ; i < rcu_dereference ( mod - > kallsyms ) - > num_symtab ; i + + ) {
rcu_dereference ( mod - > kallsyms ) - > typetab [ i ] = elf_type ( src + i , info ) ;
2022-03-22 14:03:39 +00:00
if ( i = = 0 | | is_livepatch_module ( mod ) | |
is_core_symbol ( src + i , info - > sechdrs , info - > hdr - > e_shnum ,
info - > index . pcpu ) ) {
2022-07-01 12:44:03 +03:00
ssize_t ret ;
2022-03-22 14:03:39 +00:00
mod - > core_kallsyms . typetab [ ndst ] =
2022-07-11 18:17:19 +01:00
rcu_dereference ( mod - > kallsyms ) - > typetab [ i ] ;
2022-03-22 14:03:39 +00:00
dst [ ndst ] = src [ i ] ;
dst [ ndst + + ] . st_name = s - mod - > core_kallsyms . strtab ;
2022-07-01 12:44:03 +03:00
ret = strscpy ( s ,
2022-07-11 18:17:19 +01:00
& rcu_dereference ( mod - > kallsyms ) - > strtab [ src [ i ] . st_name ] ,
2022-07-01 12:44:03 +03:00
strtab_size ) ;
if ( ret < 0 )
break ;
s + = ret + 1 ;
strtab_size - = ret + 1 ;
2022-03-22 14:03:39 +00:00
}
}
2022-07-11 18:17:19 +01:00
rcu_read_unlock ( ) ;
2022-03-22 14:03:39 +00:00
mod - > core_kallsyms . num_symtab = ndst ;
}
# if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
void init_build_id ( struct module * mod , const struct load_info * info )
{
const Elf_Shdr * sechdr ;
unsigned int i ;
for ( i = 0 ; i < info - > hdr - > e_shnum ; i + + ) {
sechdr = & info - > sechdrs [ i ] ;
if ( ! sect_empty ( sechdr ) & & sechdr - > sh_type = = SHT_NOTE & &
! build_id_parse_buf ( ( void * ) sechdr - > sh_addr , mod - > build_id ,
sechdr - > sh_size ) )
break ;
}
}
# else
void init_build_id ( struct module * mod , const struct load_info * info )
{
}
# endif
/*
* This ignores the intensely annoying " mapping symbols " found
* in ARM ELF files : $ a , $ t and $ d .
*/
static inline int is_arm_mapping_symbol ( const char * str )
{
if ( str [ 0 ] = = ' . ' & & str [ 1 ] = = ' L ' )
return true ;
return str [ 0 ] = = ' $ ' & & strchr ( " axtd " , str [ 1 ] ) & &
( str [ 2 ] = = ' \0 ' | | str [ 2 ] = = ' . ' ) ;
}
static const char * kallsyms_symbol_name ( struct mod_kallsyms * kallsyms , unsigned int symnum )
{
return kallsyms - > strtab + kallsyms - > symtab [ symnum ] . st_name ;
}
/*
* Given a module and address , find the corresponding symbol and return its name
* while providing its size and offset if needed .
*/
static const char * find_kallsyms_symbol ( struct module * mod ,
unsigned long addr ,
unsigned long * size ,
unsigned long * offset )
{
unsigned int i , best = 0 ;
unsigned long nextval , bestval ;
struct mod_kallsyms * kallsyms = rcu_dereference_sched ( mod - > kallsyms ) ;
/* At worse, next value is at end of module */
if ( within_module_init ( addr , mod ) )
nextval = ( unsigned long ) mod - > init_layout . base + mod - > init_layout . text_size ;
else
nextval = ( unsigned long ) mod - > core_layout . base + mod - > core_layout . text_size ;
bestval = kallsyms_symbol_value ( & kallsyms - > symtab [ best ] ) ;
/*
* Scan for closest preceding symbol , and next symbol . ( ELF
* starts real symbols at 1 ) .
*/
for ( i = 1 ; i < kallsyms - > num_symtab ; i + + ) {
const Elf_Sym * sym = & kallsyms - > symtab [ i ] ;
unsigned long thisval = kallsyms_symbol_value ( sym ) ;
if ( sym - > st_shndx = = SHN_UNDEF )
continue ;
/*
* We ignore unnamed symbols : they ' re uninformative
* and inserted at a whim .
*/
if ( * kallsyms_symbol_name ( kallsyms , i ) = = ' \0 ' | |
is_arm_mapping_symbol ( kallsyms_symbol_name ( kallsyms , i ) ) )
continue ;
if ( thisval < = addr & & thisval > bestval ) {
best = i ;
bestval = thisval ;
}
if ( thisval > addr & & thisval < nextval )
nextval = thisval ;
}
if ( ! best )
return NULL ;
if ( size )
* size = nextval - bestval ;
if ( offset )
* offset = addr - bestval ;
return kallsyms_symbol_name ( kallsyms , best ) ;
}
void * __weak dereference_module_function_descriptor ( struct module * mod ,
void * ptr )
{
return ptr ;
}
/*
* For kallsyms to ask for address resolution . NULL means not found . Careful
* not to lock to avoid deadlock on oopses , simply disable preemption .
*/
const char * module_address_lookup ( unsigned long addr ,
unsigned long * size ,
unsigned long * offset ,
char * * modname ,
const unsigned char * * modbuildid ,
char * namebuf )
{
const char * ret = NULL ;
struct module * mod ;
preempt_disable ( ) ;
mod = __module_address ( addr ) ;
if ( mod ) {
if ( modname )
* modname = mod - > name ;
if ( modbuildid ) {
# if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
* modbuildid = mod - > build_id ;
# else
* modbuildid = NULL ;
# endif
}
ret = find_kallsyms_symbol ( mod , addr , size , offset ) ;
}
/* Make a copy in here where it's safe */
if ( ret ) {
strncpy ( namebuf , ret , KSYM_NAME_LEN - 1 ) ;
ret = namebuf ;
}
preempt_enable ( ) ;
return ret ;
}
int lookup_module_symbol_name ( unsigned long addr , char * symname )
{
struct module * mod ;
preempt_disable ( ) ;
list_for_each_entry_rcu ( mod , & modules , list ) {
if ( mod - > state = = MODULE_STATE_UNFORMED )
continue ;
if ( within_module ( addr , mod ) ) {
const char * sym ;
sym = find_kallsyms_symbol ( mod , addr , NULL , NULL ) ;
if ( ! sym )
goto out ;
strscpy ( symname , sym , KSYM_NAME_LEN ) ;
preempt_enable ( ) ;
return 0 ;
}
}
out :
preempt_enable ( ) ;
return - ERANGE ;
}
int lookup_module_symbol_attrs ( unsigned long addr , unsigned long * size ,
unsigned long * offset , char * modname , char * name )
{
struct module * mod ;
preempt_disable ( ) ;
list_for_each_entry_rcu ( mod , & modules , list ) {
if ( mod - > state = = MODULE_STATE_UNFORMED )
continue ;
if ( within_module ( addr , mod ) ) {
const char * sym ;
sym = find_kallsyms_symbol ( mod , addr , size , offset ) ;
if ( ! sym )
goto out ;
if ( modname )
strscpy ( modname , mod - > name , MODULE_NAME_LEN ) ;
if ( name )
strscpy ( name , sym , KSYM_NAME_LEN ) ;
preempt_enable ( ) ;
return 0 ;
}
}
out :
preempt_enable ( ) ;
return - ERANGE ;
}
int module_get_kallsym ( unsigned int symnum , unsigned long * value , char * type ,
char * name , char * module_name , int * exported )
{
struct module * mod ;
preempt_disable ( ) ;
list_for_each_entry_rcu ( mod , & modules , list ) {
struct mod_kallsyms * kallsyms ;
if ( mod - > state = = MODULE_STATE_UNFORMED )
continue ;
kallsyms = rcu_dereference_sched ( mod - > kallsyms ) ;
if ( symnum < kallsyms - > num_symtab ) {
const Elf_Sym * sym = & kallsyms - > symtab [ symnum ] ;
* value = kallsyms_symbol_value ( sym ) ;
* type = kallsyms - > typetab [ symnum ] ;
strscpy ( name , kallsyms_symbol_name ( kallsyms , symnum ) , KSYM_NAME_LEN ) ;
strscpy ( module_name , mod - > name , MODULE_NAME_LEN ) ;
* exported = is_exported ( name , * value , mod ) ;
preempt_enable ( ) ;
return 0 ;
}
symnum - = kallsyms - > num_symtab ;
}
preempt_enable ( ) ;
return - ERANGE ;
}
/* Given a module and name of symbol, find and return the symbol's value */
unsigned long find_kallsyms_symbol_value ( struct module * mod , const char * name )
{
unsigned int i ;
struct mod_kallsyms * kallsyms = rcu_dereference_sched ( mod - > kallsyms ) ;
for ( i = 0 ; i < kallsyms - > num_symtab ; i + + ) {
const Elf_Sym * sym = & kallsyms - > symtab [ i ] ;
if ( strcmp ( name , kallsyms_symbol_name ( kallsyms , i ) ) = = 0 & &
sym - > st_shndx ! = SHN_UNDEF )
return kallsyms_symbol_value ( sym ) ;
}
return 0 ;
}
2022-06-13 08:02:02 +02:00
static unsigned long __module_kallsyms_lookup_name ( const char * name )
2022-03-22 14:03:39 +00:00
{
struct module * mod ;
char * colon ;
2022-06-13 08:02:01 +02:00
colon = strnchr ( name , MODULE_NAME_LEN , ' : ' ) ;
if ( colon ) {
mod = find_module_all ( name , colon - name , false ) ;
if ( mod )
2022-06-13 08:02:02 +02:00
return find_kallsyms_symbol_value ( mod , colon + 1 ) ;
return 0 ;
}
list_for_each_entry_rcu ( mod , & modules , list ) {
unsigned long ret ;
if ( mod - > state = = MODULE_STATE_UNFORMED )
continue ;
ret = find_kallsyms_symbol_value ( mod , name ) ;
if ( ret )
return ret ;
2022-03-22 14:03:39 +00:00
}
2022-06-13 08:02:02 +02:00
return 0 ;
}
/* Look for this name: can be of form module:name. */
unsigned long module_kallsyms_lookup_name ( const char * name )
{
unsigned long ret ;
/* Don't lock: we're in enough trouble already. */
preempt_disable ( ) ;
ret = __module_kallsyms_lookup_name ( name ) ;
2022-03-22 14:03:39 +00:00
preempt_enable ( ) ;
return ret ;
}
2023-01-16 11:10:07 +01:00
int module_kallsyms_on_each_symbol ( const char * modname ,
int ( * fn ) ( void * , const char * ,
2022-03-22 14:03:39 +00:00
struct module * , unsigned long ) ,
void * data )
{
struct module * mod ;
unsigned int i ;
int ret = 0 ;
mutex_lock ( & module_mutex ) ;
list_for_each_entry ( mod , & modules , list ) {
2022-03-22 14:03:40 +00:00
struct mod_kallsyms * kallsyms ;
2022-03-22 14:03:39 +00:00
if ( mod - > state = = MODULE_STATE_UNFORMED )
continue ;
2022-03-22 14:03:40 +00:00
2023-01-16 11:10:07 +01:00
if ( modname & & strcmp ( modname , mod - > name ) )
continue ;
2022-03-22 14:03:40 +00:00
/* Use rcu_dereference_sched() to remain compliant with the sparse tool */
preempt_disable ( ) ;
kallsyms = rcu_dereference_sched ( mod - > kallsyms ) ;
preempt_enable ( ) ;
2022-03-22 14:03:39 +00:00
for ( i = 0 ; i < kallsyms - > num_symtab ; i + + ) {
const Elf_Sym * sym = & kallsyms - > symtab [ i ] ;
if ( sym - > st_shndx = = SHN_UNDEF )
continue ;
ret = fn ( data , kallsyms_symbol_name ( kallsyms , i ) ,
mod , kallsyms_symbol_value ( sym ) ) ;
if ( ret ! = 0 )
goto out ;
}
2023-01-16 11:10:07 +01:00
/*
* The given module is found , the subsequent modules do not
* need to be compared .
*/
if ( modname )
break ;
2022-03-22 14:03:39 +00:00
}
out :
mutex_unlock ( & module_mutex ) ;
return ret ;
}