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

先要讲讲这个问题是怎么来的。(咱们在分析一个技术的时候,先要考虑它是想解决什么问题,或者学习新知识的时候,要清楚这个知识的目的是什么)。

我在编译内核的时候,发现arch/arm/kernel目录下有一个这样的文件:vmlinux.lds.S。第一眼看上去,想想是不是汇编文件呢?打开一看,好像不是。那它是干嘛的?而且前面已经说过,make V=1的时候,发现这个文件的用处在ld命令中,即ld -T vmlinux.lds.S,好像是链接命令用的,如下所示

            如arm-linux-ld -EL -p --no-undefined -X --build-id -o vmlinux -T arch/arm/kernel/vmlinux.lds。man ld,得到-T的意思是:为ld指定一个Linker script,意思是ld根据这个文件的内容来生成最终的二进制。

也许上面这个问题,你从没关注过,但是在研究内核代码的时候,常常有地方说“ __init宏会在最后的模块中生成一个特定的section,然后kernel加载的时候,寻找这个section中的函数”,说白了,上面这句话就是说最后生成的模块中,有一个特定的section,这又是什么东西?

好吧,希望上面的问题勾起你的好奇心。下面我们来扫盲,最后会给一个链接地址,各看官可以去那深造。

一 section是什么?

好吧,我们需要解释一下平时编译链接生成的二进制可执行程序(比如说ELF,EXE也行),so或者dll,内核(非压缩的,参加本系列第一节内容、vmlinux),或者ko是怎么组织的。

其实,大家或多或少都知道这些二进制中包括有什么text/bss/data节(也叫section)。text节存储的是代码、data存储的是已经初始化的静态变量、bss节存储的是未初始化的什么东西...

上面的东西我就不细究了。反正一点,一个二进制,最终会包含很多section。那么,为什么section叫text/bss/data,能叫别的名字吗?

OK,可以。但是你得告诉ld,那么这些内容就通过-T选项指定一个linker script就行了。这些内容我们放到后面的实例中来介绍。

(再三强调,咱们在理论上只是抛砖引玉,希望有兴趣的看官自己研究,注意和我们分享你的成果就行了。)

二 link script基础知识介绍

linker script中的语法是linker command language(很简单的language,大家不用害怕...)。那么LS的目的是什么呢?

LS描述输入文件(也就是gcc -c命令产生的.o文件即object文件)中的section最终如何对应到一个输出文件。这个其实好理解,例如一个elf由三个.o文件构成,每个.o文件都有text/data/bss段,但最终的那一个elf就会将三个输入的.o文件的段合并到一起。

好了,下面我们介绍一些基本知识:

ld的功能是将input文件组装成一个output文件。这些文件内部的都有特殊的组织结构,这种结构被叫做object file format。每一个文件叫做object file(这可能就是.o文件的来历吧。哈哈),输出文件也叫可执行文件(an executable),但是对于ld来说,它也是一种object文件。那么Object文件有什么特殊的地方呢?恩,它内部组织是按照section(段、或者节,以后不再区分二者)来组织的。一句话,object文件内部包含段...... 每个段都有名字和size。另外,段内部还包含一些数据,这些数据叫做section contents,以后称段内容。每个段有不同的属性。例如text段标志为可加载(loadable),表示该段内的contents在运行时候(当然指输出文件执行的时候)需要加载到内存中。另外一些段中没有contents,那么这些段标示为allocatable,即需要分配一些内存(有时候这些内存会被初始化成0,这里说的应该是BSS段。BSS段在二进制文件中没有占据空间,即磁盘上二进制文件的大小比较小,但是加载到内存后,需要为BSS段分配内存空间。),还有一些段属于debug的,这里包含一些debug信息。 既然需要加载到内存中,那么加载到内存的地址是什么呢?loadable和allocable的段都有两个地址,VMA:虚拟地址,即程序运行时候的地址,例如把text段的VMA首地址设置为0x800000000,那么运行时候的首地址就是这个了。另外还有一个LMA,即Load memory address。这个地址是section加载时的地址。晕了吧?二者有啥区别?一般情况下,VMA=LMA。但也有例外。例如设置某数据段的LMA在ROM中(即加载的时候拷贝到ROM中),运行的时候拷贝到RAM中,这样LMA和VMA就不同了。---------》很难搞懂不是?这种方法用于初始化一些全局变量,基于那种ROM based system。(问一个问题,run的时候,怎么根据section中的VMA进行相应设置啊??以后可能需要研究下内核中关于execve实现方面的内容了)。关于VMA和LMA,大家通过objdump -h选项可以查看。

三 简单例子

下面来一个简单例子,

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

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