链接脚本的编写( 起始地址、text、data、__bss_start、__bss_end、dis反汇编验证)

链接脚本的简介以及简单编写

链接脚本描述了要链接的文件,以及链接顺序、链接首地址

在博文ARM(IMX6U)裸机C语言版本LED驱动实验中,我们在编译过程中使用Makefile 来链接代码,其中使用了如下语句:

arm-linux-gnueabihf-ld  -Ttext 0X87800000  -o  ledc.elf   $^

上面语句中我们是通过“-Ttext”来指定链接地址是 0X87800000 的,这样的话所有的文件都会链接到以 0X87800000 为起始地址的区域。

有时候我们很多文件需要链接到指定的区域,或者叫做段里面,比如在 Linux 里面初始化函数就会放到 init 段里面。因此我们需要能够自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函数应该存放到哪个段里面去

要完成这个功能我们就需要使用到链接脚本,看名字就知道链接脚本主要用于链接的,用于描述文件应该如何被链接在一起形成最终的可执行文件。其主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。比如我们编译生成的文件一般都包含 text 段、 data 段等等。

链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。像文件名之类的字符串可以直接键入,也可以使用通配符“*”。最简单的链接脚本可以只包含一个命令“SECTIONS”,我们可以在这一个“SECTIONS”里面来描述输出文件的内存布局。我们一般编译出来的代码都包含在 text、 data、 bss 和 rodata 这四个段内,假设现在的代码要被链接到 0X10000000 这个地址,数据要被链接到 0X30000000 这个地方,下面就是完成此功能的最简单的链接脚本:

创建后缀名为.lds的文件

SECTIONS{
 . = 0X10000000;
 .text : {*(.text)}
 . = 0X30000000;
 .data ALIGN(4) : { *(.data) }
 .bss ALIGN(4) : { *(.bss) }
 }

第 1 行我们先写了一个关键字“SECTIONS”,后面跟了一个大括号,这个大括号和第 7 行的大括号是一对,这是必须的。看起来就跟 C 语言里面的函数一样。

第 2 行对一个特殊符号“.”进行赋值,“.”在链接脚本里面叫做定位计数器,默认的定位计数器为 0。我们要求代码链接到以 0X10000000 为起始地址的地方,因此这一行给“.”赋值0X10000000,表示以 0X10000000 开始,后面的文件或者段都会以 0X10000000 为起始地址开始链接。

第 3 行的“.text”是段名(代码段),后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“(.text)”中的“”是通配符,表示所有输入文件的.text段都放到“.text”中。

第 4 行,我们的要求是数据放到 0X30000000 开始的地方,所以我们需要重新设置定位计数器“.”,将其改为 0X30000000。如果不重新设置的话会怎么样?假设“.text”段大小为 0X10000,那么接下来的.data 段开始地址就是 0X10000000+0X10000=0X10010000,这明显不符合我们的要求。所以我们必须调整定位计数器为 0X30000000。

第 5 行跟第 3 行一样,定义了一个名为“.data”的段(数据段),然后所有文件的“.data”段都放到这里面。但是这一行多了一个“ALIGN(4)”,这是什么意思呢?这是用来对“.data”这个段的起始地址做字节对齐的, ALIGN(4)表示 4 字节对齐。也就是说段“.data”的起始地址要能被 4 整除,一般常见的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字节或者 8 字节对齐。

第 6 行定义了一个“.bss”段(bss数据段x,表示定义了但是还没有初始化的数据段),所有文件中的“.bss”数据都会被放到这个里面,“.bss”数据就是那些定义了但是没有被初始化的变量。

编写本试验的链接脚本

上面就是链接脚本最基本的语法格式,我们接下来就按照这个基本的语法格式来编写我们本试验的链接脚本,我们本试验的链接脚本要求如下

①、链接起始地址为0X87800000。
②、start.o 要被链接到最开始的地方,因为start.o 里面包含这第一个要执行的命令。
根据要求,在Makefile 同目录下新建一个名为“imx6ul.lds”的文件,然后在此文件里面输入如下所示代码:

imx6u.lds

SECTIONS{
	 . = 0X87800000;
	 .text :
	 {
	 start.o
	 main.o
	 *(.text)
	 }
	 .rodata ALIGN(4) : {*(.rodata*)}
	 .data ALIGN(4) : { *(.data) }
	 __bss_start = .;
	 .bss ALIGN(4) : { *(.bss) *(COMMON) }
	 __bss_end = .;
 }

上述代码“text :”中的冒号要和text段名空格隔开。

上面的链接脚本文件和示例代码基本一致的。

第2 行设置定位计数器为0X87800000,因为我们的链接地址就是0X87800000。

第5 行设置链接到开始位置的文件为start.o,因为start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。

第6行是main.o这个文件,其实可以不用写出来,因为main.o 的位置就无所谓了,可以由编译器自行决定链接位置。

第9行rodata 表示read only只读数据段。

在第11、13 行有“__bss_start”和“__bss_end”这两个东西?这个是什么呢?“__bss_start”和“__bss_end”是符号,第11、13 这两行其实就是对这两个符号进行赋值,其值为定位符“.”,这两个符号用来保存.bss 段的起始地址和结束地址
前面说了.bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的,因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋0 即可完成清零。通过第11、13 行代码,.bss 段的起始地址和结束地址就保存在了“__bss_start”和“__bss_end”中,我们就可以直接在汇编或者C 文件里面使用这两个符号。

编写完这个链接脚本,怎么去使用这个链接脚本呢?

在Makefile文件里面添加进来就OK了,如图:
在这里插入图片描述

最后查看反汇编文件,发现start已经链接到指定的位置当中了。
在这里插入图片描述

  • 11
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
在Keil uVision中使用armcc编译器时,出现 "Undefined symbol __bss_end" 的错误,通常是由于编译器没有找到 __bss_end 符号的定义。 解决这个问题的方法是在代码中添加一个链接脚本文件,以明确告诉编译器 __bss_end 符号的定义。 以下是一个示例链接脚本文件的内容: ```c LR_IROM1 0x08000000 0x08040000 { ; load region size_region ER_IROM1 0x08000000 0x08040000 ; load address = execution address RW_IRAM1 0x20000000 0x20008000 ; RW data RW_IRAM2 0x20008000 0x20010000 ; RW data RW_IRAM3 0x10000000 0x10000800 ; RW data RW_IRAM4 0x10000800 0x10001000 ; RW data RW_IRAM5 0x10001000 0x10002000 ; RW data RW_IRAM6 0x10002000 0x10004000 ; RW data RW_IRAM7 0x10004000 0x10008000 ; RW data RW_IRAM8 0x10008000 0x10010000 ; RW data RW_IRAM9 0x10010000 0x10020000 ; RW data RW_IRAM10 0x10020000 0x10040000 ; RW data RW_IRAM11 0x20000000 0x20008000 ; RW data RW_IRAM12 0x20008000 0x20010000 ; RW data RW_IRAM13 0x20010000 0x20020000 ; RW data RW_IRAM14 0x20020000 0x20040000 ; RW data } SECTIONS { .text : { *(.text) *(.text.*) } .rodata : { *(.rodata) } .data : { *(.data) *(.data.*) } .bss : { __bss_start = .; *(.bss) *(.bss.*) *(COMMON) __bss_end = .; } __heap_start = ORIGIN(RAM) + LENGTH(RAM); __heap_end = __heap_start + 0x1000; .heap : { __heap_start = .; *(.heap) __heap_end = .; } /DISCARD/ : { *(.comment) } } ``` 其中,关键部分是在 .bss 段中添加了 __bss_start 和 __bss_end 符号的定义,以明确告诉编译器和链接器 __bss_end 符号的位置。 在Keil uVision中,你可以将这个链接脚本文件添加到你的项目中,方法是: 1. 在项目根目录下创建一个名为 xxx.scf 的文件,将上述链接脚本内容复制到该文件中。 2. 在Keil uVision中打开 "Options for Target" 对话框,在 "Linker" 选项卡下的 "Scatter File" 中选择刚才创建的 xxx.scf 文件。 3. 重新编译项目,此时应该不再出现 "Undefined symbol __bss_end" 的错误。 希望这个方法能够帮助你解决问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行稳方能走远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值