链接脚本

 
链接脚本从内存四区说起:
程序执行是跑在内存中的,而程序中不同的对象(如 指针,局部变量,全局变量,函数参数 等等)要求的生命周期是不同的,所以将这些对象放在不同的区段,这样就赋予了这些对象不同的生命周期,内存四区由此而来。
 

内存四区:

内存四区分为:栈区,堆区,全局区,代码区。
  1. 栈区(stack)
栈区由编译器自动分配和释放。
栈区主要存放函数参数、函数返回值、局部变量。
  1. 堆区(heap)
堆区由程序员手动分配和释放。如果程序员未释放,在程序运行结束时,由系统释放。
堆区主要存放由 malloc 关键字申请的内存空间。在C++语言中关键字 new 申请的内存也在堆区。
  1. 全局区
全局区又叫数据区或者静态区。
全局区分为三段: data段 、bss段、rodata 段。
(1).data 段存放已初始化和值不为0的全局变量和静态变量
(2).bss 段存放未初始化和初始化值为0的全局变量和静态变量。
(3).rodata 段存放各类常量,如:const、字符串、#define等
rodata 段又叫常量区或只读存储区
  1. 代码区(text)
代码区又叫 code 段 或 text 段。
用来存放程序代码,由操作系统进行管理,只读。
 
链接的作用:
0
链接的作用就是把编译生成的多个目标文件(.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
这样在启动汇编代码和链接脚本中将内存四区总的大小给定了下来,如在上面的start.s中,就整体规定下来了内存四区的大小为64M,堆区由程序员手动分配和释放,栈区主要存放函数参数、函数返回值、局部变量,如果程使用malloc一直分配内存而没有及时释放,导致内存四区大小大于分配的大小,则会导致crash,也就是我们所说的“”堆栈溢出“”。
 
 
posted @ 2024-03-16 22:40  lethe1203  阅读(11)  评论(0编辑  收藏  举报