in the name of zero

June 4, 2007

module_init() and the init_call mechanism of linux

Filed under: hermetic studies

today, i tried to implement an initcall mechanismbased on what i’ve learned from linux. it works somehow. most of the work is being done at subsystem.c

i defined 3 levels of initcall .initcall1.init , .initcall2.init and .initcall6.init which are simply just the section name counter parts of core_initcall(), postcore_initcall() and device_initcall().

on the trail of module_init

Filed under: hermetic studies

ok for everyone’s information, i fancy anything ELF to an almost (but not quite) fanatic degree. my motivation for the activity below came when i straced insmod to see what kind of activity it does and there i encountered a few syscalls i haven’t had any chance to use nor to read about.

# strace insmod binfmt_mp3.ko
 ...
 create_module(NULL, 0)                  = -1 ENOSYS (Function not implemented)
 ...
 init_module(0x804b060, 3094, "")        = 0
 ...

grepping for module in unistd.h sort of paid off:

$ grep -i module /usr/include/asm/unistd.h
#define __NR_create_module      127
#define __NR_init_module        128
#define __NR_delete_module      129
#define __NR_query_module       167

and just to confirm things …

$ file binfmt_mp3.ko
binfmt_mp3.ko: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
i decided i’d try following what happens to a module on module_init() as it’s the entry point of lkms.

linux/init.h
	
typedef int (*initcall_t)(void);
	
#define __define_initcall(level,fn) \
        static initcall_t __initcall_##fn __attribute_used__ \
        __attribute__((__section__(".initcall" level ".init"))) = fn
	
#define device_initcall(fn)             __define_initcall("6",fn)
	
#define __initcall(fn) device_initcall(fn)
	
#define module_init(x)	__initcall(x)

apparently, initcalls also have levels.

linux/init.h
	
#define core_initcall(fn)               __define_initcall("1",fn)
#define postcore_initcall(fn)           __define_initcall("2",fn)
#define arch_initcall(fn)               __define_initcall("3",fn)
#define subsys_initcall(fn)             __define_initcall("4",fn)
#define fs_initcall(fn)                 __define_initcall("5",fn)
#define device_initcall(fn)             __define_initcall("6",fn)
#define late_initcall(fn)               __define_initcall("7",fn)

and based on the macros’ above, a module_init(function) roughly creates a function pointer entry “__initcall_function” in a section called “.initcall6.init”. __attribute_used__ tells the compiler to generate code even if it appears like the function isn’t refenced.
i have this object file from drivers/block/floppy.o as reference:

$ objdump -t floppy.o | grep -i initcall
00000000 l    d  .initcall6.init        00000000 .initcall6.init
00000000 l     O .initcall6.init        00000004 __initcall_floppy_init

yeah, i couldn’t find any initcallx.init section for lkms though. so i suppose initcall is something that works only if your module is compiled in-kernel.

the kernel linker script dedicates a special block for the “.initcallX.init” sections. bounded by __initcall_start and __initcall_end respectvely.

/arch/i386/kernel/vmlinux.lds
	
__initcall_start = .;
.initcall.init : {
	*(.initcall1.init)
	*(.initcall2.init)
	*(.initcall3.init)
	*(.initcall4.init)
	*(.initcall5.init)
	*(.initcall6.init)
	*(.initcall7.init)
}
__initcall_end = .;

looking for references to those two variables led me to init/main.c where the kernel traverses the .initcall*.init function pointers calling each function till __initcall_end.

init/main.c
	
extern initcall_t __initcall_start[], __initcall_end[];
	
init/main.c::static void __init do_initcalls(void)
	
        initcall_t *call;
        int count = preempt_count();
	
        for (call = __initcall_start; call < __initcall_end; call++) {
                char *msg;
	
                if (initcall_debug) {
                        printk(KERN_DEBUG "Calling initcall 0x%p", *call);
                        print_fn_descriptor_symbol(": %s()", (unsigned long) *call);
                        printk("\n");
                }
	
                (*call)();
	
		...

the do_initcall() function itself is found in another init section “.init.text”

that’s all i know for the time being.

Get free blog up and running in minutes with Blogsome | Theme designs available here