一、准备知识
程序段的概念:代码段、数据段、bss段(ZI段)、自定义段
段就是程序的一部分,我们把整个程序的所有东西分成了一个一个的段,给每个段起个名字,然后在链接时就可以用这个名字来指示这些段。也就是说给段命名就是为了在链接脚本中用段名来让段站在核实的位置。
段名分为2种:一种是编译器链接器内部定好的,先天性的名字;一种是程序员自己指定的、自定义的段名。
先天性段名:
代码段:(.text),又叫文本段,代码段其实就是函数编译后生成的东西
数据段:(.data),数据段就是C语言中有显式初始化为非0的全局变量(注意:全局变量才算是程序的数据,局部变量不算程序的数据,只能算是函数的数据)。数据段属于静态内存分配
bss段:(.bss),BSS是英文Block Started by Symbol的简称,又叫ZI(zero initial)段,就是零初始化段,对应C语言中初始化为0的全局变量。BSS段属于静态内存分配。
(局部变量没有初始化是随机的,全局变量没有初始化是0)
后天性段名:
段名由程序员自己定义,段的属性和特征也由程序员自己定义。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
栈(stack): 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
二、链接脚本究竟要做什么?
链接脚本其实是个规则文件,他是程序员用来指挥链接器工作的。链接器会参考链接脚本,并且使用其中规定的规则来处理.o文件中那些段,将其链接成一个可执行程序。
一个简单的链接命令如下:
arm-linux-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf
-T选项是ld命令中比较重要的一个选项,可以用它直接指明代码的代码段、数据段、bss段。
-Ttext addr
-Tdata addr
-Tbss addr
arm-linux-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf ,运行地址为0x00000000,由于没有指明数据段和bss,他们会默认的依次放在后面。相同的代码不同的Ttext,你可以对比一下他们之间会变的差异,ld会自动调整跳转的地址。
对于复杂的链接,可以专门写一个脚本来告诉编译器如何链接。这个脚本描述了输入文件的sections→输出文件的映射,以及输出文件的memory layout。因此,链接器总会使用一个linker script,如果不特别指定,则使用默认的script;可以使用‘-T’命令行选项来指定一个linker script。
arm-linux-ld -Tlink.lds -o led.elf $^
其中link.lds就是这个这个链接脚本的文件名。
三、一个简单的链接脚本
SECTIONS
{
. = 0xd0024000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
链接脚本的理解:
SECTIONS {} 这个是整个链接脚本
. 点号在链接脚本中代表当前位置。
= 等号代表赋值
代码段中start放在最前面,其他的函数往后随便排放。
参考链接:
朱老师课件
https://www.cnblogs.com/lanjianhappy/p/6053399.html
https://www.cnblogs.com/wxb20/p/6271149.html
https://blog.csdn.net/qq_28992301/article/details/51814005
http://www.eeworld.com.cn/mcu/article_2016040825595.html
https://blog.csdn.net/abcamus/article/details/53509720
https://blog.csdn.net/qq_28992301/article/details/51814005
http://blog.51cto.com/11134889/2072459