1. module_init是什么

module_init是Linux内核开发和驱动开发中非常常见的宏,其定义在 include/linux/module.h中,可以看到,module_init的实现会根据是否定义MODULE而有所不同。MODULE决定了我们编写的驱动,是与内核编译到一起,还是单独编译为ko。

1.1. MODULE的定义

MODULE是通过在编译时,通过编译器的参数来传入的。如下是Makefile中的内容,在编译ko时,会使用如下两个编译选项,如果是链接到内核,则不会使用如下选项。

1/// Makefile
2KBUILD_AFLAGS_MODULE  := -DMODULE
3KBUILD_CFLAGS_MODULE  := -DMODULE

如果想看编译某个文件时是否由 -DMODULE选项,可以直接查看编译生成 .xxx.o.cmd文件。

下面先分析MODULE未使能的情况。

2. MODULE未使能

MODULE未定义的情况下,module_init是一种特殊的initcall。initcall是内核用于声明初始化函数以及控制函数调用顺序的机制。 initcall有多个级别,module_init实际就是device_initcall,其级别为6。initcall最终会声明一个initcall_t静态变量,链接时放到内核的 .init.data段。

 1/// include/linux/module.h
 2#ifndef MODULE
 3/**
 4 * module_init() - driver initialization entry point
 5 * @x: function to be run at kernel boot time or module insertion
 6 *
 7 * module_init() will either be called during do_initcalls() (if
 8 * builtin) or at module insertion time (if a module).  There can only
 9 * be one per module.
10 */
11#define module_init(x)  __initcall(x);
12
13/**
14 * module_exit() - driver exit entry point
15 * @x: function to be run when driver is removed
16 *
17 * module_exit() will wrap the driver clean-up code
18 * with cleanup_module() when used with rmmod when
19 * the driver is a module.  If the driver is statically
20 * compiled into the kernel, module_exit() has no effect.
21 * There can only be one per module.
22 */
23#define module_exit(x)  __exitcall(x);
24
25#else /* MODULE */
26/// ... ...

2.1. initcall分析

首先通过 arch64-linux-gnu-nm -n vmlinux | grep -E -C 2 '_initcall.*(_start|_end)$'来看一下编译的最终结果。

 1ffff8000094fc228 d __setup_debug_boot_weak_hash_enable
 2ffff8000094fc240 d __initcall__kmod_ptrace__414_42_trace_init_flags_sys_enterearly
 3ffff8000094fc240 D __initcall_start
 4ffff8000094fc240 D __setup_end
 5ffff8000094fc244 d __initcall__kmod_ptrace__416_66_trace_init_flags_sys_exitearly
 6--
 7ffff8000094fc2a8 d __initcall__kmod_earlycon__376_41_efi_earlycon_remap_fbearly
 8ffff8000094fc2ac d __initcall__kmod_dummy_timer__364_37_dummy_timer_registerearly
 9ffff8000094fc2b0 D __initcall0_start
10ffff8000094fc2b0 d __initcall__kmod_core__527_975_bpf_jit_charge_init0
11ffff8000094fc2b4 d __initcall__kmod_shm__486_153_ipc_ns_init0
12--
13ffff8000094fc2bc d __initcall__kmod_pci__516_6911_pci_realloc_setup_params0
14ffff8000094fc2c0 d __initcall__kmod_inet_fragment__696_216_inet_frag_wq_init0
15ffff8000094fc2c4 D __initcall1_start
16ffff8000094fc2c4 d __initcall__kmod_fpsimd__388_2058_fpsimd_init1
17ffff8000094fc2c8 d __initcall__kmod_process__462_734_tagged_addr_init1
18--
19ffff8000094fc3cc d __initcall__kmod_genetlink__620_1498_genl_init1
20ffff8000094fc3d0 d __initcall__kmod_trace_boot__394_671_trace_boot_init1s
21ffff8000094fc3d4 D __initcall2_start
22ffff8000094fc3d4 d __initcall__kmod_debug_monitors__391_139_debug_monitors_init2
23ffff8000094fc3d8 d __initcall__kmod_index__322_194_pi_init2
24--
25ffff8000094fc460 d __initcall__kmod_rpmsg_core__351_710_rpmsg_init2
26ffff8000094fc464 d __initcall__kmod_kobject_uevent__622_814_kobject_uevent_init2
27ffff8000094fc468 D __initcall3_start
28ffff8000094fc468 d __initcall__kmod_setup__397_281_reserve_memblock_reserved_regions3
29ffff8000094fc46c d __initcall__kmod_vdso__390_363_aarch32_alloc_vdso_pages3
30--
31ffff8000094fc4a8 d __initcall__kmod_dmi_id__332_259_dmi_id_init3
32ffff8000094fc4ac d __initcall__kmod_platform__462_596_of_platform_default_populate_init3s
33ffff8000094fc4b0 D __initcall4_start
34ffff8000094fc4b0 d __initcall__kmod_setup__399_406_topology_init4
35ffff8000094fc4b4 d __initcall__kmod_mte__458_603_register_mte_tcf_preferred_sysctl4
36--
37ffff8000094fc694 d __initcall__kmod_vgaarb__423_1564_vga_arb_device_init4s
38ffff8000094fc698 d __initcall__kmod_watchdog__465_479_watchdog_init4s
39ffff8000094fc69c D __initcall5_start
40ffff8000094fc69c d __initcall__kmod_debug_monitors__389_63_create_debug_debugfs_entry5
41ffff8000094fc6a0 d __initcall__kmod_resource__402_2029_iomem_init_inode5
42--
43ffff8000094fc784 d __initcall__kmod_acpi__398_141_acpi_reserve_resources5s
44ffff8000094fc788 d __initcall__kmod_initramfs__399_762_populate_rootfsrootfs
45ffff8000094fc788 D __initcallrootfs_start
46ffff8000094fc78c D __initcall6_start
47ffff8000094fc78c d __initcall__kmod_setup__401_440_register_arm64_panic_block6
48ffff8000094fc790 d __initcall__kmod_cpuinfo__334_359_cpuinfo_regs_init6
49--
50ffff8000094fcd40 d __initcall__kmod_9pnet_virtio__634_831_p9_virtio_init6
51ffff8000094fcd44 d __initcall__kmod_dns_resolver__330_382_init_dns_resolver6
52ffff8000094fcd48 D __initcall7_start
53ffff8000094fcd48 d __initcall__kmod_mounts__426_40_kernel_do_mounts_initrd_sysctls_init7
54ffff8000094fcd4c d __initcall__kmod_panic__390_97_kernel_panic_sysctls_init7
55--
56ffff8000094fce6c d __initcall__kmod_core__606_6212_regulator_init_complete7s
57ffff8000094fce70 d __initcall__kmod_platform__464_603_of_platform_sync_state_init7s
58ffff8000094fce74 D __con_initcall_start
59ffff8000094fce74 d __initcall__kmod_vt__408_3548_con_initcon
60ffff8000094fce74 D __initcall_end
61ffff8000094fce78 d __initcall__kmod_hvc_console__376_246_hvc_console_initcon
62ffff8000094fce7c d __initcall__kmod_8250__387_690_univ8250_console_initcon
63ffff8000094fce80 D __con_initcall_end
64ffff8000094fce80 D __initramfs_start

__initcall__kmod_cpuinfo__334_359_cpuinfo_regs_init6为例来说明一下module_init的效果。

__initcall__kmod_cpuinfo__334_359_cpuinfo_regs_init6是一个initcall_t类型的静态变量,变量名称可以由 __initcall_name(initcall, __initcall_id(fn), id)得出。

将变量名拆分开来可得:__ initcall __ [kmod_cpuinfo __ 334 _ 359 _ cpuinfo_regs_init] 6[]内的内容由 __initcall_id(fn)生成,其格式为 <modname>__<counter>_<line>_<fn>

  • modname:__KBUILD_MODNAME,来自于 scripts/Makefile.lib:127: -D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname))kmod_是固定的,__KBUILD_MODNAME在这里是kmod_cpuinfo。
  • counter:来自于 __COUNTER__,是编译器内部定义的计数器,每使用一次 __COUNTER__,其值为自动加一,这里是334。
  • line:这个是声明所在的行号,对应的源码为 arch/arm64/kernel/cpuinfo.c:359:device_initcall(cpuinfo_regs_init);,这里是359。
  • fn:函数名,这里是cpuinfo_regs_init。

除去 []内的部分,变量名的其他部分由 __initcall_name生成,其格式为 __<prefix>__<iid><id>

  • prefix:固定为initcall
  • iid:__initcall_id(fn)生成的内容
  • id:__define_initcall的第二个参数

2.2. initcall的源码

下面是 include/linux/init.h的源码,上边是以未定义CONFIG_LTO_CLANGCONFIG_HAVE_ARCH_PREL32_RELOCATIONS来分析的。

  1/// include/linux/init.h
  2#ifndef MODULE
  3
  4#ifndef __ASSEMBLY__
  5
  6/*
  7 * initcalls are now grouped by functionality into separate
  8 * subsections. Ordering inside the subsections is determined
  9 * by link order.
 10 * For backwards compatibility, initcall() puts the call in
 11 * the device init subsection.
 12 *
 13 * The `id' arg to __define_initcall() is needed so that multiple initcalls
 14 * can point at the same handler without causing duplicate-symbol build errors.
 15 *
 16 * Initcalls are run by placing pointers in initcall sections that the
 17 * kernel iterates at runtime. The linker can do dead code / data elimination
 18 * and remove that completely, so the initcall sections have to be marked
 19 * as KEEP() in the linker script.
 20 */
 21
 22/* Format: <modname>__<counter>_<line>_<fn> */
 23#define __initcall_id(fn)                                       \
 24        __PASTE(__KBUILD_MODNAME,                               \
 25        __PASTE(__,                                             \
 26        __PASTE(__COUNTER__,                                    \
 27        __PASTE(_,                                              \
 28        __PASTE(__LINE__,                                       \
 29        __PASTE(_, fn))))))
 30
 31/* Format: __<prefix>__<iid><id> */
 32#define __initcall_name(prefix, __iid, id)                      \
 33        __PASTE(__,                                             \
 34        __PASTE(prefix,                                         \
 35        __PASTE(__,                                             \
 36        __PASTE(__iid, id))))
 37
 38#ifdef CONFIG_LTO_CLANG
 39/*
 40 * With LTO, the compiler doesn't necessarily obey link order for
 41 * initcalls. In order to preserve the correct order, we add each
 42 * variable into its own section and generate a linker script (in
 43 * scripts/link-vmlinux.sh) to specify the order of the sections.
 44 */
 45#define __initcall_section(__sec, __iid)                        \
 46        #__sec ".init.." #__iid
 47
 48/*
 49 * With LTO, the compiler can rename static functions to avoid
 50 * global naming collisions. We use a global stub function for
 51 * initcalls to create a stable symbol name whose address can be
 52 * taken in inline assembly when PREL32 relocations are used.
 53 */
 54#define __initcall_stub(fn, __iid, id)                          \
 55        __initcall_name(initstub, __iid, id)
 56
 57#define __define_initcall_stub(__stub, fn)                      \
 58        int __init __stub(void);                                \
 59        int __init __stub(void)                                 \
 60        {                                                       \
 61                return fn();                                    \
 62        }                                                       \
 63        __ADDRESSABLE(__stub)
 64#else
 65#define __initcall_section(__sec, __iid)                        \
 66        #__sec ".init"
 67
 68#define __initcall_stub(fn, __iid, id)  fn
 69
 70#define __define_initcall_stub(__stub, fn)                      \
 71        __ADDRESSABLE(fn)
 72#endif
 73
 74#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
 75#define ____define_initcall(fn, __stub, __name, __sec)          \
 76        __define_initcall_stub(__stub, fn)                      \
 77        asm(".section   \"" __sec "\", \"a\"            \n"     \
 78            __stringify(__name) ":                      \n"     \
 79            ".long      " __stringify(__stub) " - .     \n"     \
 80            ".previous                                  \n");   \
 81        static_assert(__same_type(initcall_t, &fn));
 82#else
 83#define ____define_initcall(fn, __unused, __name, __sec)        \
 84        static initcall_t __name __used                         \
 85                __attribute__((__section__(__sec))) = fn;
 86#endif
 87
 88#define __unique_initcall(fn, id, __sec, __iid)                 \
 89        ____define_initcall(fn,                                 \
 90                __initcall_stub(fn, __iid, id),                 \
 91                __initcall_name(initcall, __iid, id),           \
 92                __initcall_section(__sec, __iid))
 93
 94#define ___define_initcall(fn, id, __sec)                       \
 95        __unique_initcall(fn, id, __sec, __initcall_id(fn))
 96
 97#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
 98
 99/*
100 * Early initcalls run before initializing SMP.
101 *
102 * Only for built-in code, not modules.
103 */
104#define early_initcall(fn)              __define_initcall(fn, early)
105
106/*
107 * A "pure" initcall has no dependencies on anything else, and purely
108 * initializes variables that couldn't be statically initialized.
109 *
110 * This only exists for built-in code, not for modules.
111 * Keep main.c:initcall_level_names[] in sync.
112 */
113#define pure_initcall(fn)               __define_initcall(fn, 0)
114
115#define core_initcall(fn)               __define_initcall(fn, 1)
116#define core_initcall_sync(fn)          __define_initcall(fn, 1s)
117#define postcore_initcall(fn)           __define_initcall(fn, 2)
118#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
119#define arch_initcall(fn)               __define_initcall(fn, 3)
120#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)
121#define subsys_initcall(fn)             __define_initcall(fn, 4)
122#define subsys_initcall_sync(fn)        __define_initcall(fn, 4s)
123#define fs_initcall(fn)                 __define_initcall(fn, 5)
124#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)
125#define rootfs_initcall(fn)             __define_initcall(fn, rootfs)
126#define device_initcall(fn)             __define_initcall(fn, 6)
127#define device_initcall_sync(fn)        __define_initcall(fn, 6s)
128#define late_initcall(fn)               __define_initcall(fn, 7)
129#define late_initcall_sync(fn)          __define_initcall(fn, 7s)
130
131#define __initcall(fn) device_initcall(fn)
132
133#define __exitcall(fn)                                          \
134        static exitcall_t __exitcall_##fn __exit_call = fn
135
136#define console_initcall(fn)    ___define_initcall(fn, con, .con_initcall)
137/// ... ...

2.3. rootfs_initcall是什么

rootfs_initcall在5s之后,rootfs_initcall指向的函数也会在do_basic_setup中被调用到。rootfs要从存储介质中读取文件系统镜像,需要在执行rootfs_initcall指向的函数之前,将存储介质准备好,比如:

1/// drivers/mmc/core/core.c
2subsys_initcall(mmc_init);

2.4. console_initcall

为了能尽早输出日志,内核将console相关的initcall单独存放,由console_initcall声明。相关初始化函数在console_init中调用。console_init则在start_kernel中尽量选择较早的时机调用。

 1/// kernel/printk/printk.c
 2/*
 3 * Initialize the console device. This is called *early*, so
 4 * we can't necessarily depend on lots of kernel help here.
 5 * Just do some early initializations, and do the complex setup
 6 * later.
 7 */
 8void __init console_init(void)
 9{
10    int ret;
11    initcall_t call;
12    initcall_entry_t *ce;
13
14    /* Setup the default TTY line discipline. */
15    n_tty_init();
16
17    /*
18     * set up the console device so that later boot sequences can
19     * inform about problems etc..
20     */
21    ce = __con_initcall_start;
22    trace_initcall_level("console");
23    while (ce < __con_initcall_end) {
24        call = initcall_from_entry(ce);
25        trace_initcall_start(call);
26        ret = call();
27        trace_initcall_finish(call, ret);
28        ce++;
29    }
30}

2.5. 链接脚本中initcall段

initcall声明的变量会放到以 .initcall开头命名的段中,每个级别对应一个 .initcall段,而这些段又会按顺序放到 .init.data段中。

 1/// include/asm-generic/vmlinux.lds.h
 2#define INIT_CALLS_LEVEL(level)						\
 3        __initcall##level##_start = .;				\
 4        KEEP(*(.initcall##level##.init))			\
 5        KEEP(*(.initcall##level##s.init))			\
 6
 7#define INIT_CALLS							\
 8        __initcall_start = .;					\
 9        KEEP(*(.initcallearly.init))				\
10        INIT_CALLS_LEVEL(0)					\
11        INIT_CALLS_LEVEL(1)					\
12        INIT_CALLS_LEVEL(2)					\
13        INIT_CALLS_LEVEL(3)					\
14        INIT_CALLS_LEVEL(4)					\
15        INIT_CALLS_LEVEL(5)					\
16        INIT_CALLS_LEVEL(rootfs)				\
17        INIT_CALLS_LEVEL(6)					\
18        INIT_CALLS_LEVEL(7)					\
19        __initcall_end = .;
20
21#define CON_INITCALL							\
22        __con_initcall_start = .;				\
23        KEEP(*(.con_initcall.init))				\
24        __con_initcall_end = .;
 1/// arch/arm64/kernel/vmlinux.lds.S
 2.init.data : {
 3    INIT_DATA
 4    INIT_SETUP(16)
 5    INIT_CALLS
 6    CON_INITCALL
 7    INIT_RAM_FS
 8    *(.init.altinstructions .init.bss)  /* from the EFI stub */
 9}
10.exit.data : {
11    EXIT_DATA
12}

2.6. initcall的执行时机

由initcall指定的函数会在do_pre_smp_initcalls和do_basic_setup执行。do_pre_smp_initcalls执行early_initcall指定的函数,这个是在多核处理器和调度系统初始化之前执行的。do_basic_setup会按initcall的级别,依次执行指定的函数。由于lds中没有根据函数名排序,同级别initcall指定的函数的执行顺序会由链接时initcall的布局决定。多次编译的顺序也可能不同。

 1/// init/main.c
 2static noinline void __init kernel_init_freeable(void)
 3{
 4    /// ... ...
 5    rcu_init_tasks_generic();
 6    do_pre_smp_initcalls();
 7    lockup_detector_init();
 8
 9    smp_init();
10    sched_init_smp();
11
12    padata_init();
13    page_alloc_init_late();
14    /* Initialize page ext after all struct pages are initialized. */
15    if (!early_page_ext_enabled())
16        page_ext_init();
17
18    do_basic_setup();
19    /// ... ...
20}

initcall_func

2.7. initcall调试

调用initcall函数时,如果打开了initcall_debug,内核会以KERN_DEBUG等级打印trace信息。

可以通过向bootargs内添加initcall_debug来打开initcall_debug。

1# cat /proc/sys/kernel/printk
26	4	1	7
3# cat /proc/cmdline
4console=ttyAMA0 nokaslr crashkernel=512M-1G:64M,1G-:128M loglevel=6 initcall_debug no_console_suspend root=/dev/vda rw init=/linuxrc

打开后,启动信息可以看到类似如下信息,如果启动过程中,loglevel比debug等级要低,可以使用dmesg命令查看。

 1[    0.051543][    T1] calling  trace_init_flags_sys_enter+0x0/0x30 @ 1
 2[    0.051631][    T1] initcall trace_init_flags_sys_enter+0x0/0x30 returned 0 after 0 usecs
 3[    0.051728][    T1] calling  trace_init_flags_sys_exit+0x0/0x30 @ 1
 4[    0.051748][    T1] initcall trace_init_flags_sys_exit+0x0/0x30 returned 0 after 0 usecs
 5[    0.051766][    T1] calling  cpu_suspend_init+0x0/0x68 @ 1
 6[    0.051801][    T1] initcall cpu_suspend_init+0x0/0x68 returned 0 after 0 usecs
 7[    0.051814][    T1] calling  asids_init+0x0/0x110 @ 1
 8[    0.051910][    T1] initcall asids_init+0x0/0x110 returned 0 after 0 usecs
 9[    0.051927][    T1] calling  spawn_ksoftirqd+0x0/0x60 @ 1
10[    0.052809][    T1] initcall spawn_ksoftirqd+0x0/0x60 returned 0 after 4000 usecs
11[    0.052840][    T1] calling  init_signal_sysctls+0x0/0x50 @ 1
12[    0.052894][    T1] initcall init_signal_sysctls+0x0/0x50 returned 0 after 0 usecs
13[    0.052912][    T1] calling  init_umh_sysctls+0x0/0x50 @ 1
14[    0.052963][    T1] initcall init_umh_sysctls+0x0/0x50 returned 0 after 0 usecs

在安装驱动,比如insmod hello.ko时,也会有类似的信息,比如:

1[   39.313554][  T141] calling  hello_init+0x0/0xff8 [hello] @ 141
2[   39.314090][  T141] initcall hello_init+0x0/0xff8 [hello] returned 0 after 105 usecs

3. MODULE使能

Linux中有些模块既可以链接到内核,也可以选择编译为ko,而这些模块的初始化函数可能是以initcall方式指定的。为了两者相互兼容,会把initcall定义为module_init。

下面分析module_init的实现,源码见《module_init源码》

  • __copy(initfn):从initfn复制函数属性,从gcc-9开始支持。
  • __attribute__((alias(#initfn))):为init_module创建别名,指向原来的initfn。
  • ___ADDRESSABLE(init_module, __initdata):声明一个initcall_t变量,并放到 .init.data段,变量名类似 __UNIQUE_ID___addressable_init_module326

__inittest:代码中没有找到调用的地方,但是从v2.6.0对module_init的注释来看,猜测是为了防止编译器警告。

1/* These macros create a dummy inline: gcc 2.9x does not count alias
2 as usage, hence the `unused function' warning when __init functions
3 are declared static. We use the dummy __*_module_inline functions
4 both to kill the warning and check the type of the init/cleanup
5 function. */

init_module是initfn的别名,其地址也是相同的,但是initfn通常会是static函数,而init_module是一个global函数。

1Disassembly of section .init.text:
2
30000000000000000 <init_module>:
4hello_init():
5   0:   d503201f        nop
6   4:   d503201f        nop
1/// t代表static,T代表global
20000000000000000 00000000000000c8 t hello_init
30000000000000000 00000000000000c8 T init_module

3.1. module_init源码

 1/// include/linux/module.h
 2/// ... ...
 3#else /* MODULE */
 4
 5/*
 6 * In most cases loadable modules do not need custom
 7 * initcall levels. There are still some valid cases where
 8 * a driver may be needed early if built in, and does not
 9 * matter when built as a loadable module. Like bus
10 * snooping debug drivers.
11 */
12#define early_initcall(fn)              module_init(fn)
13#define core_initcall(fn)               module_init(fn)
14#define core_initcall_sync(fn)          module_init(fn)
15#define postcore_initcall(fn)           module_init(fn)
16#define postcore_initcall_sync(fn)      module_init(fn)
17#define arch_initcall(fn)               module_init(fn)
18#define subsys_initcall(fn)             module_init(fn)
19#define subsys_initcall_sync(fn)        module_init(fn)
20#define fs_initcall(fn)                 module_init(fn)
21#define fs_initcall_sync(fn)            module_init(fn)
22#define rootfs_initcall(fn)             module_init(fn)
23#define device_initcall(fn)             module_init(fn)
24#define device_initcall_sync(fn)        module_init(fn)
25#define late_initcall(fn)               module_init(fn)
26#define late_initcall_sync(fn)          module_init(fn)
27
28#define console_initcall(fn)            module_init(fn)
29
30/* Each module must use one module_init(). */
31#define module_init(initfn)                                     \
32        static inline initcall_t __maybe_unused __inittest(void)                \
33        { return initfn; }                                      \
34        int init_module(void) __copy(initfn)                    \
35                __attribute__((alias(#initfn)));                \
36        ___ADDRESSABLE(init_module, __initdata);
37
38/* This is only required if you want to be unloadable. */
39#define module_exit(exitfn)                                     \
40        static inline exitcall_t __maybe_unused __exittest(void)                \
41        { return exitfn; }                                      \
42        void cleanup_module(void) __copy(exitfn)                \
43                __attribute__((alias(#exitfn)));                \
44        ___ADDRESSABLE(cleanup_module, __exitdata);
45
46#endif
1/// include/linux/init.h
2#define __initdata      __section(".init.data")
 1/// include/linux/compiler.h
 2/*
 3 * Force the compiler to emit 'sym' as a symbol, so that we can reference
 4 * it from inline assembler. Necessary in case 'sym' could be inlined
 5 * otherwise, or eliminated entirely due to lack of references that are
 6 * visible to the compiler.
 7 */
 8#define ___ADDRESSABLE(sym, __attrs) \
 9    static void * __used __attrs \
10        __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)&sym;

3.2. init_module的执行时机

命令行下使用 insmodmodprobe安装模块,最终系统调用为 init_modulefinit_module。 可以使用 strace insmod test.ko查看使用的具体的系统调用。

系统调用init_module和finit_module用来从用户态加载ko文件,man 2 init_module可以看到两者的介绍。init_module接收一个ELF文件的路径,而finit_module接收一个文件描述符。

 1/// kernel/module/main.c
 2SYSCALL_DEFINE3(init_module, void __user *, umod,
 3        unsigned long, len, const char __user *, uargs)
 4{
 5    /// ... ...
 6    return load_module(&info, uargs, 0);
 7}
 8
 9SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
10{
11    /// ... ...
12    return load_module(&info, uargs, flags);
13}

init_module和finit_module最后都会调到load_module,load_module的流程如下:

init_module

4. 参考资料

  1. gcc __COUNTER__
  2. copy (function)
  3. alias (“target”)
  4. __attribute__((alias)) 变量属性
  5. __attribute__((alias)) 函数属性
  6. linux 内核驱动中__attribute__((alias(#x)))别名问题