Linux Kernel系列三:Kernel编译和链接中的linker scr(2)

 SECTIONS

 {
       . = 0x10000;
       .text : { *(.text) }
       . = 0x8000000;
       .data : { *(.data) }
       .bss : { *(.bss) }
 }

SECTIONS是LS语法中的关键command,它用来描述输出文件的内存布局。例如上例中就含text/data/bss三个部分(实际上text/data/bss才是段,但是SECTIONS这个词在LS中是一个command,希望各位看官要明白)。 .=0x10000; 其中的.非常关键,它代表location counter(LC)。意思是.text段的开始设置在0x10000处。这个LC应该指的是LMA,但大多数情况下VMA=LMA。 .text:{*(.text)},这个表示输出文件的.text段内容由所有输入文件(*)的.text段组成。组成顺序就是ld命令中输入文件的顺序,例如1.obj,2.obj...... 此后,由来了一个.=0x800000000;。如果没有这个赋值的,那么LC应该等于0x10000+sizeof(text段),即LC如果不强制指定的话,它默认就是上一次的LC+中间section的长度。还好,这里强制指定LC=0X800000000.表明后面的.data段的开始位于这个地址。 .data和后面的.bss表示分别有输入文件的.data和.bss段构成。

你看,我们从这个LC文件中学到了什么?

恩,我们可以任意设置各个段的LMA值。当然,绝大部分情况,我们不需要有自己的LS来控制输出文件的内存布局。不过LK(linux kernel)可不一样了......

 

四 霸王硬上弓---vmlinux.lds.S分析

 

OK,有了上面的基础知识,下面我们霸王硬上弓,直接分析arch/arm/kernel/vmlinux.lds.S.虽然最终链接用的是vmlinux.lds,但是那个文件

由vmlinux.lds.S(这是一个汇编文件)得到,

arm-linux-gcc -E -Wp,-MD,arch/arm/kernel/.vmlinux.lds.d -nostdinc ...... -D__KERNEL__ -mlittle-endian ......

-DTEXT_OFFSET=0x00008000 -P -C -Uarm -D__ASSEMBLY__ -o arch/arm/kernel/vmlinux.lds     arch/arm/kernel/vmlinux.lds.S

所以,我们直接分析vmlinux.lds好了。

/*
   一堆注释,这里就不再贴上了,另外,增加//号做为注释标识
 * Convert a physical address to a Page Frame Number and back
 */

//OUTPUT_ARCH是LS语法中的COMMAND,用来指定输出文件的machine arch。objdump -f可查询所有支持的machine。另外

//这些东西涉及到一种叫BFD的。各位看官可以自己搜索下BFD的内容。

//下面这 表示输出文件基于ARM架构
OUTPUT_ARCH(arm)   

//ENTRY也是一个command,用来设置入口点。这里表示入口点是stext 。根据LD的描述,入口点的意思就是程序运行的第一条指令。内核是一个模块,大家把他想象

//成一个运行在硬件上的大程序就可以了。而我们的程序又是运行在内核至上的。比较下Java虚拟机以及运行在其上的Java程序吧......
ENTRY(stext)

//设置jiffies为jiffies_64
jiffies = jiffies_64;

//定义输出文件的段
SECTIONS
{

//设置location count为0xc0008000,这个好理解吧?内核运行的地址全在C0000000以上
 . = 0xC0000000 + 0x00008000;

//定义一个.text.head段,由输入文件中所有.text.head段组成

/*

LS语法中,关于seciton的定义如下:

section [address] [(type)] :
       [AT(lma)] [ALIGN(section_align)]
       [SUBALIGN(subsection_align)]
       [constraint]
       {
         output-section-command
         output-section-command
         ...
       } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp]

其中,address为VMA,而AT命令中的为LMA。一般情况,address不会设置,所以它默认等于当前的location counter

*/
 .text.head : {

/*这个非常关键,咱们在内核代码中经常能看到一些变量声明,例如extern int __stext,但是却找不到在哪定义的

其实这些都是在lds文件中定义的。这里得说一下编译链接相关的小知识。咱们这知道大概即可,具体内容可以自己深入研究

假设C代码中定义一个变量 int x = 0;那么

1 编译器首先会分配一块内存,用来存储该变量的值

2 编译器在程序的symbol表中,创建一项,用来存储这个变量的地址

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/46606dcd6b9a27c58f98479a68d6551b.html