AT - Section Attribute of GNU Linker Scripts
Preface
最近解决了一个小MCU系统里memory分配的问题。这个小系统本身memory有限,而且还是非连续的两块,原本是把binary(.text,.rodata,.data)放在第一块,其他的(.bss,.stack,.heap)放第二块,现在feature增加,binary超出了第一块的大小,应该怎么办?
Content
这个问题其实有个很常用的场景就是ROM code。细节上稍有不同,原理一致。众所周知ROM code是在芯片tape out前就准备好放在netlist里的,是固化在芯片内部read only的code。但代码中有一个段叫.data section,它保存了默认值为非0的全局或者局部变量的值,这些值在运行过程中极大可能会被改变,而ROM code在芯片里是read only的,这部分是如何处理的呢?ATF里的BL1给了很好的例子。
Note:
本文中分析的ATF代码版本为v2.11。
示例中引用的编译结果为编译QEMU ARMv8的输出。
一般来讲,ATF的BL1是用来做ROM code的,这在官方文档AP_BL1里有介绍。BL1里关于本文提出的问题主要有两个方面,一个是如何pack,另一个是如何load。
Pack
没有看ATF BL1这段之前,博主通过objcopy的功能也可以处理.data section的问题。具体有下面几个步骤:
- ld script文件里把.data section的位置设置为最终要run的VMA1
注:ROM code中,这里的VMA一定是可修改的RAM或者DRAM。 根据生成的elf文件,用下面命令生成不带.data的binary,如xx.code.bin
1
objcopy -O binary -R .data xx.elf xx.code.bin
根据生成的elf文件,用下面命令生成只有.data的binary,如xx.data.bin
1
objcopy -O binary -j .data xx.elf xx.data.bin
- 把xx.code.bin做一定的alignment,把xx.code.bin和xx.data.bin cat在一起
如果直接objcopy -O binary xx.elf xx.bin,中间不连续的地址部分也会被填充。比如.data段的VMA和.text的结尾有2M的空间,那么最终生成的bin文件也会多出2M的填充数据。
ATF BL1里的处理太简单了,看如下代码(bl1.ld.S):
1
DATA_SECTION >RAM AT>ROM
一行搞定。其中“>RAM”定义里VMA1,而“AT>ROM”则定义了LMA2。这里就是AT这个关键字的功劳。
有点抽象,来看下展开是什么样子的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
......
.data . : ALIGN(16) {
__DATA_START__ = .;
*(SORT_BY_ALIGNMENT(.data*)) __DATA_END__ = .;
} >RAM AT>ROM
__DATA_RAM_START__ = __DATA_START__;
__DATA_RAM_END__ = __DATA_END__;
......
__DATA_ROM_START__ = LOADADDR(.data);
......
在dump文件中看下其中几个变量的值:
1
2
3
000000000e0ee000 g .data 0000000000000000 __DATA_RAM_START__
000000000e0ee105 g .data 0000000000000000 __DATA_RAM_END__
0000000000006b00 g *ABS* 0000000000000000 __DATA_ROM_START__
它们分别对应VMA的开始和结束和LMA的开始。
再来看下build好的BL1 for QEMU ARMv8的section table:
1
2
3
4
5
6
7
8
9
10
11
12
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00005000 0000000000000000 0000000000000000 00001000 2**11
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rodata 00001b00 0000000000005000 0000000000005000 00006000 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .data 00000105 000000000e0ee000 0000000000006b00 00008000 2**4
CONTENTS, ALLOC, LOAD, DATA
3 .stacks 00001000 000000000e0ee140 0000000000006c05 00008140 2**6
ALLOC
4 .bss 00000860 000000000e0ef140 0000000000006c10 00008140 2**5
ALLOC
主要看属性里有LOAD的部分,其中.text和.rodata的VMA和LMA是一样的,而.data的VMA在0xe0ee000,LMA则是0x6b00。这些值和上面lds里的几个值是符合的。
Load
Load部分直接看ATF里怎么处理的。这段code在el3_common_macros.S里。
1
2
3
4
5
6
7
8
9
10
11
#if defined(IMAGE_BL1) || \
(defined(IMAGE_BL2) && RESET_TO_BL2 && BL2_IN_XIP_MEM)
adrp x0, __DATA_RAM_START__
add x0, x0, :lo12:__DATA_RAM_START__ /* set x0 to start of VMA */
adrp x1, __DATA_ROM_START__
add x1, x1, :lo12:__DATA_ROM_START__ /* set x1 to start of LMA */
adrp x2, __DATA_RAM_END__
add x2, x2, :lo12:__DATA_RAM_END__ /* set x2 to end of VMA */
sub x2, x2, x0 /* set x2 to the size of data section */
bl memcpy16 /* copy data section from LMA to VMA */
#endif
Recap
通过pack和load这两个步骤,ROM code自己就把原本放在ROM里的.data section放在了RAM或者DRAM里。回到本文开头的问题,把.data section分离出来,从第一块memory挪到第二块memory,是不是就释放出一定大小的memory给.text!