链接脚本
参考博客:内存管理01——链接脚本
链接脚本从内存四区说起:
程序执行是跑在内存中的,而程序中不同的对象(如 指针,局部变量,全局变量,函数参数 等等)要求的生命周期是不同的,所以将这些对象放在不同的区段,这样就赋予了这些对象不同的生命周期,内存四区由此而来。
内存四区:
内存四区分为:栈区,堆区,全局区,代码区。
- 栈区(stack)
栈区由编译器自动分配和释放。
栈区主要存放函数参数、函数返回值、局部变量。
- 堆区(heap)
堆区由程序员手动分配和释放。如果程序员未释放,在程序运行结束时,由系统释放。
堆区主要存放由 malloc 关键字申请的内存空间。在C++语言中关键字 new 申请的内存也在堆区。
- 全局区
全局区又叫数据区或者静态区。
全局区分为三段: data段 、bss段、rodata 段。
(1).data 段存放已初始化和值不为0的全局变量和静态变量
(2).bss 段存放未初始化和初始化值为0的全局变量和静态变量。
(3).rodata 段存放各类常量,如:const、字符串、#define等
rodata 段又叫常量区或只读存储区
- 代码区(text)
代码区又叫 code 段 或 text 段。
用来存放程序代码,由操作系统进行管理,只读。
链接的作用:
![0](https://img2024.cnblogs.com/blog/3261801/202403/3261801-20240316224012922-2012713927.png)
链接的作用就是把编译生成的多个目标文件(.o)合并起来,生成最后的可执行文件(.elf)。如上图中间的就是.o目标文件,最右的则是链接生成的.elf文件。除此之外,链接脚本还关注一个问题,就是生成的各个段被加载在内存的什么位置。
链接脚本的编写与分析:
1 ENTRY(_start) 2 SECTIONS { 3 . = 0x80000000; /* .代表当前的位置,也就是链接起始地址0x80000000 */ 4 5 . = ALIGN(4); /* 4字节对齐 */ 6 .text : /* 代码区,用来指定代码段的起始地址和大小等信息 */ 7 { 8 start.o (.text) 9 *(.text) 10 } 11 12 . = ALIGN(4); 13 .rodata : /* 只读数据段 */ 14 { 15 *(.rodata) 16 } 17 18 . = ALIGN(4); 19 .data : /* 数据段 */ 20 { 21 *(.data) 22 } 23 24 . = ALIGN(4); 25 .bss : /* bss段 */ 26 { 27 *(.bss) 28 } 29 }
- 第1行,ENTRY(_start) 用于指定程序的入口,ENTRY( )是设置入口地址的命令, “_start”是程序的入口,本章的led程序的入口地址位于start.S的“_start”标号处。
- 第2行,定义SECTIONS。SECTIONS可以理解为是一块区域,我们在这块区域排布我们的代码, 链接时链接器就会按照这里的指示链接我们的代码。
- 第3行,“.”运算符代表当前位置。 我们在SECTION的最开始使用“.= 0x80000000”就是将链接起始地址设置为0x80000000。
- 第5行,设置字节对齐。这里同样用到了“.”运算符,它表示从当前位置开始执行四字节对齐。 假设当前位置为0x80000001,执行该命令后当前地址将会空出三个字节转到0x80000004地址处。
- 第6行,定义代码段。“.text :”用于定义代码段,固定的语法要求,我们按照要求写即可。 在“{}”中指定那些内容放在代码段。
- 第8-9行,将start.o中的代码放到代码段的最前面。start.S是启动代码应当首先被执行, 所以通常情况下要把它放到代码段的最前面,其他源文件的代码按照系统默认的排放顺序即可, 通配符“*”在这里表示其他剩余所有的.o文件。
- 第12-16行,设置rodata数据段。同设置代码段类似,首先设置字节对齐,然后定义代码段。在代码段里使用“*”通配符, 将所有源文件中的代码添加到这个代码段中。
- 第18-22行,设置data段。设置方法与设置数据段完全相同
- 第24-28行,设置bss段。
那么栈区大小是在哪里指定的呢?在start.s或者head.s指定的,如下:
.text // 通常被用来标识代码段(即程序的指令部分) .align 2 //设置字节对齐 .global _start //定义全局变量 _start: //程序的开始 b reset //跳转到reset标号处 reset: bic r0, r0, #0x1f /* 将 r0 的低 5 位清零,也就是 cpsr 的 M0~M4 */ orr r0, r0, #0x13 /* r0 或上 0x13,表示使用 SVC 模式 */ msr cpsr, r0 /* 将 r0 的数据写入到 cpsr_c 中 */ ldr sp, =0x84000000 // 设置栈地址64M b main // 跳转到main函数 /*跳转到light_led函数*/ // bl light_led /*进入死循环*/ loop: b loop