What are Symbols and Symbol table in linux kernel

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.

System.map

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

/proc/kallsyms

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.

Leave a Reply

Your email address will not be published. Required fields are marked *