What are Symbols and Symbol table in linux kernel
Hello students in this lecture will explain you what are symbols and symbol table in linux kernel, this is part of our free linux device driver course, if you are looking for free embeddded systems training online you can visit embeddedpathashala.com. so let us start understanding symbols and symbol table in linux kernel.
First let me tell you
what is a symbol,
what is symbol table,
what are static symbol table and live symbol table,
how to export symbols from kernel Module
Practical example for exporting
symbol type characters
using nm and objdump on a .ko file
weak symbols
KASLR
What is a symbol?
A symbol is simply a human readable name assigned to a location in a memory. that symbol basically holds a variable or a function. so assume if you write init counter = in C the name counter is the symbol that the compiler and linker use torefer to that memory address, Always remember at machine-code level only raw address exist symbols exist to make programs readable and debugging friendly.
What is symbol table?
A symbol table is a datastructure which is created by the compiler in compilation phase. it stores the mapping of symbol name to its corresponding memory address, type, size and scope.
in the kernel contect the symbol table contains every exported function and global variable used across the kernel and its modules for example you can see below information
Symbol Name | Address | Type | Scope
------------------|------------|----------|--------
printk | 0xffffffff | function | global
jiffies | 0xffffffff | variable | global
print_jiffies | 0xffffffffc | function | module
every kernel image we build embed a symbol table that contain the names and address of all the kernel symbols. this will contain information about both builtin modules and dynamically loaded modules.
System.map – symbol table for static kernel modules:
system.map is a simple text file that is generated everytime you compile the kernel, this static symbol table that is present at
sudo cat /boot/System.map-6.8.0-106-generic
and below is the output for the above system.map
ffffffff84200000 B __end_of_kernel_reserve
ffffffff84210000 b __brk_dmi_alloc
ffffffff84220000 b __brk_early_pgt_alloc
ffffffff84230000 B __brk_limit
ffffffff84230000 B _end
ffffffff84400000 D __init_scratch_begin
ffffffff84400000 d sme_workarea
ffffffff84800000 D __init_scratch_end
below is sample output where you can see different symbols and their address, below is how you should decode the symbol table these are the columns
<address> <type> <symbol_name> Example: ffffffff81a2b4c0 T printk ffffffff81c00000 D jiffies
there are different type characters that follow standard naming convention – T = Text function in global scope t= local scope D= initialized data, d= local data and so on we will discuss in detail in upcomming discussions
Note: as system.map is geneated at compile time, it only knows information about the symbols that are compiled directly into the kenrel that is it will have infoamtion about the builtin kernel modules, it will not have any infoamtion about the externally loaded kenrel modules or out of the tree kernel modules.
How to read the system.map:
Reading system.map requries root privileges as it will expose the raw kernel address.
sudo cat System.map-6.8.0-88-generic | grep <symbol_name>
now let us understand about the live runtime symbol table
/proc/kallsyms — the live runtime symbol table
/proc/kallsyms – it is a virtual file that will tell the information about the current live symbol table during the run time. it is not like system.map which only has static information about the inbuild kenrel modules
/proc/kallsyms : this will contain all the built-in kenel symbols, it will contain the symbols from out of tree kenrel modules which are dynamicallylocaded. when you load a module using insmod the real time infoamtion about loaded modules you can get here.
so when you are debugging issues on loadable kenrel modules it is the first point of contact, because it will have all the information about the modules that are loaded and unloaded.
Static file stored on the disk.
created at kernel compile time.
Only built-in symbols information available.
Information will update only after recompiling the kernel.
compile-time built-in only infoamtion
Live virtual file -> infoamtion updated dynamically.
Updated at runtime as modules load/unload.so when a new module is inserted or removed the infoamtion is updated.
Includes both built-in and out-of-tree module symbols.
runtime , built-in + LKMs
How to export symbols from a kernel module?
By default any function or variable you define inside the kernel module is local to that particular module only, other modules cannot see or use that symbol iformation, to make htat symbol information available across the different kernel modules kernel will provide us two macros whcih are EXPORT_SYMBOL(sym), EXPORT_SYMBOL_GPL(sym) let us see what they are in detail
| Macro | Who can use the exported symbol | Use case |
|---|---|---|
| EXPORT_SYMBOL(sym) | Any kernel module, regardless of license | General-purpose APIs, hardware drivers |
| EXPORT_SYMBOL_GPL(sym) | Only modules with a GPL-compatible license | Core kernel APIs, subsystem internals |
once symbol is exported the symbol appears in the /proc/kallsyms, while the module is loaded and becomes linkabale by other module during insmod time.
EXPORT_SYMBOL_GPL this enforce the GPL boundary at the kernel level- so attempting to use it from the proprietary module causes the kernel to refuse the loading and you will see the error “no symbol error”
Example code: export_sym.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
MODULE_LICENSE("GPL");
/* Forward declaration */
void print_jiffies(void);
/*
* print_jiffies - prints the current jiffies value to kernel log.
* This function is defined here and exported so other modules can call it.
*/
void print_jiffies(void)
{
printk(KERN_INFO "%s: jiffies = %ld\n", __func__, jiffies);
}
static int test_export_init(void)
{
printk(KERN_INFO "%s: module loaded\n", __func__);
return 0;
}
static void test_export_exit(void)
{
printk(KERN_INFO "%s: module unloaded\n", __func__);
}
/* Export the symbol — only GPL-licensed modules can link against it */
EXPORT_SYMBOL_GPL(print_jiffies);
module_init(test_export_init);
module_exit(test_export_exit);
Makefile
obj-m := export_sym.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
loading the source module
# Load the module sudo insmod export_sym.ko # Confirm the symbol appears in the live symbol table sudo cat /proc/kallsyms | grep print_jiffies # Check kernel log for init message dmesg | tail -5 # Unload when done sudo rmmod export_sym
below are few core concepts you should understand
Why does EXPORT_SYMBOL_GPL exist?
core subsystem interfaces are restricted to GPL compatible code via EXPORT_SYMBOL_GPL this is valid technical approach it will prevent the proprietary moduls from using hte sensitive kernel information, while stil the ABI of that module will exist.
What happens at link time?
when you insert a module with insmod the kenrels module loader reads the .ko file it resolves all the undefined symbols against the current kernel symbol table. then it will map the module into the kernel memeory. if any of hte required symbol is not found then the loading of the kenrel module will fail with unknownsymbol.
Quick reference
| Command / Item | Purpose |
|---|---|
| /boot/System.map-* | Static kernel symbol table (compile-time) |
| /proc/kallsyms | Live symbol table including loaded LKMs |
| EXPORT_SYMBOL(fn) | Export symbol for any module |
| EXPORT_SYMBOL_GPL(fn) | Export symbol restricted to GPL modules |
| MODULE_LICENSE(“GPL”) | Required to use GPL-only symbols |
| jiffies | Global kernel tick counter since boot |
| __func__ | Current function name as string literal |
| dmesg | tail | View recent kernel log output |
| insmod / rmmod | Load / unload a kernel module |
Using nm and objdump on a .ko file
nm export_sym.ko # list all symbols, types, addresses
objdump -t export_sym.ko # full symbol table dump
there is one more concept which is weak symbols
Weak symbols (__weak)
int __weak default_handler(void) { return -ENODEV; }
EXPORT_SYMBOL(default_handler);
a weak symbol will act as fallback so if any other module will provide strong definition of hte same symbol name, then the storng one wins during link time.
that is asll for this lecture, in next lecture we will discuss more about linux kenrel programming.
so please follow embeddedpathashala for more free embedded systems courses.
Free learning to all !!
EmbeddedPatahshala.
