I wrote a simple hello world kernel module
#include <linux/kernel.h> #include <linux/module.h> #include <linux/delay.h> MODULE_LICENSE("GPL"); static int __init test_hello_init(void) { printk(KERN_INFO"%s: In initn", __func__); return 0; } static void __exit test_hello_exit(void) { printk(KERN_INFO"%s: In exitn", __func__); } module_init(test_hello_init); module_exit(test_hello_exit);
After loading the module, i am checking what all symbols are added into /proc/kallsysms. I don’t observe test_hello_init. Why don’t we have it
0000000000000000 r __func__.20413 [hello] 0000000000000000 t test_hello_exit [hello] 0000000000000000 r __func__.20417 [hello] 0000000000000000 r _note_6 [hello] 0000000000000000 d __this_module [hello] 0000000000000000 t cleanup_module [hello]
Advertisement
Answer
Using Linux kernel 5.8 source as a reference, the module’s symbol table for “kallsyms” is set up by the call to add_kallsyms()
(in “kernel/module.c”) at module load time (call path: syscall init_module()
, load_module()
, post_relocation()
, add_kallsyms()
), before the module has been fully initialized. It adds the module’s full symbol table (pointed to by the module’s kallsyms
member), and also constructs a cut-down, core symbol table (in the module’s core_kallsyms
member) for use after the module has been initialized.
The switch from the full symbol table to the core symbol table occurs at the call
/* Switch to core kallsyms now init is done: kallsyms may be walking! */ rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms);
from do_init_module()
(call path: syscall init_module()
, load_module()
, do_init_module()
). This occurs after the module’s init
function (if it has one) has been called.
do_init_module()
is also responsible for discarding the module’s initial memory section (containing functions marked __init
and data marked __initdata
) once the module is initialized successfully. This is done by adding a pointer to the module’s init section to the static init_free_list
list and scheduling a work item to free any sections on the list:
freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
freeinit->module_init = mod->init_layout.base;
if (llist_add(&freeinit->node, &init_free_list)) schedule_work(&init_free_wq);
The scheduled work function is do_free_init()
. That exchanges init_free_list
with an empty list and iterates through the original copy of init_free_list
to free the module’s initial memory section:
list = llist_del_all(&init_free_list);
llist_for_each_safe(pos, n, list) { initfree = container_of(pos, struct mod_initfree, node); module_memfree(initfree->module_init); kfree(initfree); }