2005-04-17 02:20:36 +04:00
/*
* kallsyms . c : in - kernel printing of symbolic oopses and stack traces .
*
* Rewritten and vastly simplified by Rusty Russell for in - kernel
* module loader :
* Copyright 2002 Rusty Russell < rusty @ rustcorp . com . au > IBM Corporation
*
* ChangeLog :
*
* ( 25 / Aug / 2004 ) Paulo Marques < pmarques @ grupopie . com >
* Changed the compression method from stem compression to " table lookup "
* compression ( see scripts / kallsyms . c for a more complete description )
*/
# include <linux/kallsyms.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/seq_file.h>
# include <linux/fs.h>
# include <linux/err.h>
# include <linux/proc_fs.h>
2005-10-31 02:03:48 +03:00
# include <linux/sched.h> /* for cond_resched */
2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
2006-12-07 07:35:30 +03:00
# include <linux/ctype.h>
2005-04-17 02:20:36 +04:00
# include <asm/sections.h>
# ifdef CONFIG_KALLSYMS_ALL
# define all_var 1
# else
# define all_var 0
# endif
2009-05-13 00:43:35 +04:00
/*
* These will be re - linked against their real values
* during the second link stage .
*/
2009-01-14 23:38:20 +03:00
extern const unsigned long kallsyms_addresses [ ] __attribute__ ( ( weak ) ) ;
extern const u8 kallsyms_names [ ] __attribute__ ( ( weak ) ) ;
2005-04-17 02:20:36 +04:00
2009-05-13 00:43:35 +04:00
/*
* Tell the compiler that the count isn ' t in the small data section if the arch
* has one ( eg : FRV ) .
2007-11-29 03:22:04 +03:00
*/
extern const unsigned long kallsyms_num_syms
2009-01-14 23:38:20 +03:00
__attribute__ ( ( weak , section ( " .rodata " ) ) ) ;
2007-11-29 03:22:04 +03:00
2009-01-14 23:38:20 +03:00
extern const u8 kallsyms_token_table [ ] __attribute__ ( ( weak ) ) ;
extern const u16 kallsyms_token_index [ ] __attribute__ ( ( weak ) ) ;
2005-04-17 02:20:36 +04:00
2009-01-14 23:38:20 +03:00
extern const unsigned long kallsyms_markers [ ] __attribute__ ( ( weak ) ) ;
2005-04-17 02:20:36 +04:00
static inline int is_kernel_inittext ( unsigned long addr )
{
if ( addr > = ( unsigned long ) _sinittext
& & addr < = ( unsigned long ) _einittext )
return 1 ;
return 0 ;
}
static inline int is_kernel_text ( unsigned long addr )
{
2009-09-23 03:44:15 +04:00
if ( ( addr > = ( unsigned long ) _stext & & addr < = ( unsigned long ) _etext ) | |
arch_is_kernel_text ( addr ) )
2005-04-17 02:20:36 +04:00
return 1 ;
return in_gate_area_no_task ( addr ) ;
}
static inline int is_kernel ( unsigned long addr )
{
if ( addr > = ( unsigned long ) _stext & & addr < = ( unsigned long ) _end )
return 1 ;
return in_gate_area_no_task ( addr ) ;
}
2006-10-03 12:13:48 +04:00
static int is_ksym_addr ( unsigned long addr )
{
if ( all_var )
return is_kernel ( addr ) ;
2008-02-06 12:36:26 +03:00
return is_kernel_text ( addr ) | | is_kernel_inittext ( addr ) ;
2006-10-03 12:13:48 +04:00
}
2009-05-13 00:43:35 +04:00
/*
* Expand a compressed symbol data into the resulting uncompressed string ,
* given the offset to where the symbol is in the compressed stream .
*/
2005-04-17 02:20:36 +04:00
static unsigned int kallsyms_expand_symbol ( unsigned int off , char * result )
{
int len , skipped_first = 0 ;
2006-12-08 13:35:57 +03:00
const u8 * tptr , * data ;
2005-04-17 02:20:36 +04:00
2009-05-13 00:43:35 +04:00
/* Get the compressed symbol length from the first symbol byte. */
2005-04-17 02:20:36 +04:00
data = & kallsyms_names [ off ] ;
len = * data ;
data + + ;
2009-05-13 00:43:35 +04:00
/*
* Update the offset to return the offset for the next symbol on
* the compressed stream .
*/
2005-04-17 02:20:36 +04:00
off + = len + 1 ;
2009-05-13 00:43:35 +04:00
/*
* For every byte on the compressed symbol data , copy the table
* entry for that byte .
*/
while ( len ) {
tptr = & kallsyms_token_table [ kallsyms_token_index [ * data ] ] ;
2005-04-17 02:20:36 +04:00
data + + ;
len - - ;
while ( * tptr ) {
2009-05-13 00:43:35 +04:00
if ( skipped_first ) {
2005-04-17 02:20:36 +04:00
* result = * tptr ;
result + + ;
} else
skipped_first = 1 ;
tptr + + ;
}
}
* result = ' \0 ' ;
2009-05-13 00:43:35 +04:00
/* Return to offset to the next symbol. */
2005-04-17 02:20:36 +04:00
return off ;
}
2009-05-13 00:43:35 +04:00
/*
* Get symbol type information . This is encoded as a single char at the
* beginning of the symbol name .
*/
2005-04-17 02:20:36 +04:00
static char kallsyms_get_symbol_type ( unsigned int off )
{
2009-05-13 00:43:35 +04:00
/*
* Get just the first code , look it up in the token table ,
* and return the first char from this token .
*/
return kallsyms_token_table [ kallsyms_token_index [ kallsyms_names [ off + 1 ] ] ] ;
2005-04-17 02:20:36 +04:00
}
2009-05-13 00:43:35 +04:00
/*
* Find the offset on the compressed stream given and index in the
* kallsyms array .
*/
2005-04-17 02:20:36 +04:00
static unsigned int get_symbol_offset ( unsigned long pos )
{
2006-12-08 13:35:57 +03:00
const u8 * name ;
2005-04-17 02:20:36 +04:00
int i ;
2009-05-13 00:43:35 +04:00
/*
* Use the closest marker we have . We have markers every 256 positions ,
* so that should be close enough .
*/
name = & kallsyms_names [ kallsyms_markers [ pos > > 8 ] ] ;
2005-04-17 02:20:36 +04:00
2009-05-13 00:43:35 +04:00
/*
* Sequentially scan all the symbols up to the point we ' re searching
* for . Every symbol is stored in a [ < len > ] [ < len > bytes of data ] format ,
* so we just need to add the len to the current pointer for every
* symbol we wish to skip .
*/
for ( i = 0 ; i < ( pos & 0xFF ) ; i + + )
2005-04-17 02:20:36 +04:00
name = name + ( * name ) + 1 ;
return name - kallsyms_names ;
}
/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name ( const char * name )
{
2007-07-17 15:03:51 +04:00
char namebuf [ KSYM_NAME_LEN ] ;
2005-04-17 02:20:36 +04:00
unsigned long i ;
unsigned int off ;
for ( i = 0 , off = 0 ; i < kallsyms_num_syms ; i + + ) {
off = kallsyms_expand_symbol ( off , namebuf ) ;
if ( strcmp ( namebuf , name ) = = 0 )
return kallsyms_addresses [ i ] ;
}
return module_kallsyms_lookup_name ( name ) ;
}
2008-12-06 03:03:58 +03:00
int kallsyms_on_each_symbol ( int ( * fn ) ( void * , const char * , struct module * ,
unsigned long ) ,
void * data )
{
char namebuf [ KSYM_NAME_LEN ] ;
unsigned long i ;
unsigned int off ;
int ret ;
for ( i = 0 , off = 0 ; i < kallsyms_num_syms ; i + + ) {
off = kallsyms_expand_symbol ( off , namebuf ) ;
ret = fn ( data , namebuf , NULL , kallsyms_addresses [ i ] ) ;
if ( ret ! = 0 )
return ret ;
}
return module_kallsyms_on_each_symbol ( fn , data ) ;
}
EXPORT_SYMBOL_GPL ( kallsyms_on_each_symbol ) ;
2006-10-03 12:13:48 +04:00
static unsigned long get_symbol_pos ( unsigned long addr ,
unsigned long * symbolsize ,
unsigned long * offset )
{
unsigned long symbol_start = 0 , symbol_end = 0 ;
unsigned long i , low , high , mid ;
2009-01-14 23:38:20 +03:00
/* This kernel should never had been booted. */
BUG_ON ( ! kallsyms_addresses ) ;
2009-05-13 00:43:35 +04:00
/* Do a binary search on the sorted kallsyms_addresses array. */
2006-10-03 12:13:48 +04:00
low = 0 ;
high = kallsyms_num_syms ;
while ( high - low > 1 ) {
2008-07-25 12:45:34 +04:00
mid = low + ( high - low ) / 2 ;
2006-10-03 12:13:48 +04:00
if ( kallsyms_addresses [ mid ] < = addr )
low = mid ;
else
high = mid ;
}
/*
2009-05-13 00:43:35 +04:00
* Search for the first aliased symbol . Aliased
* symbols are symbols with the same address .
2006-10-03 12:13:48 +04:00
*/
while ( low & & kallsyms_addresses [ low - 1 ] = = kallsyms_addresses [ low ] )
- - low ;
symbol_start = kallsyms_addresses [ low ] ;
2009-05-13 00:43:35 +04:00
/* Search for next non-aliased symbol. */
2006-10-03 12:13:48 +04:00
for ( i = low + 1 ; i < kallsyms_num_syms ; i + + ) {
if ( kallsyms_addresses [ i ] > symbol_start ) {
symbol_end = kallsyms_addresses [ i ] ;
break ;
}
}
2009-05-13 00:43:35 +04:00
/* If we found no next symbol, we use the end of the section. */
2006-10-03 12:13:48 +04:00
if ( ! symbol_end ) {
if ( is_kernel_inittext ( addr ) )
symbol_end = ( unsigned long ) _einittext ;
else if ( all_var )
symbol_end = ( unsigned long ) _end ;
else
symbol_end = ( unsigned long ) _etext ;
}
2007-05-08 11:28:41 +04:00
if ( symbolsize )
* symbolsize = symbol_end - symbol_start ;
if ( offset )
* offset = addr - symbol_start ;
2006-10-03 12:13:48 +04:00
return low ;
}
/*
* Lookup an address but don ' t bother to find any names .
*/
int kallsyms_lookup_size_offset ( unsigned long addr , unsigned long * symbolsize ,
unsigned long * offset )
{
2008-01-30 01:13:22 +03:00
char namebuf [ KSYM_NAME_LEN ] ;
2006-10-03 12:13:48 +04:00
if ( is_ksym_addr ( addr ) )
return ! ! get_symbol_pos ( addr , symbolsize , offset ) ;
2008-01-30 01:13:22 +03:00
return ! ! module_address_lookup ( addr , symbolsize , offset , NULL , namebuf ) ;
2006-10-03 12:13:48 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Lookup an address
2009-05-13 00:43:35 +04:00
* - modname is set to NULL if it ' s in the kernel .
* - We guarantee that the returned name is valid until we reschedule even if .
* It resides in a module .
* - We also guarantee that modname will be valid until rescheduled .
2005-04-17 02:20:36 +04:00
*/
const char * kallsyms_lookup ( unsigned long addr ,
unsigned long * symbolsize ,
unsigned long * offset ,
char * * modname , char * namebuf )
{
2007-07-17 15:03:51 +04:00
namebuf [ KSYM_NAME_LEN - 1 ] = 0 ;
2005-04-17 02:20:36 +04:00
namebuf [ 0 ] = 0 ;
2006-10-03 12:13:48 +04:00
if ( is_ksym_addr ( addr ) ) {
unsigned long pos ;
2005-04-17 02:20:36 +04:00
2006-10-03 12:13:48 +04:00
pos = get_symbol_pos ( addr , symbolsize , offset ) ;
2005-04-17 02:20:36 +04:00
/* Grab name */
2006-10-03 12:13:48 +04:00
kallsyms_expand_symbol ( get_symbol_offset ( pos ) , namebuf ) ;
2007-05-30 10:43:16 +04:00
if ( modname )
* modname = NULL ;
2005-04-17 02:20:36 +04:00
return namebuf ;
}
2009-05-13 00:43:35 +04:00
/* See if it's in a module. */
2008-01-30 01:13:22 +03:00
return module_address_lookup ( addr , symbolsize , offset , modname ,
namebuf ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 11:28:43 +04:00
int lookup_symbol_name ( unsigned long addr , char * symname )
{
symname [ 0 ] = ' \0 ' ;
2007-07-17 15:03:51 +04:00
symname [ KSYM_NAME_LEN - 1 ] = ' \0 ' ;
2007-05-08 11:28:43 +04:00
if ( is_ksym_addr ( addr ) ) {
unsigned long pos ;
pos = get_symbol_pos ( addr , NULL , NULL ) ;
/* Grab name */
kallsyms_expand_symbol ( get_symbol_offset ( pos ) , symname ) ;
return 0 ;
}
2009-05-13 00:43:35 +04:00
/* See if it's in a module. */
2007-05-08 11:28:43 +04:00
return lookup_module_symbol_name ( addr , symname ) ;
}
2007-05-08 11:28:47 +04:00
int lookup_symbol_attrs ( unsigned long addr , unsigned long * size ,
unsigned long * offset , char * modname , char * name )
{
name [ 0 ] = ' \0 ' ;
2007-07-17 15:03:51 +04:00
name [ KSYM_NAME_LEN - 1 ] = ' \0 ' ;
2007-05-08 11:28:47 +04:00
if ( is_ksym_addr ( addr ) ) {
unsigned long pos ;
pos = get_symbol_pos ( addr , size , offset ) ;
/* Grab name */
kallsyms_expand_symbol ( get_symbol_offset ( pos ) , name ) ;
modname [ 0 ] = ' \0 ' ;
return 0 ;
}
2009-05-13 00:43:35 +04:00
/* See if it's in a module. */
2007-05-08 11:28:47 +04:00
return lookup_module_symbol_attrs ( addr , size , offset , modname , name ) ;
}
2007-05-01 02:09:48 +04:00
/* Look up a kernel symbol and return it in a text buffer. */
int sprint_symbol ( char * buffer , unsigned long address )
2005-04-17 02:20:36 +04:00
{
char * modname ;
const char * name ;
unsigned long offset , size ;
2008-11-20 02:36:36 +03:00
int len ;
2005-04-17 02:20:36 +04:00
2008-11-20 02:36:36 +03:00
name = kallsyms_lookup ( address , & size , & offset , & modname , buffer ) ;
2005-04-17 02:20:36 +04:00
if ( ! name )
2007-05-01 02:09:48 +04:00
return sprintf ( buffer , " 0x%lx " , address ) ;
2007-07-16 10:41:24 +04:00
2008-11-20 02:36:36 +03:00
if ( name ! = buffer )
strcpy ( buffer , name ) ;
len = strlen ( buffer ) ;
buffer + = len ;
2007-07-16 10:41:24 +04:00
if ( modname )
2008-11-20 02:36:36 +03:00
len + = sprintf ( buffer , " +%#lx/%#lx [%s] " ,
offset , size , modname ) ;
2007-07-16 10:41:24 +04:00
else
2008-11-20 02:36:36 +03:00
len + = sprintf ( buffer , " +%#lx/%#lx " , offset , size ) ;
return len ;
2007-05-01 02:09:48 +04:00
}
2009-05-13 00:43:35 +04:00
EXPORT_SYMBOL_GPL ( sprint_symbol ) ;
2007-05-01 02:09:48 +04:00
/* Look up a kernel symbol and print it to the kernel messages. */
void __print_symbol ( const char * fmt , unsigned long address )
{
char buffer [ KSYM_SYMBOL_LEN ] ;
sprint_symbol ( buffer , address ) ;
2005-04-17 02:20:36 +04:00
printk ( fmt , buffer ) ;
}
2009-05-13 00:43:35 +04:00
EXPORT_SYMBOL ( __print_symbol ) ;
2005-04-17 02:20:36 +04:00
/* To avoid using get_symbol_offset for every symbol, we carry prefix along. */
2009-05-13 00:43:35 +04:00
struct kallsym_iter {
2005-04-17 02:20:36 +04:00
loff_t pos ;
unsigned long value ;
2009-05-13 00:43:35 +04:00
unsigned int nameoff ; /* If iterating in core kernel symbols. */
2005-04-17 02:20:36 +04:00
char type ;
2007-07-17 15:03:51 +04:00
char name [ KSYM_NAME_LEN ] ;
char module_name [ MODULE_NAME_LEN ] ;
2007-05-08 11:28:39 +04:00
int exported ;
2005-04-17 02:20:36 +04:00
} ;
static int get_ksymbol_mod ( struct kallsym_iter * iter )
{
2007-05-08 11:28:39 +04:00
if ( module_get_kallsym ( iter - > pos - kallsyms_num_syms , & iter - > value ,
& iter - > type , iter - > name , iter - > module_name ,
& iter - > exported ) < 0 )
2005-04-17 02:20:36 +04:00
return 0 ;
return 1 ;
}
/* Returns space to next name. */
static unsigned long get_ksymbol_core ( struct kallsym_iter * iter )
{
unsigned off = iter - > nameoff ;
2007-05-08 11:28:39 +04:00
iter - > module_name [ 0 ] = ' \0 ' ;
2005-04-17 02:20:36 +04:00
iter - > value = kallsyms_addresses [ iter - > pos ] ;
iter - > type = kallsyms_get_symbol_type ( off ) ;
off = kallsyms_expand_symbol ( off , iter - > name ) ;
return off - iter - > nameoff ;
}
static void reset_iter ( struct kallsym_iter * iter , loff_t new_pos )
{
iter - > name [ 0 ] = ' \0 ' ;
iter - > nameoff = get_symbol_offset ( new_pos ) ;
iter - > pos = new_pos ;
}
/* Returns false if pos at or past end of file. */
static int update_iter ( struct kallsym_iter * iter , loff_t pos )
{
/* Module symbols can be accessed randomly. */
if ( pos > = kallsyms_num_syms ) {
iter - > pos = pos ;
return get_ksymbol_mod ( iter ) ;
}
2009-05-13 00:43:35 +04:00
2005-04-17 02:20:36 +04:00
/* If we're not on the desired position, reset to new position. */
if ( pos ! = iter - > pos )
reset_iter ( iter , pos ) ;
iter - > nameoff + = get_ksymbol_core ( iter ) ;
iter - > pos + + ;
return 1 ;
}
static void * s_next ( struct seq_file * m , void * p , loff_t * pos )
{
( * pos ) + + ;
if ( ! update_iter ( m - > private , * pos ) )
return NULL ;
return p ;
}
static void * s_start ( struct seq_file * m , loff_t * pos )
{
if ( ! update_iter ( m - > private , * pos ) )
return NULL ;
return m - > private ;
}
static void s_stop ( struct seq_file * m , void * p )
{
}
static int s_show ( struct seq_file * m , void * p )
{
struct kallsym_iter * iter = m - > private ;
2009-05-13 00:43:35 +04:00
/* Some debugging symbols have no name. Ignore them. */
2005-04-17 02:20:36 +04:00
if ( ! iter - > name [ 0 ] )
return 0 ;
2007-05-08 11:28:39 +04:00
if ( iter - > module_name [ 0 ] ) {
char type ;
2009-05-13 00:43:35 +04:00
/*
* Label it " global " if it is exported ,
* " local " if not exported .
*/
2007-05-08 11:28:39 +04:00
type = iter - > exported ? toupper ( iter - > type ) :
tolower ( iter - > type ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( m , " %0*lx %c %s \t [%s] \n " ,
2009-05-13 00:43:35 +04:00
( int ) ( 2 * sizeof ( void * ) ) ,
2007-05-08 11:28:39 +04:00
iter - > value , type , iter - > name , iter - > module_name ) ;
} else
2005-04-17 02:20:36 +04:00
seq_printf ( m , " %0*lx %c %s \n " ,
2009-05-13 00:43:35 +04:00
( int ) ( 2 * sizeof ( void * ) ) ,
2005-04-17 02:20:36 +04:00
iter - > value , iter - > type , iter - > name ) ;
return 0 ;
}
2006-12-07 07:40:36 +03:00
static const struct seq_operations kallsyms_op = {
2005-04-17 02:20:36 +04:00
. start = s_start ,
. next = s_next ,
. stop = s_stop ,
. show = s_show
} ;
static int kallsyms_open ( struct inode * inode , struct file * file )
{
2009-05-13 00:43:35 +04:00
/*
* We keep iterator in m - > private , since normal case is to
2005-04-17 02:20:36 +04:00
* s_start from where we left off , so we avoid doing
2009-05-13 00:43:35 +04:00
* using get_symbol_offset for every symbol .
*/
2005-04-17 02:20:36 +04:00
struct kallsym_iter * iter ;
int ret ;
iter = kmalloc ( sizeof ( * iter ) , GFP_KERNEL ) ;
if ( ! iter )
return - ENOMEM ;
reset_iter ( iter , 0 ) ;
ret = seq_open ( file , & kallsyms_op ) ;
if ( ret = = 0 )
( ( struct seq_file * ) file - > private_data ) - > private = iter ;
else
kfree ( iter ) ;
return ret ;
}
2006-12-07 07:40:36 +03:00
static const struct file_operations kallsyms_operations = {
2005-04-17 02:20:36 +04:00
. open = kallsyms_open ,
. read = seq_read ,
. llseek = seq_lseek ,
2007-05-08 11:29:25 +04:00
. release = seq_release_private ,
2005-04-17 02:20:36 +04:00
} ;
static int __init kallsyms_init ( void )
{
2008-04-29 12:02:31 +04:00
proc_create ( " kallsyms " , 0444 , NULL , & kallsyms_operations ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-05-13 00:43:35 +04:00
device_initcall ( kallsyms_init ) ;