Search code examples
linuxgcclinux-kernelstaticlinux-device-driver

What happens to local static identifiers in __init function?


In application programming, static variables are stored in .BSS section. And unlike local variables they're not de allocated on function return or something similar to global variables.


In Linux kernel modules, functions can be tagged with __init attribute in Linux and code in .text of such functions will be removed, once they're executed.


Let's say there's a function with __init and has static local variables; Will those static local's be de allocated from .BSS section along with the function from .text section?

Yes, of course there might not be any use (mention if you find one) of declaring a static variable in an __init function, but I want to understand behind the scenes.


Solution

  • If something is defined as static, it must stay alive for the entire lifetime of the associated unit. The only thing that happens is that you will not be able to reference the object by its original name anywhere outside the function in which it was declared. Therefore, if you don't save a reference to the object somewhere else, the object will still be available, but you will have lost access to it (this could be considered a memory leak).

    The only exception when talking about kernel code is the __initdata macro, that will put variables in a specific section (.init.data) and discard them when the initialization is done (just like .init). In such case, the variable will be gone even if defined static, and references will become invalid. This is useful if you need some kind of complex (and large) structure only for initialization purposes and you want to save space by discarding it after using it in your init function.

    Here's a working example:

    // SPDX-License-Identifier: GPL-3.0
    #include <linux/init.h>   // module_{init,exit}()
    #include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
    #include <linux/kernel.h> // printk(), pr_*()
    
    #ifdef pr_fmt
    #undef pr_fmt
    #endif
    #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
    
    static unsigned *global_ptr;
    static unsigned global_var;
    static int global_initdata_var __initdata;
    
    static int __init my_init(void)
    {
            static unsigned local_var = 123;
            static unsigned local_initdata_var __initdata = 456;
    
            global_ptr = &local_var;
            global_var = local_initdata_var;
    
            return global_initdata_var;
    }
    
    static void __exit my_exit(void)
    {
            pr_info("%u %u\n", *global_ptr , global_var);
    }
    
    module_init(my_init);
    module_exit(my_exit);
    MODULE_VERSION("0.1");
    MODULE_DESCRIPTION("Test module.");
    MODULE_AUTHOR("Marco Bonelli");
    MODULE_LICENSE("GPL");
    

    Looking into the .ko file with objdump after compiling the above shows:

    • local_var defined inside the init function in the .data section (because it was explicitly initialized to 123).
    • local_initdata_var and global_initdata_var in the .init.data section (because of __initdata), regardless of where they were defined.
    • Global non-explicitly initialized variables in the .bss section.
    / # objdump -j .init.data -j .data -j .bss -D static_init.ko
    
    static_init.ko:     file format elf64-littleaarch64
    
    
    Disassembly of section .data:
    
    0000000000000000 <local_var.20642>:
       0:   0000007b        .word   0x0000007b
    
    Disassembly of section .init.data:
    
    0000000000000000 <local_initdata_var.20643>:
       0:   000001c8        .word   0x000001c8
    
    0000000000000004 <global_initdata_var>:
       4:   00000000        .word   0x00000000
    
    Disassembly of section .bss:
    
    0000000000000000 <global_ptr>:
            ...
    
    0000000000000008 <global_var>:
       8:   00000000        .word   0x00000000
    

    Result of inserting/removing the module:

    / # insmod static_init.ko
    / # cat /proc/kallsyms | grep static_init
    ffff800011ad9768 b static_init_done.7337
    ffff800008ca0000 t $x   [static_init]
    ffff800008ca0000 t my_exit      [static_init]
    ffff800008ca2000 d $d   [static_init]
    ffff800008ca2000 d local_var.20642      [static_init]
    ffff800008ca2348 b $d   [static_init]
    ffff800008ca2348 b global_ptr   [static_init]
    ffff800008ca2350 b global_var   [static_init]
    ffff800008ca1028 r $d   [static_init]
    ffff800008ca2040 d $d   [static_init]
    ffff800008ca1040 r $d   [static_init]
    ffff800008ca1040 r _note_6      [static_init]
    ffff800008ca2040 d __this_module        [static_init]
    ffff800008ca0000 t cleanup_module       [static_init]
    / # rmmod static_init
    [   12.155152] static_init: 123 456
    / #
    

    As you can see, local_var is still there even after inserting the module, and is marked as d (local data symbol), while variables defined with __initdata (no matter where) are no longer there.