全部博文(44)
分类:
2010-03-08 13:42:28
前面提到内核image分为三个segment,其中就有note segment,它是包含在text segment中的。NOTE segment被用于不同的vendor在ELF文件中添加一些标识,让运行这些二进制代码的系统确定能否为该ELF提供其所需要的系统调用接口。它对我们了解内核用处不大,详细内容参见参考文献2。
NOTES :text :note
上面代码中,NOTE是一个宏,展开的格式和构建其它section的格式一样,这里”:text:note”表示把生成的section即加入text segment又加入note segment。从objdump的内容可以看到后者包含在前者之中,如下:
LOAD off 0x00001000 vaddr 0xc1000000 paddr 0x01000000 align 2**12 filesz 0x004de000 memsz 0x004de000 flags r-x NOTE off 0x0037b844 vaddr 0xc137a844 paddr 0x0137a844 align 2**2 filesz 0x00000024 memsz 0x00000024 flags ---
LOAD off 0x00001000 vaddr 0xc1000000 paddr 0x01000000 align 2**12
filesz 0x004de000 memsz 0x004de000 flags r-x
NOTE off 0x0037b844 vaddr 0xc137a844 paddr 0x0137a844 align 2**2
filesz 0x00000024 memsz 0x00000024 flags ---
前面提到,只读数据被放到了text segment,链接脚本中的RODATA宏完成了这项工作。RODATA创建了大量不同名称的section,它们有些是内置的,有些则是自定义的。创建方式并无特别之处,有了前面的知识,你可以轻易的看懂它们。这里要说的是关于自定义section的第二个例子——内核符号表。
读过LDD的朋友都知道,在module中导出符号给内核其它部分应该使用__ksymtab,我们也经常在内核中看到类似的代码,如:
EXPORT_SYMBOL(boot_cpu_data);
但,内核是怎么做的?符号表如何被创建?如果你看了/path_to_your_kernel_src/include/linux/module.h中EXPORT_SYMBOL的定义,再配合自定义section的知识,很快就能明白内核只是创建了一个名为__ksymtab的自定义section,当调用EXPROT_SYMBOL宏时会生成一个struct kernel_symbol变量记录下函数/数据的名称和地址,最后将这个变量存入__ksymtab section中。RODATA宏的如下代码:
/* Kernel symbol table: Normal symbols */ \
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___ksymtab) = .; \
*(__ksymtab) \
VMLINUX_SYMBOL(__stop___ksymtab) = .; \
} \
\
|
将输入文件中的__kysmtab section合并生成新的__ksymtab section,这就是内核最终的导出符号表,同样,__start___ksymtab和__stop___ksymtab记录下了表的起始地址和结束地址。如此一来,动态加载module时内核如何将module中调用的函数替换成相应的地址就不难理解了吧。
链接脚本知识:
或许你已经注意到,上述创建__ksymtab section的代码中,并没有在最后加上:text标明将该section放到text segment。实际上这是链接脚本的一个简化,当没有为section指定segment时,以上一个明确指定的segment为准。例如之前最后一次明确指定segment的__ex_table section指定了text segment,则其后没有指定segment的section也被放到了text segment,直到下一次明确指定segment的section出现为止。