本文是基于韦东山视频的学习笔记
链接脚本的简单解析和bss段的清零
连接脚本的简单解析
根据文档的描述,这里是完整的语法。
Here is the full syntax of a section definition, including all the optional portions:
SECTIONS {
…
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
…
}
但是目前来说,我们只用到
SECTIONS {
secname start : AT ( ldadr )
{ contents }
}
- secname : 段名
- start:运行地址
- ldadr: 加载地址 ,不写时,默认等于运行地址
- contents:内容
bss
在前面的博客有说bss段的数据一般不烧进 bin 文件,因为里面都是一些初始值为零或没有初始值的全局变量,但是当真的需要这些初始值为零的变量怎么办咧,那我们就得来把bss段清零。
bss段代码
/*bss段清零*/
ldr r1, =bss_start
ldr r2, =bss_end
mov r3, #0
clean_bss:
strb r3, [r1]
add r1, r1, #1
cmp r1, r2
bne clean_bss
链接文件代码
SECTIONS {
.text 0: {*(.text)}
.rodata : {*(.rodata)}
.data 0x30000000: AT(0x800)
{
data_load_addr = LOADADDR(.data);
data_start = .;
*(.data)
data_end = .;
}
.bss :
{
bss_start = .;
*(.bss) *(.COMMET)
bss_end = .;
}
}
链接脚本的简单改进
原因
上文重定位的代码和 bss 段的清零中,汇编代码用的是
mov r0, #0x800
ldrb r1, [r0]
mov r0, #0x30000000
strb r1, [r0]copy:
ldrb r0, [r1]
strb r0, [r2]
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne copy
但是,用 ldrb 和 strb 的效率实在时太慢了。举个栗子。
- 如果需要重定位16字节的数据,用 ldrb 去访问16位的 Nor flash 的话,我们就需要访问16次,再用 strb 去写入32位 sdram 需要写入16次,一共32次。
- 但是相同的情况下,要重定位16字节的数据,如果用 ldb 去访问 Nor flash 的话,就只需要访问8次,如果用 str 去写 sdram 的话,就只需要写入 4 次,一共12次。
不难看出,第二种方法更加有效率,当数据成千上百万计的时候,将会有巨大的差别。
代码
copy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
ble copy ;改为小于等于,因为现在是复制4字节
/*bss段清零*/
ldr r1, =bss_start
ldr r2, =bss_end
mov r3, #0
clean_bss:
str r3, [r1]
add r1, r1, #4 ;str是4字节操作,需要加4个字节
cmp r1, r2
ble clean_bss ;改为小于等于,因为现在是复制4字节
问题
但是以以上代码直接运行的时候,我们会发现全局变量居然会被清零!!
代码重定位和位置无关码
代码重定位
之前为了全局变量在 bin 文件中使用,我们重定位了 data段。除此之外,我们也可以重定位整个代码段以达到相同的目的,接下来就是操作了。
SECTIONS {
.text 0: {*(.text)}
.rodata : {*(.rodata)}
.data 0x30000000: AT(0x800)
{
data_load_addr = LOADADDR(.data);
data_start = .;
*(.data)
data_end = .;
}
.bss :
{
. = ALIGN(4);
bss_start = .;
*(.bss) *(.COMMET)
bss_end = .;
}
}
位置无关码
所谓的位置无关码,最简单的理解就是与位置无关的代码(好像没有解释一样……),一般都用代码相对地址来表示,而不用绝对地址。
这里有几点需要注意的。
- 调用程序用 B/BL 指令
- 重定位之前不要使用绝对地址,比如:
不可访问全局变量/静态变量
不可以访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问的) - 重定位之后,使用绝对跳转命令跳到Runtime addr, 比如:
ldr pc, =main