Linux kernel 分析之七:内核启动-链接脚本

来源:互联网 发布:井泵电机数据 编辑:程序博客网 时间:2024/05/18 03:21

一般来说,用户是不需要关心section的具体位置的。在用户态,内核会解析elf可执行文件的各个section,然后把它映射到虚拟地址空间。然而,在内核启动时,一切得从 

零开始。很多在用户态下应用程序不需要操心的东西,例如映射section的任务不得不由内核自己来完成。上一篇感悟揭示了内核如何建立页表,并且把自身的一部分映射到虚拟地址。内核还要负责对BSS段(所有在代码中未定义的全局变量)的初始化(设置为0),这就要求内核知道section的具体位置(否则如何知道该映射哪一部分呢?)
此外,在开启页面映射的过程中,我最为疑惑的是几个常量(页目录
swapper_pg_dir,页表pg0等等)是如何确定的。扩展一下。gcc链接可执行文件时,是如何确定变量的地址的?按理说应该有某种途径(命令行参数或者文件)告诉链接器ld如何定位这些变量。最普通如hello world。为什么_start的地址是0x80482e0?于是想到,我们需要一个文件来指定各个section的虚拟地址。在内核源代码里,还看到这个文件
arch/i386/kernel/vmlinux.lds.S。不像是普通的汇编文件。原来这就是linker scripts链接器脚本。
在链接器脚本中,.表示当前location counter地址计数器的值。默认为0。017   . = __KERNEL_START;
表示地址计数器从__KERNEL_START(0xc00100000)开始。.text:{...}
表示.text section包含了哪几个section031   . = ALIGN(16);
则表示对齐方式。
具体格式可以调用info ld查看Linker Scripts一节。
链接器脚本指定了各个section的起始位置和结束位置。它还允许程序员在脚本中对变量进行赋值。这使内核可以通过__initcall_start和__initcall_end之类的变量获得段的起始地址和结束地址,从而对某些段进行操作。
根据链接器脚本,以及nm vmlinux的结果,内核中各个section的虚拟地址就很清楚了。以我的机子为例(粗略):地址分配
text section:
从_text:c0100000 A _text到_etext:c0436573 A _etextException table
从__start___ex_table:c0436580 A __start___ex_table到__stop___ex_table:c04370b8 A __stop___ex_tableRODATAread only section.datawritable section.data_nosavesection
从__nosave_begin:c050f000 A __nosave_begin到__nosave_end:c050f000 A __nosave_end.data.page_alignedsection.data.cacheline_alignedsection.data.read_mostlysection.data.init_tasksectioninit section
从__init_begin c0514000 A __init_begin到__init_end c0540000 A __init_end其中.initcall.init section:从__initcall_start:c053b570 A __initcall_start到__initcall_end:c053b8c0 A __initcall_end
BSS section
从__bss_startc0540000 A __bss_start到__bss_endc0594c78 A __bss_stop其中swapper进程的页表
从c0540000 B swapper_pg_dir到c0541000共一页empty_zero_page
从c0541000 B empty_zero_page到c0542000共一页
pg0页目录0
从c0595000 A pg0到init_pg_tables_end.exitcall.exit sectionstab section
几个比较重要的section:
bss section,存放在代码里未初始化的全局变量,最后初始化为0。
init sections,所有只在初始化时调用的函数和变量,包括所有在内核启动时调用的函数,以及内核模块初始化时调用的函数。其中最特别的是.initcall.init section。通过__initcall_start和__initcall_end,内核可以调用里面所有的函数。这些section在使用一次后就可以释放,从而节省内存。

0 0
原创粉丝点击