linux内核模块编译链接与加载剖析一

来源:互联网 发布:淘宝客服工作室照片 编辑:程序博客网 时间:2024/05/01 09:32

看到很多书上或网上说内核模块的加载内核版本在2.6之后就必须使用这两个module_init和module_exit个宏,但是在内核3.6上发现不使用这两个宏而把初始化和去初始化函数命名为init_module()与cleanup_module()也是可以,就对内核模块的编译和加载产生很大的兴趣,下面来分析一下模块的编译和加载过程,为什么这两种方式都可以?这两个宏到底做了什么?以及模块在执行加载命令后发生了什么?

内核模块的编译

如果大家仔细观察的话在编译内核模块的会产生类似.mod.c的文件

[root@localhost modules_programming]# ls
hello-1.c  hello-2.c  Makefile
[root@localhost modules_programming]# make
make -C /lib/modules/3.6.10-4.fc18.i686/build M=/myfile/modules_programming modules
make[1]: Entering directory `/usr/src/kernels/3.6.10-4.fc18.i686'
  CC [M]  /myfile/modules_programming/hello-1.o
  CC [M]  /myfile/modules_programming/hello-2.o
  Building modules, stage 2.
  MODPOST 2 modules
  CC      /myfile/modules_programming/hello-1.mod.o

  LD [M]  /myfile/modules_programming/hello-1.ko
  CC      /myfile/modules_programming/hello-2.mod.o
  LD [M]  /myfile/modules_programming/hello-2.ko
make[1]: Leaving directory `/usr/src/kernels/3.6.10-4.fc18.i686'

在我们执行了make之后,首先顶层makefile遍历执行各个子文件夹得make文件,这个在linux内核模块编程一中有讲到过,在内核的linux-3.6.10/scripts/有一个Makefile.modpost,我们打开这个文件看到模块的编译分为四步

1、各自模块独立.o的编译,就是我们的hello-1.o,hello-2.o

2、根据1中产生的 <module>.o调用modpost产生对应的 <module>.mod.c并编译生 <module>.mod.o

   然后把<module>.o与<module>.mod.o链接出<module>.ko

3、将在模块的ELF段中加入确定的信息,包括版本幻数和模块信息

4、这一步就仅用于外部模块的版本控制

这里列出hello-1.c、hello-2.c,区别就在于hello-2.c使用了module_init()与module_exit()两个宏,而hello-1.c没有

/*   *  hello-1.c - The simplest kernel module. */#include <linux/module.h>/* Needed by all modules */#include <linux/kernel.h>/* Needed for KERN_INFO */int init_module(void){printk(KERN_ALERT "Hello world 1.\n");/*  * A non 0 return means init_module failed; module can't be loaded.  */return 0;}void cleanup_module(void){printk(KERN_ALERT "Goodbye world 1.\n");}
/*   *  hello-2.c - Demonstrating the module_init() and module_exit() macros. *  This is preferred over using init_module() and cleanup_module(). */#include <linux/module.h>/* Needed by all modules */#include <linux/kernel.h>/* Needed for KERN_INFO */#include <linux/init.h>/* Needed for the macros */static int __init hello_2_init(void){printk(KERN_INFO "Hello, world 2\n");return 0;}static void __exit hello_2_exit(void){printk(KERN_INFO "Goodbye, world 2\n");}module_init(hello_2_init);module_exit(hello_2_exit);

在linux-3.6.10/include/linux/init.h 有对module_init()与module_exit()定义

/* Each module must use one module_init(). */#define module_init(initfn)\static inline initcall_t __inittest(void)\{ return initfn; }\int init_module(void) __attribute__((alias(#initfn)));/* This is only required if you want to be unloadable. */#define module_exit(exitfn)\static inline exitcall_t __exittest(void)\{ return exitfn; }\void cleanup_module(void) __attribute__((alias(#exitfn)));

module_init(hello_2_init);就相当于

...int init_module(void)  __attribute__((alias(#hello_2_init)));

这里通过__attribute__((alias(#hello_2_init)))给hello_2_init换了一个名字init_module

而在编译过程中生成的hello-1.mod.c,hello-2.mod.c的内容是一样的

/*   *  hello-1.mod.c - The simplest kernel module. */#include <linux/module.h>#include <linux/vermagic.h>#include <linux/compiler.h>MODULE_INFO(vermagic, VERMAGIC_STRING);struct module __this_module__attribute__((section(".gnu.linkonce.this_module"))) = {.name = KBUILD_MODNAME,.init = init_module,#ifdef CONFIG_MODULE_UNLOAD.exit = cleanup_module,#endif.arch = MODULE_ARCH_INIT,};static const char __module_depends[]__used__attribute__((section(".modinfo"))) ="depends=";

<module>.mod.c中定义一个module结构类型的变量struct module __this_module这个变量是放在ELF文件的段名为.gnu.linkonce.this_module的段中,通过readelf工具也可以看到相关的段

[root@localhost modules_programming]# readelf -S hello-1.mod.o
There are 19 section headers, starting at offset 0x6c64:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000000 00  AX  0   0  4
  [ 2] .data             PROGBITS        00000000 000034 000000 00  WA  0   0  4
  [ 3] .bss              NOBITS          00000000 000034 000000 00  WA  0   0  4
  [ 4] .modinfo          PROGBITS        00000000 000034 000039 00   A  0   0  1
  [ 5] .gnu.linkonce.thi PROGBITS        00000000 000080 000180 00  WA  0   0 32
  [ 6] .rel.gnu.linkonce REL             00000000 0070e8 000010 08     17   5  4
  [ 7] .debug_info       PROGBITS        00000000 000200 003ba3 00      0   0  1
  [ 8] .rel.debug_info   REL             00000000 0070f8 001b38 08     17   7  4
  [ 9] .debug_abbrev     PROGBITS        00000000 003da3 0002a6 00      0   0  1
  [10] .debug_aranges    PROGBITS        00000000 004049 000018 00      0   0  1
  [11] .rel.debug_arange REL             00000000 008c30 000008 08     17  10  4
  [12] .debug_line       PROGBITS        00000000 004061 00039a 00      0   0  1
  [13] .debug_str        PROGBITS        00000000 0043fb 002787 01  MS  0   0  1
  [14] .comment          PROGBITS        00000000 006b82 00002d 01  MS  0   0  1
  [15] .note.GNU-stack   PROGBITS        00000000 006baf 000000 00      0   0  1
  [16] .shstrtab         STRTAB          00000000 006baf 0000b4 00      0   0  1
  [17] .symtab           SYMTAB          00000000 006f5c 000130 10     18  16  4
  [18] .strtab           STRTAB          00000000 00708c 000059 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)


最后hello-1.o与hello-1.mod.o链接的到hello-1.ko

通过nm产看编译出来的hell-1.ko,hello-2.ko内核模块的符号表,查看区别

[root@localhost modules_programming]# nm hello-1.ko
00000020 T cleanup_module
00000000 T init_module
         U mcount
00000000 r __module_depends
00000009 r __mod_vermagic5
         U printk
00000000 D __this_module
[root@localhost modules_programming]# nm hello-2.ko
00000000 T cleanup_module
00000000 t hello_2_exit
00000000 t hello_2_init

00000000 T init_module
00000000 r __module_depends
00000009 r __mod_vermagic5
         U printk
00000000 D __this_module

hello-2.ko比hello-1.ko多了hello_2_init,hello_2_exit两个符号,但是他们是局部符号

况且init_module与cleanup_module是hello_2_init,hello_2_exit别名,所以就本质上来说hello-1.ko与hello-2.ko是一样

注:

局部符号,对于目标文件的外部不可见

全局符号,外部可见


原创粉丝点击